security/리버싱핵심원리

API 코드 패치를 이용한 API hooking - notepad.exe 프로세스 은폐하기 (2)

민사민서 2023. 4. 3. 20:34

글로벌 API 후킹

1. 현재 실행중인 모든 프로세스에 대해 API 후킹 - (1)에서 ZwQuerySystemInformation API 이용

2. 앞으로 실행될 모든 프로세스에 대해 API 후킹 - CreateProcess API 이용

2-1. CreateProcessA / CreateProcessW 모두 후킹해야하는 번거로움, 자식 프로세스가 후킹되지 않은 채로 실행될 가능성존재 - ZwResumeThread API 이용

 

HideProc2.exe 코드 구현

- HideProc.exe랑 동일, 은폐 프로세스 이름이 "notepad.exe"로 하드코딩

- "stealth2.dll"은 모든 프로세스가 인식할 수 있는 경로인 %system32%(C:\Windows\System32)에 위치함. 따라서 dll name을 full path로 변환해주는 루틴 제거

 

stealth2.dll 코드 구현 (5바이트 패치, CreateProcess 후킹)

// stealth2.dll
#include "stdio.h"
#include "windows.h"
#include "tchar.h"

#define STR_MODULE_NAME					(L"stealth2.dll")
#define STR_HIDE_PROCESS_NAME			(L"notepad.exe")
#define STATUS_SUCCESS					(0x00000000L) 

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    BYTE Reserved1[52];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass, 
    PVOID SystemInformation, 
    ULONG SystemInformationLength, 
    PULONG ReturnLength);

typedef BOOL (WINAPI *PFCREATEPROCESSA)(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
);

typedef BOOL (WINAPI *PFCREATEPROCESSW)(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
);

BYTE g_pOrgCPA[5] = {0,};
BYTE g_pOrgCPW[5] = {0,};
BYTE g_pOrgZwQSI[5] = {0,};

BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, FARPROC pfnNew, PBYTE pOrgBytes)
{
	FARPROC pFunc;
	DWORD dwOldProtect, dwAddress;
	BYTE pBuf[5] = {0xE9, 0, };

	pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
	if( *((PBYTE)pFunc) == 0xE9 )
		return FALSE;

	VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	memcpy(pOrgBytes, pFunc, 5);

	dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5;
	memcpy(&pBuf[1], &dwAddress, 4);

	memcpy(pFunc, pBuf, 5);

	VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

	return TRUE;
}

BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
	FARPROC pFunc;
	DWORD dwOldProtect;

	pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
	if( *((PBYTE)pFunc) != 0xE9 )
		return FALSE;

	VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	memcpy(pFunc, pOrgBytes, 5);

	VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

	return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
	TOKEN_PRIVILEGES tp;
	HANDLE hToken;
	LUID luid;

	if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken)) {
		_tprintf(L"OpenProcessToken error: %u\n", GetLastError());
		return FALSE;
	}

	if(!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) {
		_tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());
		return FALSE;
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	if(bEnablePrivilege)
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
		tp.Privileges[0].Attributes = 0;

	if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) {
		_tprintf(L"AdjuestTokenPrivileges error: %u\n", GetLastError());
		return FALSE;
	}

	if(GetLastError()==ERROR_NOT_ALL_ASSIGNED) {
		_tprintf(L"The token does not have the specified privilege.\n");
		return FALSE;
	}

	return TRUE;
}

NTSTATUS WINAPI NewZwQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass, 
	PVOID SystemInformation, 
	ULONG SystemInformationLength, 
	PULONG ReturnLength)
{
	NTSTATUS status;
	FARPROC pFunc;
	PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
	char szProcName[MAX_PATH] = {0,};

	unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);

	pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"), 
                           "ZwQuerySystemInformation");
	status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
             (SystemInformationClass, SystemInformation, 
              SystemInformationLength, ReturnLength);

	if( status != STATUS_SUCCESS )
		goto __NTQUERYSYSTEMINFORMATION_END;

	if( SystemInformationClass == SystemProcessInformation )
	{
		pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

		while(TRUE)
		{
            if(pCur->Reserved2[1] != NULL)
            {
                if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME))
			    {
				    if(pCur->NextEntryOffset == 0)
					    pPrev->NextEntryOffset = 0;
				    else
					    pPrev->NextEntryOffset += pCur->NextEntryOffset;
			    }
			    else		
				    pPrev = pCur;	// 원하는 프로세스를 못 찾은 경우만 pPrev 세팅
            }

			if(pCur->NextEntryOffset == 0)
				break;

			pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
		}
	}

__NTQUERYSYSTEMINFORMATION_END:

	hook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                 (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);

	return status;
}

BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName) {
	HANDLE hThread;
	LPVOID pRemoteBuf;
	DWORD bufSize = (DWORD)(_tcslen(szDllName)+1) * sizeof(TCHAR);
	FARPROC pThreadProc;

	pRemoteBuf = VirtualAllocEx(hProcess, NULL, bufSize, MEM_COMMIT, PAGE_READWRITE);
	if(!pRemoteBuf)
		return FALSE;

	WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, bufSize, NULL);

	pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryW");

	hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf, 0, NULL);
	WaitForSingleObject(hThread, INFINITE);

	VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

	CloseHandle(hThread);

	return TRUE;
}

