API钩取技术

旧文迁移,逆向分析之”翠花”,其实开始学这个还是因为被卫士管家坑,它勾了API使进程即使有系统权限也会操作受限,只有学原理再把它勾回来哦,当然现在还只是用户层的勾取,其实原理就是改变原来API的入口,让它转到目的地址执行后再继续,可以控制程序流程。

技术图表


如图,他们相互组合就死一种钩取方式:

动态-进程内存-代码-调试技术


直接看代码就能理解了,不多说,这里直接上记事本WriteFile的勾取代码,此API的作用是将数据写入文件,勾取做的操作是若发现小写字母,将其替换成大写字母,于是保存在文件中的字符串就不存在小写字符了。这里使用的是钩取API:
首先,需要知道操作的数据存放在哪里?根据MSDN:

1
2
3
4
5
6
7
BOOL WriteFile(
HANDLE hFile,//文件句柄
LPCVOID lpBuffer,//数据缓存区指针
DWORD nNumberOfBytesToWrite,//你要写的字节数
LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针
LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针
);

(其他一些基础知识就不复制了,可以看这个)调用API时,数据存放在ESP+4的位置,数据长度放在ESP+8的位置。然后。。上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include<windows.h>
#include<tchar.h>
#include<stdio.h>
LPVOID writeFileAddress = NULL;//WriteFile函数地址
BYTE chINT3 = 0xCC;//int 3指令的机器码
BYTE chOrgBYTE = 0;//存放原来被覆盖掉的一字节
CREATE_PROCESS_DEBUG_INFO createProcessDebugInfo;//存放调试信息
//当创建进程调试事件时运行此,作用是在原WriteFile函数入口处打Int3断点
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pDebugEvent) {
//获取WirteFile函数地址
writeFileAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "WriteFile");
//感觉这一步可有可无
memcpy(&createProcessDebugInfo, &pDebugEvent->u, sizeof(CREATE_PROCESS_DEBUG_INFO));
//将WriteFile入口处第一字节保存后,用CC断点替换它,以后每次执行此函数将会发生调试中断
ReadProcessMemory(createProcessDebugInfo.hProcess, writeFileAddress, &chOrgBYTE, sizeof(BYTE), NULL);
WriteProcessMemory(createProcessDebugInfo.hProcess, writeFileAddress, &chINT3, sizeof(BYTE), NULL);
return TRUE;
}

