与模块注入的区别

代码注入没有模块的特性,无法通过检测模块隐藏的方式检测出来。代码注入将机器码写入目标的进程空间,但编写注入代码时有以下原则

  1. 不能有全局变量
    每个进程的全局变量区都是不一样的
  2. 不能使用常量字符串
    字符串字面量也是储存在静态储存区,通过一个全局变量储存地址
  3. 不能使用系统调用
    系统调用依赖于dll的输出表,在反汇编中查看时是一个间接调用,只有当程序使用它时编译器才会将地址写入。
  4. 不能嵌套调用其他函数

因为Kernel32的物理页是共享的,在我们的进程中获得的api地址和别的进程一样,可以通过远程线程传参的方式将函数地址和参数写进目标进程


实现代码注入

思路

将要执行的功能和参数用参数的形式传递给线程函数(参数包括api的参数和通过LoadLibrary获取的目标进程的api函数地址),然后将线程函数和参数全部写入目标进程,再通过线程注入的方式执行。

注意事项

在debug版中函数调用前会有一个跳转指令,要获得真正的函数地址需要加上一个偏移。

硬编码中E9代表的是jmp指令,E9后面固定跟4个字节,表示函数执行地址和当前地址的偏移。所以函数的真正执行地址就是E9所在地址+5个字节+偏移。

如下图,0x04F6就是偏移,当前地址+5+0x04F6得到函数真正的地址。


还有2个大坑注意下

vs编译的话不要用debug版,如下

编译器会生成一些本身代码之外的指令,这些指令在目标进程是无法正常执行的,在OD上调试一下看看,在选项里面选择线程中断,然后执行代码注入

如下图,函数地址已经正确传进来了,但是下面的反汇编很显然不对,在0x00E40020处直接CALL了一个无法访问的地址,所以代码注入的话不要用debug版编译,切换release版之后重新编译一下解决方案。

还有一个坑就是申请虚拟内存时,储存函数的内存权限要使用PAGE_EXECUTE_READWRITE,其他的地方普通读写就行了,否则指令无法执行。


源码

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include<stdlib.h>
#include<stdio.h>
#include<Windows.h>
#include<TlHelp32.h>


DWORD getProcID(CONST CHAR *szProcName);

/*要注入到目标进程的函数*/
DWORD WINAPI createFileThreadProc(LPVOID lpCreateFileParam);

BOOL injectRemoteCode(DWORD procID, CHAR *szNewFileName);

/*定义参数结构体,用于线程传参*/
typedef struct {
/*函数地址*/
DWORD dwCreateAPIAddr;
/*以下都是CreateFile用到的参数*/
LPCSTR lpFileName;
DWORD dwDesiredAccess;
DWORD dwShareMode;
LPSECURITY_ATTRIBUTES lpSecurityAttributes;
DWORD dwCreationDisposition;
DWORD dwFlagsAndAttributes;
HANDLE hTemplateFile;
}CREATE_FILE_PARAM, *PCREATE_FILE_PARAM;


/*定义函数指针,用于使用CreateFileA函数*/
typedef HANDLE(WINAPI *PFN_CREATE_FILE)(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);

int main(int argc, char *argv[]) {

DWORD procID;
CHAR fileName[] = "D:\\absolution\\temporary\\a.txt";
procID = getProcID("test.exe");
if (!procID) {
OutputDebugString("getProcID Error\n");
return 0;
}

injectRemoteCode(procID, fileName);

return 0;
}

DWORD getProcID(CONST CHAR *szProcName) {
HANDLE hSnapshot;
PROCESSENTRY32 entry;

hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (INVALID_HANDLE_VALUE == hSnapshot) {
OutputDebugString("CreateToolheap32Snapshot Error\n");
return 0;
}

if (Process32First(hSnapshot, &entry) == TRUE) {
do {
if (strcmp(entry.szExeFile, szProcName) == 0) {
CloseHandle(hSnapshot);
return entry.th32ProcessID;
}
} while (Process32Next(hSnapshot, &entry) == TRUE);
}

OutputDebugString("Not Found!\n");
CloseHandle(hSnapshot);
return FALSE;

}