BOOL WINAPI NewCreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
	BOOL bRet;
	FARPROC pFunc;

	// unhook
	unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);

	// original API 호출
	pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);
	
	// 생성된 자식 프로세스에 stealth2.dll 인젝션
	if(bRet)
		InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

	// hook again
	hook_by_code("kernel32.dll", "CreateProcessA", (FARPROC)NewCreateProcessA, g_pOrgCPA);
		
	return bRet;
}

BOOL WINAPI NewCreateProcessW(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
    BOOL bRet;
    FARPROC pFunc;

    // unhook
    unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW);

    // original API 호출
    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
    bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

    // 생성된 자식 프로세스에 stealth2.dll 인젝션
    if( bRet )
        InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

    // hook again
    hook_by_code("kernel32.dll", "CreateProcessW", 
                (PROC)NewCreateProcessW, g_pOrgCPW);

    return bRet;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
	char szCurProc[MAX_PATH] = {0,};
	char *p = NULL;

	// HideProc2.exe에는 inject 되지 않도록
	GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
	p = strrchr(szCurProc, '\\');
	if( p!=NULL && !_stricmp(p+1, "HideProc2.exe") )
		return TRUE;

	// Privilege 변경
	SetPrivilege(SE_DEBUG_NAME, TRUE);
	
	switch(fdwReason) {
	case DLL_PROCESS_ATTACH:
		hook_by_code("kernel32.dll", "CreateProcessA", (FARPROC)NewCreateProcessA, g_pOrgCPA);
		hook_by_code("kernel32.dll", "CreateProcessW", (FARPROC)NewCreateProcessW, g_pOrgCPW);
		hook_by_code("ntdll.dll", "ZwQuerySystemInformation", (FARPROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
		break;
	case DLL_PROCESS_DETACH:
		unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);
		unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW);
		unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);
		break;
	}
}

- NewCreateProcessA, NewCreateProcessW에서 원 API를 호출한 후, 생성된 자식 프로세스에 InjectDll2()를 이용해 stealth2.dll을 인젝션한다

- InjectDll2()는 자식 프로세스의 핸들을 받아서(hProcess) 인젝션 진행

  * 기존 InjectDll()은 PID를 받아 OpenProcess로 프로세스의 핸들을 얻어 인젝션 진행하는 방식이었음

 

문제점?

- 후킹 함수가 호출될 때마다 반복적인 언훅/훅으로 인해 성능 저하

- 멀티스레드 환경에서 Run Time Error 발생 가능성 (한 스레드는 실행, 다른 스레드는 쓰기)

- 후킹 함수에서 자식 프로세스를 생성(원 API 호출)하고 다시 후킹하기까지 후킹 안 된 채로 실행된다

stealth3.dll 코드 구현 (핫 패치 방식)

- 위와 같이 NOP 5바이트 + MOV EDI,EDI 로 시작하는 API에 한해 핫 패치(7바이트 패치)가 가능하다

아쉽게 ntdll!ZwQuerySystemInformation API는 핫패치가 불가능하다

BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, FARPROC pfnNew) {
	FARPROC pFunc;
	DWORD dwOldProtect, dwAddress;
	BYTE pBuf[5] = {0xE9, 0, }; // JMP to pfnNew
	BYTE pBuf2[2] = {0xEB, 0xF9}; // short JMP -7
	
	pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
	// if already hot-patched
	if(*((PBYTE)pFunc) == 0xEB)
		return FALSE;

	VirtualProtect((LPVOID)((DWORD)pFunc-5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	dwAddress = (DWORD)pfnNew - (DWORD)pFunc; // pFunc-5(명령어 시작위치) + 5(명령어 길이)
	memcpy(pBuf+1, &dwAddress, 4);
	// JMP
	memcpy((LPVOID)((DWORD)pFunc-5), &pBuf, 5);
	// Short JMP
	memcpy((LPVOID)pFunc, &pBuf2, 2);

	VirtualProtect((LPVOID)((DWORD)pFunc-5), 7, dwOldProtect, NULL);

	return TRUE;
}

BOOL unhook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName) {
	FARPROC pFunc;
	DWORD dwOldProtect;
	PBYTE pByte;
	byte pBuf[5] = {0x90, 0x90, 0x90, 0x90, 0x90}; // NOP*5
	byte pBuf2[2] = {0x8B, 0xFF}; // MOV EDI, EDI

	pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
	// if already unhooked
	if(*((PBYTE)pFunc) == 0x8B)
		return FALSE;

	VirtualProtect((LPVOID)((DWORD)pFunc-5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	memcpy((LPVOID)((DWORD)pFunc-5), pBuf, 5);
	memcpy(pFunc, pBuf2, 2);

	VirtualProtect((LPVOID)((DWORD)pFunc-5), 7, dwOldProtect, NULL);

	return TRUE;
}

BOOL WINAPI NewCreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
	BOOL bRet;
	FARPROC pFunc;

	// original API 호출
	pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
	pFunc = (FARPROC)((DWORD)pFunc+2);
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);
	
	// 생성된 자식 프로세스에 stealth2.dll 인젝션
	if(bRet)
		InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);
		
	return bRet;
}