//当调用WriteFile函数时会执行此函数
BOOL OnExcptionDebugEvent(LPDEBUG_EVENT pDebugEvent) {
DWORD dwBufferAddress,dwBufferLength;
PBYTE lpBuffer;
CONTEXT threadContext;
PEXCEPTION_RECORD per = &pDebugEvent->u.Exception.ExceptionRecord;
if (EXCEPTION_BREAKPOINT == per->ExceptionCode) {//判断中断类型是不是Int3中断
if (writeFileAddress == per->ExceptionAddress) {//判断发生中断的地址是不是WriteFile函数入口
//恢复原程序内容,即恢复第一字节,这样WriteFile函数才能正常执行
WriteProcessMemory(createProcessDebugInfo.hProcess, writeFileAddress, &chOrgBYTE, sizeof(BYTE), NULL);
//获取线程上下文
threadContext.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(createProcessDebugInfo.hProcess, &threadContext);
//获取堆栈内容,即WriteFile的第二个和第三个参数,注意由于这里已经调用了函数,堆栈压入了函数返回地址,减了4字节
ReadProcessMemory(createProcessDebugInfo.hProcess, (LPVOID)(threadContext.Esp + 0x8), &dwBufferAddress, sizeof(DWORD), NULL);
ReadProcessMemory(createProcessDebugInfo.hProcess, (LPVOID)(threadContext.Esp + 0xC), &dwBufferLength, sizeof(DWORD), NULL);
//分配缓冲区,用来存放字符串
lpBuffer = (PBYTE)malloc(sizeof(BYTE)*(dwBufferLength+1));
memset(lpBuffer, 0, (dwBufferLength + 1));
//将第二个参数所指地址,即原字符串拷贝到新建的缓冲区中
ReadProcessMemory(createProcessDebugInfo.hProcess, (LPVOID)dwBufferAddress, lpBuffer, dwBufferLength, NULL);
printf("缓冲区中的内容是:\n%s\n", lpBuffer);
//转换小写字母为大写
for (int i = 0; i < dwBufferLength; i++) {
if (isalpha(lpBuffer[i])) {
lpBuffer[i] = toupper(lpBuffer[i]);
}
}
printf("转换后的内容是:\n%s\n", lpBuffer);
//将转换后的内容写会原缓冲区
WriteProcessMemory(createProcessDebugInfo.hProcess, (LPVOID)dwBufferAddress, lpBuffer, dwBufferLength, NULL);
free(lpBuffer);
//将EIP地址改回WriteFile的入口地址,因为执行int3指令,地址已经向后移了一位
threadContext.Eip = (DWORD)writeFileAddress;
SetThreadContext(createProcessDebugInfo.hProcess, &threadContext);
//成功处理异常,程序继续执行
ContinueDebugEvent(pDebugEvent->dwProcessId, pDebugEvent->dwThreadId, DBG_CONTINUE);
Sleep(0);//这一句很关键,用于释放本进程资源,使被调试进程得以执行到writeFileAddress所指示地址之后,否则下一句立即执行可能导致死循环
WriteProcessMemory(createProcessDebugInfo.hProcess, writeFileAddress, &chINT3, sizeof(BYTE), NULL);
return TRUE;
}
}
}

//调试循环,循环等待调试事件,直到进程退出
void DebugLoop() {
DEBUG_EVENT debugEvent;
DWORD dwContinueStatus;
while (WaitForDebugEvent(&debugEvent, INFINITE)) {
dwContinueStatus = DBG_CONTINUE;
//当创建调试时执行钩取
if (CREATE_PROCESS_DEBUG_EVENT == debugEvent.dwDebugEventCode) {
OnCreateProcessDebugEvent(&debugEvent);
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
continue;
}//当发生异常调试事件时转到
else if (EXCEPTION_DEBUG_EVENT == debugEvent.dwDebugEventCode) {
if (OnExcptionDebugEvent(&debugEvent))
continue;
}//当被调试进程退出时执行
else if (EXIT_PROCESS_DEBUG_EVENT == debugEvent.dwDebugEventCode) {
break;
}
//讲道理这里应该设置为DBG_EXCEPTION_NOT_HANDLED再交由SEH处理的
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, dwContinueStatus);
}
}

int main(int argc, char* argv[]) {
DWORD dwProcessId;
if (argc != 2) {
printf("使用方式:ConsoleApplication1.exe pid\n");
return 1;
}
dwProcessId = atoi(argv[1]);
//开始调试
if (!DebugActiveProcess(dwProcessId)) {
printf("调试失败!\npid:%d\t错误代码:%d\n", dwProcessId, GetLastError());
return 1;
}
//开始循环钩取
DebugLoop();
return 0;
}

动态-进程内存-IAT-注入dll- CreateRemoteThread


它是修改进程的IAT,将原函数的地址替换成新函数的地址,在新函数中对输入参数做处理后再调用原函数,如图:

(钩取前)

(钩取后)
还时直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<Windows.h>
#include<wchar.h>
#include<stdio.h>

//存放原函数地址
FARPROC orgFun = NULL;
//定义setwindowtextW函数形式
typedef BOOL(WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);

BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString) {
wchar_t* pNum = L"一二三四五六七八九";
wchar_t tmp[2] = { 0, };
int nIndex = 0;
for (int i = 0; i < wcslen(lpString); i++) {
if (L'0' <= lpString[i] && L'9' >= lpString[i]) {
tmp[0] = lpString[i];
nIndex = _wtoi(tmp);
lpString[i] = pNum[nIndex];
}
}
return ((PFSETWINDOWTEXTW)orgFun)(hWnd, lpString);
}