/*要注入到目标进程的函数*/
DWORD WINAPI createFileThreadProc(LPVOID lpCreateFileParam) {
/*获取参数列表*/
PCREATE_FILE_PARAM pCreateFileParam = (PCREATE_FILE_PARAM)lpCreateFileParam;
/*获取api地址*/
PFN_CREATE_FILE pfnCreateFile = (PFN_CREATE_FILE)pCreateFileParam->dwCreateAPIAddr;

/*执行*/
pfnCreateFile(
pCreateFileParam->lpFileName,
pCreateFileParam->dwDesiredAccess,
pCreateFileParam->dwShareMode,
pCreateFileParam->lpSecurityAttributes,
pCreateFileParam->dwCreationDisposition,
pCreateFileParam->dwFlagsAndAttributes,
pCreateFileParam->hTemplateFile
);

return 0;
}

/*远程代码注入*/
BOOL injectRemoteCode(DWORD procID, CHAR *szNewFileName) {
HANDLE hProcess;
CREATE_FILE_PARAM createFileParam;
LPVOID lpFileNameAddr;
LPVOID lpFnCreateFileAddr;
LPVOID lpCreateFileParamAddr;

DWORD dwFnCreateFileSize = 0x400;
DWORD dwRealAddr;
HMODULE hMod;
HANDLE hThread;


hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);

if (hProcess == NULL) {
OutputDebugString("OpenProcess Error\n");
return FALSE;
}

/*分三段申请内存,储存文件名,参数结构体,函数*/
lpFileNameAddr = VirtualAllocEx(hProcess, NULL, strlen(szNewFileName) + 1, MEM_COMMIT, PAGE_READWRITE);
/*储存函数的内存权限PAGE_EXECUTE_READWRITE*/
lpFnCreateFileAddr = VirtualAllocEx(hProcess, NULL, dwFnCreateFileSize, MEM_COMMIT , PAGE_EXECUTE_READWRITE);
lpCreateFileParamAddr = VirtualAllocEx(hProcess, NULL, sizeof(CREATE_FILE_PARAM), MEM_COMMIT , PAGE_READWRITE);

if (lpFileNameAddr == NULL || lpFnCreateFileAddr == NULL || lpCreateFileParamAddr == NULL) {
OutputDebugString("VirtualAllocEx Error\n");
CloseHandle(hProcess);
return FALSE;
}

/*获取系统api的地址*/
hMod = LoadLibrary("kernel32.dll");
if (hMod == NULL) {
OutputDebugString("LoadLibrary Error\n");
CloseHandle(hProcess);
return FALSE;
}
createFileParam.dwCreateAPIAddr = (DWORD)GetProcAddress(hMod, "CreateFileA");
FreeLibrary(hMod);

/*将申请的内存地址写入参数结构*/
createFileParam.lpFileName = (LPCTSTR)lpFileNameAddr;
/*初始化参数列表*/
createFileParam.dwShareMode = GENERIC_READ | GENERIC_WRITE;
createFileParam.dwShareMode = 0;
createFileParam.lpSecurityAttributes = NULL;
createFileParam.dwCreationDisposition = OPEN_ALWAYS;
createFileParam.dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
createFileParam.hTemplateFile = NULL;


/*修正函数地址*/
dwRealAddr = (DWORD)createFileThreadProc;
/*当前第一个字节0xE9为jmp指令*/
if (*((BYTE *)dwRealAddr) == 0xE9) {
/*地址+1后按DWORD解引,得到偏移*/
dwRealAddr = dwRealAddr + 5 + (*(DWORD *)(dwRealAddr + 1));
}

/*在目标进程内存写入数据*/

WriteProcessMemory(hProcess, lpFileNameAddr, szNewFileName, strlen(szNewFileName) + 1, 0);
/*从jmp后的地址开始写入*/
WriteProcessMemory(hProcess, lpFnCreateFileAddr, (LPVOID)dwRealAddr, dwFnCreateFileSize, 0);
WriteProcessMemory(hProcess, lpCreateFileParamAddr, &createFileParam, sizeof(CREATE_FILE_PARAM), 0);

/*创建远程线程*/
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)lpFnCreateFileAddr,
lpCreateFileParamAddr,
0,
NULL
);



if (hThread == NULL) {
OutputDebugString("CreateREmoteThread Error\n");
CloseHandle(hProcess);
CloseHandle(hThread);
return FALSE;

}

WaitForSingleObject(hThread, INFINITE);

CloseHandle(hProcess);
CloseHandle(hThread);
return TRUE;
}