BOOL WINAPI NewCreateProcessW(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
    BOOL bRet;
    FARPROC pFunc;

	// original API 호출
    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
	pFunc = (FARPROC)((DWORD)pFunc+2);
    bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

    // 생성된 자식 프로세스에 stealth2.dll 인젝션
    if( bRet )
        InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

    return bRet;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
	char szCurProc[MAX_PATH] = {0,};
	char *p = NULL;

	// HideProc2.exe에는 inject 되지 않도록
	GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
	p = strrchr(szCurProc, '\\');
	if( p!=NULL && !_stricmp(p+1, "HideProc2.exe") )
		return TRUE;

	// Privilege 변경
	SetPrivilege(SE_DEBUG_NAME, TRUE);
	
	switch(fdwReason) {
	case DLL_PROCESS_ATTACH:
		hook_by_hotpatch("kernel32.dll", "CreateProcessA", (FARPROC)NewCreateProcessA);
		hook_by_hotpatch("kernel32.dll", "CreateProcessW", (FARPROC)NewCreateProcessW);
		hook_by_code("ntdll.dll", "ZwQuerySystemInformation", (FARPROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
		break;
	case DLL_PROCESS_DETACH:
		unhook_by_hotpatch("kernel32.dll", "CreateProcessA");
		unhook_by_hotpatch("kernel32.dll", "CreateProcessW");
		unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);
		break;
	}
}

 

- ZwQuerySystemInformation은 5바이트 패치 방식의 API 후킹

- CreateProcessW, CreateProcessA는 핫 패치 방식의 API 후킹

- hook_by_hotpatch, unhook_by_hotpatch, 그리고 후킹 함수 내부의 구현 방식, DllMain이 달라졌다

 

(장점)

- DllMain에서 한 번만 후킹하면 최종적으로 언훅하기 전까지 언훅/훅 과정 불필요

- 진입점에 따라 분기가 달라진다

   1) 프로세스 실행 중 API 호출 => 이중 JMP에 의해 사용자 정의 후킹 함수 실행

   2) 사용자 정의 후킹 함수 내에서 (API+2) 호출 => 이중 JMP를 건너뛰고 원 API 함수 실행

(단점)

- ntdll.dll에서 제공하는 native API들은 7바이트 후킹 자체가 불가능

 

CreateProcess API 후킹의 단점 극복 - ZwResumeThread API 후킹

CreateProcessW 내부로 들어가보면
arg1, arg12 추가해서 CreateProcessInternalW 호출하고
해당 API 안에서 가장 마지막에 호출되는 API가 바로 NtResumeThread()

- kernel32!CreateProcessInternalW 안에서 ntdll!ZwCreateUserProcess API에 의해 자식 프로세스가 생성되고(메인 스레드는 suspend 상태), ntdll!ZwResumeThread에 의해 자식 프로세스 실행시킴

- ntdll!ZwResumeThread 후킹하면 자식 프로세스의 EP 코드 실행 전에 제어 가로챌 수 있음

- ntdll.dll에서 제공하는 native API라 핫 패치 방식 불가능, 고전적인 5바이트 패치 방식의 후킹 사용

NTSTATUS WINAPI NewZwResumeThread(HANDLE ThreadHandle, PULONG SuspendCount)
{
    NTSTATUS status, statusThread;
    FARPROC pFunc = NULL, pFuncThread = NULL;
    DWORD dwPID = 0;
	static DWORD dwPrevPID = 0;
    THREAD_BASIC_INFORMATION tbi;
    HMODULE hMod = NULL;
    TCHAR szModPath[MAX_PATH] = {0,};

	hMod = GetModuleHandle(L"ntdll.dll");

    // call ntdll!ZwQueryInformationThread()
    // ThreadHandle이 가리키는 스레드가 소속된 자식 프로세스의 PID 획득
    pFuncThread = GetProcAddress(hMod, "ZwQueryInformationThread");
    statusThread = ((PFZWQUERYINFORMATIONTHREAD)pFuncThread)
                   (ThreadHandle, 0, &tbi, sizeof(tbi), NULL);

    dwPID = (DWORD)tbi.ClientId.UniqueProcess;
    if ( (dwPID != GetCurrentProcessId()) && (dwPID != dwPrevPID) )
    {
        dwPrevPID = dwPID;

        // change privilege
       	SetPrivilege(SE_DEBUG_NAME, TRUE);

        // get injection dll path
        GetModuleFileName(GetModuleHandle(STR_MODULE_NAME), 
                          szModPath, 
                          MAX_PATH);

		InjectDll(dwPID, szModPath);
    }

    unhook_by_code("ntdll.dll", "ZwResumeThread", g_pZWRT);

    pFunc = GetProcAddress(hMod, "ZwResumeThread");
    status = ((PFZWRESUMETHREAD)pFunc)(ThreadHandle, SuspendCount);

    hook_by_code("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, g_pZWRT);

    return status;
}