BOOL hook_iat(LPCSTR dllName, PROC orgFunc, PROC newFunc) {
HMODULE hMod;
PBYTE pAddr;
DWORD dwRVA;
DWORD dwOldProtect;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
PIMAGE_THUNK_DATA pThunk;
//先获取原函数在iat中的地址
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;//基址
pAddr += pAddr[0x3c];//IMAGE_NT_HEADERS
dwRVA = pAddr[0x80];//记录导入表偏移地址
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(hMod + dwRVA);//导入地址表
for (; pImportDesc->Name; pImportDesc++) {//遍历每一个DLL
LPCSTR dllNameTmp = LPCSTR(hMod + pImportDesc->Name);//获取当前Dll名称
if (!_stricmp(dllNameTmp, dllName)) {//若匹配
pThunk = PIMAGE_THUNK_DATA(hMod + pImportDesc->FirstThunk);//转向地址表
for (; pThunk->u1.Function;pThunk++) {
if (pThunk->u1.Function == (DWORD)orgFun){
//若此函数地址与GetProcAddress获取到的一样,那么此地址就是要找的地址
VirtualProtect(&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); //先给写权限
pThunk->u1.Function = (DWORD)newFunc;//修改地址表处的地址
VirtualProtect(&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);//恢复权限
return TRUE;
}
}
}

}
return FALSE;

}

BOOL WINAPI DllMain(HINSTANCE hInstansc, DWORD dwReason, LPVOID lpreversed) {
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
//注入时,先获取原函数的地址
orgFun = GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowTextW");
hook_iat("user32.dll", orgFun, (PROC)MySetWindowTextW);
case DLL_PROCESS_DETACH:
hook_iat("user32.dll", (PROC)MySetWindowTextW, orgFun);
default:
break;
}
}

这是主要代码,作用是当DLL载入时进行钩取:替换原函数为自定义函数;当DLL卸载时进行脱钩:将新函数替换为原函数地址。下面的没有敲了,就是之前学过的dll注入工具的代码,copy自书上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "stdio.h"
#include "windows.h"
#include "tlhelp32.h"
#include "winbase.h"
#include "tchar.h"


void usage()
{
printf("\nInjectDll.exe by ReverseCore\n"
"- blog : http://www.reversecore.com\n"
"- email : reversecore@gmail.com\n\n"
"- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");
}


BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
HANDLE hProcess, hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;

if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
DWORD dwErr = GetLastError();
return FALSE;
}

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

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

pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}


BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;

if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
return FALSE;

bMore = Module32First(hSnapshot, &me);
for( ;bMore ;bMore = Module32Next(hSnapshot, &me) )
{
if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) )
{
bFound = TRUE;
break;
}
}

if( !bFound )
{
CloseHandle(hSnapshot);
return FALSE;
}

if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
{
CloseHandle(hSnapshot);
return FALSE;
}

pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);

return TRUE;
}


DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState)
{
DWORD dwRtn = 0;
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LUID luid;
if (LookupPrivilegeValue(NULL, szPrivilege, &luid))
{
BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof (LUID_AND_ATTRIBUTES);

PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1;
PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2;

pTP->PrivilegeCount = 1;
pTP->Privileges[0].Luid = luid;
pTP->Privileges[0].Attributes = dwState;

if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP))
dwRtn = pPrevTP->Privileges[0].Attributes;
}

CloseHandle(hToken);
}

return dwRtn;
}


int _tmain(int argc, TCHAR* argv[])
{
if( argc != 4 )
{
usage();
return 1;
}

// adjust privilege
_EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED);

// InjectDll.exe <i|e> <PID> <dll_path>
if( !_tcsicmp(argv[1], L"i") )
InjectDll((DWORD)_tstoi(argv[2]), argv[3]);
else if(!_tcsicmp(argv[1], L"e") )
EjectDll((DWORD)_tstoi(argv[2]), argv[3]);

return 0;
}

