Orange labs, 김범진
혹시라도 이걸 만들기 위해 고민하시는 분들 계시면 참고하시라고 올리며 더 좋은 방법 찾으신 분은 알려주시면 감사드리겠습니다.
힌트를 얻은 글은
https://blog.naver.com/techshare/220974308231
C# - 특정 EXE 프로세스를 종료시킨 EXE를 찾아내는 방법
다음과 같은 tweet을 봤습니다. ^^ Matt Swann @MSwannMSFT Mar 31 ; https://twitte...
blog.naver.com
Microsoft-Windows-Kernel-Audit-API-Calls 로 분류된 이벤트 사용.
etw-providers-docs/Manifests-Win10-17134/Microsoft-Windows-Kernel-Audit-API-Calls.xml at master · repnz/etw-providers-docs
Document ETW providers. Contribute to repnz/etw-providers-docs development by creating an account on GitHub.
github.com
C++ 대략 코드
struct __declspec(uuid("{e02a841c-75a3-4fa7-afc8-ae09cf9b7f23}")) MWKAAC_PROVIDER_GUID_HOLDER;
static const auto MWKAAC_PROVIDER_GUID = __uuidof(MWKAAC_PROVIDER_GUID_HOLDER);
struct {
ULONG status;
WCHAR szLoggerName[1024];
TRACEHANDLE hSession;
EVENT_TRACE_PROPERTIES *pSessionProperties;
ULONG BufferSize = 0;
EVENT_TRACE_LOGFILEW logFile;
TRACEHANDLE hTrace;
HANDLE hThread;
EVT_HANDLE hSubscribe;
EVT_HANDLE hBookmark;
} m_event;
VOID CETW::Start() {
m_event.status = -1;
m_event.BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(WCHAR) * MAX_LOGFILE_NAME +
sizeof(WCHAR) * MAX_LOGGER_NAME;
m_event.pSessionProperties = (EVENT_TRACE_PROPERTIES*)__alloc("pSessionProperties", m_event.BufferSize);
if (NULL == m_event.pSessionProperties)
{
AGENT2_FUNCTION_ERROR_LOG(this, __FUNCTION__, "m_event.pSessionProperties", m_event.BufferSize, "can not allocate");
break;
}
// Set the session properties. You only append the log file name
// to the properties structure; the StartTrace function appends
// the session name for you.
ZeroMemory(m_event.pSessionProperties, m_event.BufferSize);
m_event.pSessionProperties->Wnode.BufferSize = m_event.BufferSize;
m_event.pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
m_event.pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
m_event.pSessionProperties->Wnode.Guid = OrangeLabsTheClientAgent;
m_event.pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; //EVENT_TRACE_SYSTEM_LOGGER_MODE;
m_event.pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
m_event.pSessionProperties->BufferSize = 0;
m_event.pSessionProperties->MaximumBuffers = 200;
StringCbCopy(m_event.szLoggerName, sizeof(m_event.szLoggerName), LOGGER_NAME);
// Create the trace session.
m_event.status = StartTrace((PTRACEHANDLE)&m_event.hSession, m_event.szLoggerName, m_event.pSessionProperties);
if (ERROR_SUCCESS == m_event.status) {
m_log.Log2("%20ws StartTrace:%d", LOGGER_NAME, m_event.status);
}
else
{
if (ERROR_ALREADY_EXISTS == m_event.status)
{
m_log.Log2("ERROR:%ws is already in use. hSession:%p", m_event.szLoggerName, m_event.hSession);
if (ERROR_SUCCESS == ControlTrace(NULL, m_event.szLoggerName, m_event.pSessionProperties, EVENT_TRACE_CONTROL_QUERY)) {
m_log.Log2("EVENT_TRACE_CONTROL_QUERY");
if (ERROR_SUCCESS == ControlTrace(NULL, m_event.szLoggerName, m_event.pSessionProperties, EVENT_TRACE_CONTROL_STOP)) {
m_log.Log2("EVENT_TRACE_CONTROL_STOP");
}
else {
m_log.Log2("ERROR:EVENT_TRACE_CONTROL_STOP");
}
}
else {
m_log.Log2("ERROR:EVENT_TRACE_CONTROL_QUERY");
}
}
else
{
m_log.Log2("StartTrace() failed with %lu", m_event.status);
}
break;
}
m_event.logFile.LoggerName = m_event.szLoggerName;
//m_event.logFile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD;//PROCESS_TRACE_MODE_REAL_TIME;
m_event.logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME|PROCESS_TRACE_MODE_EVENT_RECORD|PROCESS_TRACE_MODE_RAW_TIMESTAMP;
m_event.logFile.EventRecordCallback = EventRecordCallback;
m_event.logFile.BufferCallback = BufferRecordCallback;
m_event.logFile.Context = this;
ULONG status = EnableTraceEx2(m_event.hSession, &MWKAAC_PROVIDER_GUID, EVENT_CONTROL_CODE_ENABLE_PROVIDER,
TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL);
m_event.hTrace = ::OpenTraceW(&m_event.logFile);
if ((TRACEHANDLE)INVALID_HANDLE_VALUE == m_event.hTrace) {
CErrorMessage err(GetLastError());
m_log.Log(WIN32_ERROR_LOGFORMAT(__FUNCTION__, "OpenTraceW()", err));
break;
}
}
VOID CETW::Shutdown() {
if (m_event.pSessionProperties) {
if (m_event.hSession) {
if ((TRACEHANDLE)INVALID_HANDLE_VALUE != m_event.hTrace){
CloseTrace(m_event.hTrace);
m_event.hTrace = NULL;
}
m_event.status = ControlTrace(m_event.hSession, m_event.szLoggerName, m_event.pSessionProperties, EVENT_TRACE_CONTROL_STOP);
if (ERROR_SUCCESS != m_event.status)
{
}
if (m_event.hThread) {
WaitForSingleObject(m_event.hThread, 30 * 1000);
CloseHandle(m_event.hThread);
m_event.hThread = NULL;
}
m_event.hSession = NULL;
}
__free("pSessionProperties", m_event.pSessionProperties);
m_event.pSessionProperties = NULL;
}
}
VOID CETW::EventRecordCallback(PEVENT_RECORD EventRecord) {
CETW *pClass = (CETW *)EventRecord->UserContext;
DWORD PID = EventRecord->EventHeader.ProcessId;
auto opCode = EventRecord->EventHeader.EventDescriptor.Opcode;
auto task = EventRecord->EventHeader.EventDescriptor.Task;
auto eventId = EventRecord->EventHeader.EventDescriptor.Id;
if( 2 == eventId ) {
// 프로세스 강제 종료됨.
ProcessPtr cpptr, // 원인
tpptr; // 대상
DWORD TargetPID = GetData<uint32_t>(EventRecord, L"TargetProcessId");
int nExclude = 0;
pClass->GetProcessCacheByPID(PID, [&](ProcessPtr pptr) {
// 이벤트를 받았을 때 이미 강제 종료된 프로세스가 없어진 상태라
// 종료된 프로세스 정보도 일정 시간 동안 캐시에 보관해야함.
if( pptr ) {
cpptr = pptr;
pptr->doc.Get([&](Json::Value& doc) {
if( pClass->ExcludeFileCount(doc) ) {
nExclude++;
}
if( nExclude ) return;
if (WINDOWS_OPERATING_SYSTEM_A_UID ==doc.get("ProductNameUID", 0).asUInt() ..
// 로그오프시 아래 녀석들이 프로그램들을 무자비하게 죽임. 적절히 필터링
// csrss.exe, smss.exe, svchost.exe, LogonUI.exe
// WerFault.exe
// 프로세스 비정상 종료시 WerFault.exe가 그 프로세스에 붙어 정보를 읽고 죽임
)) {
nExclude += 1;
return;
}
});
}
else {
}
});
pClass->GetProcessCacheByPID(TargetPID, [&](ProcessPtr pptr) {
if( pptr ) {
tpptr = pptr;
pptr->doc.Get([&](Json::Value& doc) {
});
}
});
if (cpptr && tpptr) {
// cpptr 살인자
// tpptr 희생자
}
else {
if (!cpptr) {
pClass->GetLog()->info("cpptr is null.");
}
if (!tpptr) {
pClass->GetLog()->info("tpptr is null.");
}
return;
}
}
}
ULONG CETW::BufferRecordCallback(_In_ PEVENT_TRACE_LOGFILE Buffer) {
return TRUE;
}
남은 숙제가 있다면 Microsoft-Windows-Kernel-Audit-APICalls 엔 TerminateProcess 뿐 아니라 OpenProcess 같은 것 도있으니 실시간 이벤트를 모두 받지 않고 필터링 하고 싶다 . 그리고 이렇게 이벤트 받는 비용이 결코 가벼워 보이진 않는다는 것 .