动态-进程内存-注入-DLL注入- CreateRemoteThread


隐藏进程:

ZwQuerySystemInformation获取进程列表的函数有很多,但是一般最终都是调用ZwQuerySystemInformation获取进程列表(这是个低级的函数,win8后使用特定分开的函数了),所以学习时勾取这个函数,根据MSDN:

1
2
3
4
5
6
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);

这个函数可以查询多种系统信息,从参数中可以看出来,第一个参数就是定义要查询什么信息,第二个是用来存放结果的,它的大小由第一个参数决定,不同类型的信息有不同的大小,若是不知道可以将第二三个参数指定为空,第四个参数将会返回需要的大小,此时再动态申请并再次调用即可,至于第一个参数:
SystemInformationClass为一个枚举类型(但是不能直接在vssdk里面看到,因为这个函数是为公开的,需要自己定义,这里就不全部列出来了)

1
2
3
4
5
6
7
8
9
10
11
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;

同样,第二个参数结构也不能直接在sdk里面看到,需要自己定义,进程信息的结构体为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
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;

由于未公开,只能使用GetProcAddress获取函数地址,要使用需要先定义函数结构:

1
2
3
4
5
typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)
(SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);

现在就可以使用此函数获取进程列表了,这也是要勾取的函数,新函数替代此函数后还需要调用此函数。

5字节勾取:

与上一种修改IAT不同,这里是直接修改API入口处的5字节代码,将其修改为远跳转:

(钩取前)

(钩取后)
实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include "windows.h"
#include<tchar.h>
//设置编译器选项,将procName放在共享读写节区
#pragma comment(linker,"/SECTION:.SHARE,RWS")
#pragma data_seg(".SHARE")
TCHAR procName[MAX_PATH] = { 0, };
#pragma data_seg()

#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;
ULONG NumberOfThreads;
BYTE Reserved1[48];
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;
//定义ZwQuerySystemInformation函数结构
typedef NTSTATUS(WINAPI *PFZWQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
//定义全局变量用于存放原函数入口处的5字节数据
BYTE orgBytes[5] = { 0 };
#define DEF_NTDLL ("ntdll.dll")
#define DEF_ZWQUERYSYSTEMINFORMATION ("ZwQuerySystemInformation")
//勾取函数
BOOL hook(LPCSTR dllName, LPCSTR funcName, PROC newFunc, PBYTE orgBytes) {
FARPROC orgFunc;
BYTE tmpBytes[5] = { 0xE9,0, };
PBYTE pBytes;
DWORD dwOldProtect, dwAddress;
//获取要钩取的函数的地址,pBytes指向的即要修改的前5字节首地址
orgFunc = GetProcAddress(GetModuleHandle(dllName), funcName);
pBytes = (PBYTE)orgFunc;
//防止多次钩取
if (pBytes[0] == 0xE9)
return FALSE;
//修改权限后修改前5字节
VirtualProtect(pBytes, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(orgBytes, pBytes, 5);//先备份原数据
dwAddress = (DWORD)newFunc - (DWORD)orgFunc - 5;//计算偏移地址,由于跳转指令5字节,所以这里要减5
memcpy(&tmpBytes[1], &dwAddress, 4);
memcpy(orgFunc, tmpBytes, 5);//将5字节数据写入
VirtualProtect(orgFunc, 5, dwOldProtect, &dwOldProtect);//恢复权限
return TRUE;
}

BOOL unhook(LPCSTR dllName, LPCSTR funcName, PBYTE orgBytes) {
FARPROC funcAddr;
PBYTE pByte;
DWORD dwOldProtect, dwAddress;
//同钩取函数
funcAddr = GetProcAddress(GetModuleHandle(dllName), funcName);
pByte = (PBYTE)funcAddr;
if (pByte[0] != 0xE9)
return FALSE;
VirtualProtect(pByte, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pByte, orgBytes, 5);
VirtualProtect(pByte, 5, dwOldProtect, &dwOldProtect);
}

NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength) {
FARPROC pFunc;
NTSTATUS status;
//脱钩,以便执行正常的代码获取结果
unhook(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, orgBytes);
//获取原函数地址
pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL),
DEF_ZWQUERYSYSTEMINFORMATION);
//执行原函数
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);
if (status != STATUS_SUCCESS)
goto endLable;//若是查询失败,跳过下面的步骤
if (SystemInformationClass == SystemProcessInformation) {
PSYSTEM_PROCESS_INFORMATION pCur,pPrev;
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) {
if (pCur->Reserved2[1] != NULL)
{
//判断是否为要隐藏的进程,若是就将其从链表移出
if (!_tcsicmp((char*)pCur->Reserved2[1], procName))
{
if (pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}

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

// 继续遍历
pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
}
}

endLable:
//再次钩取
hook(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, (PROC)NewZwQuerySystemInformation, orgBytes);
return status;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = { 0, };
char *p = NULL;
//不勾取本注入进程
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if ((p != NULL) && !_stricmp(p + 1, "HideProc.exe"))
return TRUE;
switch (fdwReason)
{
//载入进程勾取
case DLL_PROCESS_ATTACH:
hook(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,(PROC)NewZwQuerySystemInformation, orgBytes);
break;
//卸载脱钩
case DLL_PROCESS_DETACH:
unhook(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,orgBytes);
break;
}

return TRUE;
}


#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
_tcscpy_s(procName, szProcName);
}
#ifdef __cplusplus
}
#endif

7字节钩取

5字节代码钩取使用范围很广,但是存在一个问题就是多线程时,若在正在钩取时执行会抛出异常,于是根据部分API的特性有了7字节勾取方法-热补丁勾取:
在比较上层的库中:
User32.dll

Gdi32.dll

Kernel.dll

Ntdll.dll

可以看到在前三个库中,每个函数开始前都有7个字节是准空指令,无实际用途,那么就可以在5个Nop指令里面写远跳转指令,在mov reg,reg里面写近跳转,跳转到远跳转指令处,那么在新函数中就不用再脱钩+钩取了,可以直接调用原函数地址向后偏移2字节处,下面以CreateProcess函数为例(因为ZwSystemInformation属于Ntdll内,不能使用这种方法钩取):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = { 0xE9, 0, };
BYTE pBuf2[2] = { 0xEB, 0xF9 };
PBYTE pByte;

pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
//防止重复钩取
if (pByte[0] == 0xEB)
return FALSE;
//更改前7字节的权限
VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//这里不用再减去5字节,因为pFunc就是远跳转指令之后的位置
dwAddress = (DWORD)pfnNew - (DWORD)pFunc;
memcpy(&pBuf[1], &dwAddress, 4);
//写入远跳转
memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);
// 写入近跳转: MOV EDI, EDI (0x8BFF)
memcpy(pFunc, pBuf2, 2);
//恢复权限
VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect);
return TRUE;
}

新函数中为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
);
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;

pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
//直接调用原函数加2处
pFunc = (FARPROC)((DWORD)pFunc + 2);
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
/*..........................................................
..........................................................*/

return bRet;
}

高级全局钩取

要进行全局钩取,需要钩取已存在的所有进程和新建的所有进程。

钩取已存在进程

还是利用RemoteCreateThread函数,之前是指定PID和指定进程名,记得指定进程名的时候最后还是通过遍历进程列表匹配进程名最终获得进程ID,再使用PID来注入DLL,现在注入所有进程可以说更简单,就是遍历时不判断,无条件注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
typedef void(*PFN_SetProcName)(LPCTSTR szProcName);
enum { INJECTION_MODE = 0, EJECTION_MODE };

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
/*激活指定权限代码*/
return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess, hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
printf("OpenProcess(%d) failed!!!\n", dwPID);
return FALSE;
}

pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);

WriteProcessMemory(hProcess, pRemoteBuf,
(LPVOID)szDllPath, dwBufSize, NULL);

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle("kernel32.dll"),
"LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

CloseHandle(hThread);
CloseHandle(hProcess);

return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
BOOL bMore = FALSE, bFound = FALSE;
HANDLE hSnapshot, hProcess, hThread;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;

if (INVALID_HANDLE_VALUE ==
(hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
return FALSE;

bMore = Module32First(hSnapshot, &me);
for (; bMore; bMore = Module32Next(hSnapshot, &me))
{
if (!_tcsicmp(me.szModule, szDllPath) ||
!_tcsicmp(me.szExePath, szDllPath))
{
bFound = TRUE;
break;
}
}

if (!bFound)
{
CloseHandle(hSnapshot);
return FALSE;
}

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
CloseHandle(hSnapshot);
return FALSE;
}

pThreadProc = (LPTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(L"kernel32.dll"),
"FreeLibrary");
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);

return TRUE;
}

BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;

// 获取进程快照
pe.dwSize = sizeof(PROCESSENTRY32);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

// 遍历每一个进程
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;
//为了保证稳定性不注入pid<100的进程
if (dwPID < 100)
continue;

if (nMode == INJECTION_MODE)
InjectDll(dwPID, szDllPath);
else
EjectDll(dwPID, szDllPath);
} while (Process32Next(hSnapShot, &pe));

CloseHandle(hSnapShot);

return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
int nMode = INJECTION_MODE;
HMODULE hLib = NULL;
PFN_SetProcName SetProcName = NULL;

if (argc != 4)
{
printf("\n Usage : HideProc.exe <-hide|-show> "\
"<process name> <dll path>\n\n");
return 1;
}

// change privilege
SetPrivilege(SE_DEBUG_NAME, TRUE);

// load library
hLib = LoadLibrary(argv[3]);

// set process name to hide
SetProcName = (PFN_SetProcName)GetProcAddress(hLib, "SetProcName");
SetProcName(argv[2]);

// Inject(Eject) Dll to all process
if (!_tcsicmp(argv[1], "-show"))
nMode = EJECTION_MODE;

InjectAllProcess(nMode, argv[3]);

// free library
FreeLibrary(hLib);

return 0;
}

钩取新创建的进程

创建进程一般使用CreateProcess(A/W)函数,他们在内部又分别调用了CreateProcessInternal(A/W)函数,为了尽量不遗漏需要全部钩取他们,而这些函数一定是被某个进程调用的,于是就可以在被注入的dll中添加上钩取这些API的函数,这里比之前的注入简单一些,不需要通过pid再OpenProcess获得进程句柄,CreateProcess创建子进程成功后会返回子进程句柄,于是直接拿去用就好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);

if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

现在就实现全局钩取了:
记事本已经从进程浏览器里面消失:

新建的进程也无法看到notepad进程:

低级全局钩取

上面是高级钩取,钩取的是CreateProcessA/W,CreateProcessInternalA/W这四个函数,它们属于比较高级的函数,已经文档化了,比较稳定,一般不会改变,但是他们存在一些问题,观察钩取部分:

可以看到,钩取发生在创建子进程后,这样可能导致钩取不及时,而且还有些其他问题,例如可能程序是调用其他API,幸运的是逆向大神已经发现了这四个函数最终都会调用到ZwResumeThread,更多的,子进程创建完成后会被挂起,此函数会恢复挂起进程,钩取它可以在子进程运行前注入DLL,这就是低级钩取,当然他也有缺点,看长相就知道这个也是没有文档化的函数,可能在其他系统会发生变化。。。继续,为了篇幅,开始表演:

钩取函数

查找要钩取的函数的方法有很多,要么根据经验(nil),要么使用API监视器,要么就是调试跟踪,这个例子是钩取浏览器的API,使其当访问指定域名时重定向到另外的域名,作者的经验是钩取InternetConnect:

1
2
3
4
5
6
7
8
9
10
HINTERNET InternetConnect(
_In_ HINTERNET hInternet,
_In_ LPCTSTR lpszServerName,
_In_ INTERNET_PORT nServerPort,
_In_ LPCTSTR lpszUsername,
_In_ LPCTSTR lpszPassword,
_In_ DWORD dwService,
_In_ DWORD dwFlags,
_In_ DWORD_PTR dwContext
);

第二个是服务名,可以是域名型,也可以是IP型,现在使用od验证一下:

先设置断点,接着在浏览器输入betamao.me进行访问:

发现中断在了入口处,至少说明的确调用了此函数,此时将堆栈指向的内容改成baidu.com

看到自动重定向到了www.baidu.com

说明这的确是需要钩取的API,至于钩取方法还是昨天学的那个方法,好了,字数凑得差不多了,开始正事。

钩取代码

这里使用的为5字节代码钩取,其实热补丁方式也是可以的,看代码也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
HINTERNET WINAPI NewInternetConnectW
(
HINTERNET hInternet,
LPCWSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCTSTR lpszUsername,
LPCTSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
)
{
HINTERNET hInt = NULL;
FARPROC pFunc = NULL;
HMODULE hMod = NULL;

// 脱钩
if (!unhook_by_code("wininet.dll", "InternetConnectW", g_pICW))
{
DebugLog("NewInternetConnectW() : unhook_by_code() failed!!!\n");
return NULL;
}

// 获取原API的地址
hMod = GetModuleHandle(L"wininet.dll");
if (hMod == NULL)
{
DebugLog("NewInternetConnectW() : GetModuleHandle() failed!!! [%d]\n",
GetLastError());
goto __INTERNETCONNECT_EXIT;
}
pFunc = GetProcAddress(hMod, "InternetConnectW");
if (pFunc == NULL)
{
DebugLog("NewInternetConnectW() : GetProcAddress() failed!!! [%d]\n",
GetLastError());
goto __INTERNETCONNECT_EXIT;
}
//如果访问的是百度,就强制重定向到betamao.me
if (!_tcsicmp(lpszServerName, L"www.baidu.com")||
!_tcsicmp(lpszServerName, L"baidu.com"))
{
DebugLog("[redirect] naver, daum, nate, yahoo => reversecore\n");
hInt = ((PFINTERNETCONNECTW)pFunc)(hInternet,
L"betamao.me",
nServerPort,
lpszUsername,
lpszPassword,
dwService,
dwFlags,
dwContext);
}
else//否则不变化
{
DebugLog("[no redirect]\n");
hInt = ((PFINTERNETCONNECTW)pFunc)(hInternet,
lpszServerName,
nServerPort,
lpszUsername,
lpszPassword,
dwService,
dwFlags,
dwContext);
}

__INTERNETCONNECT_EXIT:

// 再次钩取
if (!hook_by_code("wininet.dll", "InternetConnectW",
(PROC)NewInternetConnectW, g_pICW))
{
DebugLog("NewInternetConnectW() : hook_by_code() failed!!!\n");
}

return hInt;
}

低级钩取

这是对新进程注入部分,就是先注入再调用ZwResumeThread,由于这个函数位于NtDll,不能使用热补丁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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, };

DebugLog("NewZwResumeThread() : start!!!\n");

hMod = GetModuleHandle("ntdll.dll");
if (hMod == NULL)
{
DebugLog("NewZwResumeThread() : GetModuleHandle() failed!!! [%d]\n",
GetLastError());
return NULL;
}

//获取原函数地址:ntdll!ZwQueryInformationThread()
pFuncThread = GetProcAddress(hMod, "ZwQueryInformationThread");
if (pFuncThread == NULL)
{
DebugLog("NewZwResumeThread() : GetProcAddress() failed!!! [%d]\n",
GetLastError());
return NULL;
}

statusThread = ((PFZWQUERYINFORMATIONTHREAD)pFuncThread)
(ThreadHandle, 0, &tbi, sizeof(tbi), NULL);
if (statusThread != STATUS_SUCCESS)
{
DebugLog("NewZwResumeThread() : pFuncThread() failed!!! [%d]\n",
GetLastError());
return NULL;
}

dwPID = (DWORD)tbi.ClientId.UniqueProcess;
if ((dwPID != GetCurrentProcessId()) && (dwPID != dwPrevPID))
{
DebugLog("NewZwResumeThread() => call InjectDll()\n");

dwPrevPID = dwPID;

// 改变权限
if (!SetPrivilege(SE_DEBUG_NAME, TRUE))
DebugLog("NewZwResumeThread() : SetPrivilege() failed!!!\n");

// 获取要注入的dll的路径
GetModuleFileName(GetModuleHandle(STR_MODULE_NAME),
szModPath,
MAX_PATH);
//注入
if (!InjectDll(dwPID, szModPath))
DebugLog("NewZwResumeThread() : InjectDll(%d) failed!!!\n", dwPID);
}

// 调用ntdll!ZwResumeThread()
if (!unhook_by_code("ntdll.dll", "ZwResumeThread", g_pZWRT))
{
DebugLog("NewZwResumeThread() : unhook_by_code() failed!!!\n");
return NULL;
}

pFunc = GetProcAddress(hMod, "ZwResumeThread");
if (pFunc == NULL)
{
DebugLog("NewZwResumeThread() : GetProcAddress() failed!!! [%d]\n",
GetLastError());
goto __NTRESUMETHREAD_END;
}

status = ((PFZWRESUMETHREAD)pFunc)(ThreadHandle, SuspendCount);
if (status != STATUS_SUCCESS)
{
DebugLog("NewZwResumeThread() : pFunc() failed!!! [%d]\n", GetLastError());
goto __NTRESUMETHREAD_END;
}

__NTRESUMETHREAD_END:

if (!hook_by_code("ntdll.dll", "ZwResumeThread",
(PROC)NewZwResumeThread, g_pZWRT))
{
DebugLog("NewZwResumeThread() : hook_by_code() failed!!!\n");
}

DebugLog("NewZwResumeThread() : end!!!\n");

return status;
}

书上作者不仅对每一步调用结果做了判断,并输出调试信息,而且它的注入代码也有改进,之前遇到过,注入win7失败,其实是因为内部发生了变化,不能直接使用RemoteCreateThread需要注入更底层的API,参数发生了细微变化,于是这里作者也对目标系统做出了判断,自动切换了API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//判断系统版本 
BOOL IsVistaLater()
{
OSVERSIONINFO osvi;

ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

GetVersionEx(&osvi);

if (osvi.dwMajorVersion >= 6)
return TRUE;

return FALSE;
}
//自定义API
BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
{
HANDLE hThread = NULL;
FARPROC pFunc = NULL;

if (IsVistaLater()) // Vista, 7, Server2008
{
pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateThreadEx");
if (pFunc == NULL)
{
DebugLog("MyCreateRemoteThread() : GetProcAddress() failed!!! [%d]\n",
GetLastError());
return FALSE;
}

((PFNTCREATETHREADEX)pFunc)(&hThread,
0x1FFFFF,
NULL,
hProcess,
pThreadProc,
pRemoteBuf,
FALSE,
NULL,
NULL,
NULL,
NULL);
if (hThread == NULL)
{
DebugLog("MyCreateRemoteThread() : NtCreateThreadEx() failed!!! [%d]\n", GetLastError());
return FALSE;
}
}
else // 2000, XP, Server2003
{
hThread = CreateRemoteThread(hProcess, NULL, 0,
pThreadProc, pRemoteBuf, 0, NULL);
if (hThread == NULL)
{
DebugLog("MyCreateRemoteThread() : CreateRemoteThread() failed!!! [%d]\n", GetLastError());
return FALSE;
}
}

if (WAIT_FAILED == WaitForSingleObject(hThread, INFINITE))
{
DebugLog("MyCreateRemoteThread() : WaitForSingleObject() failed!!! [%d]\n", GetLastError());
return FALSE;
}

return TRUE;
}

于是可以查看注入”效果图”:

来源


逆向工程核心工程