一、C++代码注入原则:
- 在注入代码中不允许使用API。
- 在注入代码中不允许使用全局变量。
- 在注入代码中不允许使用字符串(编译时也被当做全局变量)。
- 在注入代码中不允许使用函数嵌套。
二、注入代码编写思路:
- 在本进程通过获取 LoadLibraryA 与 GetProcess 函数的地址。
- 涉及一组参数,里面包括 {函数地址、模块地址、函数名、传递参数}。
- 传入进去后,利用LoadLibraryA 与 GetProcess 函数,在注入代码中直接现场"加载模块-获取函数-调用",来达到调用API的目的。
三、编写过程的几个坑:
- 使用typedef定义函数指针,先在msdn搜索函数原型,复制过去,将名字定义成指针并大写。
- 申请内存时的权限,参数的内存使用 PAGE_READWRITE权限;代码的内存使用PAGE_EXECUTE_READWRITE权限,否则代码无法被执行。
- 一定要预先在msdn上搜索确定函数要加载的模块以及函数名,这一步很容易出错。如果出错只能调试被注入程序获取结果,比较麻烦。
四、olldbg调试思路:
- 在 "选项-调试设置-事件"中勾选“中断于新线程”。
- 注入后就可以在新线程上一步步进行调试。
五、源代码(练习了两套,一套是注入MessageBoxA,另一套是注入CreateFileA)

1 // 代码注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
2 // 代码注入,实现在任意进程注入 messagebox() 这段代码
3
4 #include "pch.h"
5 #include <stdio.h>
6 #include <Windows.h>
7 DWORD WINAPI ThreadProc(LPVOID lpParameter);
8 // 定义传入参数
9 typedef struct _THREAD_PARAM {
10 FARPROC pFunc[2]; // 存放两个函数 LoadLibraryA ; GetProcess;
11 char szBuff[4][128]; // 存放四个参数
12 }THREAD_PARAM, *PTHREAD_PARAM;
13
14 // LoadLibraryA函数
15 typedef HMODULE(WINAPI *PFLOADLIBARAYA)(LPCSTR lpLibFileName);
16
17 // GetProcAddress()函数
18 typedef FARPROC(WINAPI *PGETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName);
19
20 // MessageBox()函数
21 typedef int(WINAPI *PMESSAGEBOXA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
22
23 // 注入目标进程的线程函数
24 DWORD WINAPI ThreadProc(LPVOID lpParameter) {
25 /*
26 牢记:在代码注入中
27 1. 不能使用系统函数。
28 2. 不能使用全局变量。
29 2. 不能使用字符串(因为这会被当成全局函数)
30 */
31
32
33 // 先将传入的参数值取出来
34 PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParameter;
35 HMODULE hMod = NULL;
36 FARPROC pFunc = NULL; // messageBox这个函数
37
38 // 先调用 LoadLibarayA函数来加载user32.dll
39 hMod = ((PFLOADLIBARAYA)pParam->pFunc[0])(pParam->szBuff[0]); //LoadLibraryA(kernel32.dll) 先获取模块句柄,在获取 MessageBox这个函数。
40 if (!hMod) return 1;
41 // 再来调用 GetProcess 得到 MessageBox 这个函数
42 pFunc = (FARPROC)((PGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuff[1]);
43 if (!pFunc) return 1;
44 // 调用函数来弹出对话框
45 ((PMESSAGEBOXA)pFunc)(NULL, pParam->szBuff[2], pParam->szBuff[3], MB_OK);
46
47 return 0;
48 }
49
50 BOOL InjectCode(DWORD Pid) {
51
52
53
54 THREAD_PARAM param = { 0, };
55 LPVOID lpBuffer[2] = { 0, }; // 存储两块开辟内存,一块存储参数,另一块存储代码
56
57 // 获取 kernel32.dll模块句柄,因为需要的函数全部存储在此
58 HMODULE hModule = GetModuleHandleA(("kernel32.dll"));
59
60 // 初始化传入线程的参数
61 param.pFunc[0] = GetProcAddress(hModule, "LoadLibraryA");
62 param.pFunc[1] = GetProcAddress(hModule, "GetProcAddress");
63 strcpy_s(param.szBuff[0], "user32.dll"); //加载的模块名
64 strcpy_s(param.szBuff[1], "MessageBoxA"); //加载的函数名
65 strcpy_s(param.szBuff[2], "abc"); //传入的第一个参数
66 strcpy_s(param.szBuff[3], "def"); // 传入的第二个参数
67
68 //开辟内存,将线程参数传入内存中
69 HANDLE hProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
70 DWORD dwSize = sizeof(THREAD_PARAM);
71
72 lpBuffer[0] = VirtualAllocEx(hProcessHandle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
73
74 if (WriteProcessMemory(hProcessHandle, lpBuffer[0], (LPVOID)¶m, dwSize, NULL)) {
75 printf("第一段代码写入成功\n");
76 }
77 else {
78 printf("第一段代码写入失败,错误码:%d\n", GetLastError());
79 return FALSE;
80 }
81
82 // 开辟内存给线程,并将注入代码写入
83 dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
84 lpBuffer[1] = VirtualAllocEx(hProcessHandle, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
85 if (WriteProcessMemory(hProcessHandle, lpBuffer[1], (LPVOID)ThreadProc, dwSize, NULL)) {
86 printf("第二段代码写入成功\n");
87 }
88 else {
89 printf("第二段代码写入失败,错误码:%d\n", GetLastError());
90 return FALSE;
91 }
92
93 // 开始创建远程线程
94 HANDLE hThread = CreateRemoteThread(hProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)lpBuffer[1], (LPVOID)lpBuffer[0], 0, NULL);
95 if (hThread) {
96 printf("线程开始执行!\n");
97 }
98 else {
99 printf("创建远程线程失败,错误码:%d\n", GetLastError());
100 return FALSE;
101 }
102 // 等待线程开始执行
103 printf("到目前位置成功!");
104 WaitForSingleObject(hThread, INFINITE);
105 CloseHandle(hProcessHandle);
106 CloseHandle(hThread);
107
108
109 return TRUE;
110 }
111
112 // 拒绝访问时的提权代码
113 BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
114 {
115 TOKEN_PRIVILEGES tp;
116 HANDLE hToken;
117 LUID luid;
118
119 if (!OpenProcessToken(GetCurrentProcess(),
120 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
121 &hToken))
122 {
123 wprintf(L"OpenProcessToken error: %u\n", GetLastError());
124 return FALSE;
125 }
126
127 if (!LookupPrivilegeValue(NULL, // lookup privilege on local system
128 lpszPrivilege, // privilege to lookup
129 &luid)) // receives LUID of privilege
130 {
131 wprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());
132 return FALSE;
133 }
134
135 tp.PrivilegeCount = 1;
136 tp.Privileges[0].Luid = luid;
137 if (bEnablePrivilege)
138 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
139 else
140 tp.Privileges[0].Attributes = 0;
141
142 // Enable the privilege or disable all privileges.
143 if (!AdjustTokenPrivileges(hToken,
144 FALSE,
145 &tp,
146 sizeof(TOKEN_PRIVILEGES),
147 (PTOKEN_PRIVILEGES)NULL,
148 (PDWORD)NULL))
149 {
150 wprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError());
151 return FALSE;
152 }
153
154 if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
155 {
156 wprintf(L"The token does not have the specified privilege. \n");
157 return FALSE;
158 }
159
160 return TRUE;
161 }
162
163 int main(int argc,char *argv[])
164 {
165 //判断参数个数
166 if (argc != 2) {
167 printf("\n USAGE : %s <pid>\n", argv[0]);
168 return 1;
169 }
170 if (!SetPrivilege(SE_DEBUG_NAME, TRUE)) {
171 printf("提权失败!\n");
172 }
173 else {
174 printf("提权成功!\n");
175 //atol 将字符串转换为数字
176 if (InjectCode(atol(argv[1]))) {
177 printf("开启成功!\n");
178 }
179 else {
180 printf("开启失败!\n");
181 }
182 }
183
184 getchar();
185 }

1 // 代码注入CreateFile函数.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
2 //
3
4 #include "pch.h"
5 #include <iostream>
6 #include <Windows.h>
7
8 // 构建参数
9 typedef struct _thread_param {
10 FARPROC pFunc[2]; // LoadLibrary / GetProcAddress 函数
11 // 这里应该存放字符
12 char szBuffer[3][128]; // 加载的模块名字 user32.dll / CreateFileA / szFilePath
13 }THREAD_PARAM,*PTHREAD_PARAM;
14
15 // 先来构建函数指针
16 typedef HANDLE (WINAPI *PCREATEFILEA)(
17 LPCSTR lpFileName,
18 DWORD dwDesiredAccess,
19 DWORD dwShareMode,
20 LPSECURITY_ATTRIBUTES lpSecurityAttributes,
21 DWORD dwCreationDisposition,
22 DWORD dwFlagsAndAttributes,
23 HANDLE hTemplateFile
24 );
25
26 // LoadLibrary
27 typedef HMODULE (WINAPI* PLOADLIBRARYA)(
28 LPCSTR lpLibFileName
29 );
30
31
32 // GetProcAddress
33 typedef FARPROC (WINAPI* PGETPROCADDRESS)(
34 HMODULE hModule,
35 LPCSTR lpProcName
36 );
37
38 // GetProcess
39 DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) {
40
41 // 先取出参数,注意:传入一个指针,对应的也应该生成一个指针变量
42 PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParameter;
43
44 // 注意:loadLibraryA与getprocaddress 在kernel32.dll,这个不用导出,因为我们直接传入其函数地址,直接根据函数指针调用即可
45
46 // 调用 LoadLibarayA来获取存放CreateFile模块
47 HMODULE hModule = (HMODULE)((PLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuffer[0]);
48 if (!hModule) return 1;
49
50
51 // 调用GetPrcAddress来加载模块
52 FARPROC pFunc = (FARPROC)((PGETPROCADDRESS)pParam->pFunc[1])(hModule, pParam->szBuffer[1]);
53 if (!pFunc) return 1;
54
55
56 // 现在调用CreateFileA函数
57 ((PCREATEFILEA)pFunc)(pParam->szBuffer[2], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
58
59 return 0;
60 }
61
62 BOOL CodeInject(DWORD Pid) {
63
64 // 我们的思路是获取目标进程句柄,分配-写入,最后再根据地址开启返回线程
65
66
67 // 加载LoadLibraryA和GetProcAddress 两个函数的地址并且初始化参数
68 THREAD_PARAM param;
69 HMODULE hMoudle = LoadLibraryA("kernel32.dll");
70 param.pFunc[0] = (FARPROC)GetProcAddress(hMoudle, "LoadLibraryA");
71 param.pFunc[1] = (FARPROC)GetProcAddress(hMoudle, "GetProcAddress");
72 strcpy_s(param.szBuffer[0], "Kernel32.dll");
73 strcpy_s(param.szBuffer[1], "CreateFileA");
74 strcpy_s(param.szBuffer[2], "OneFile.txt");
75
76
77 LPVOID pBuffer[2]; // 两个存储目标进程地址的数组。
78 int Res;
79 HANDLE hProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
80
81 // 向目标进程分配参数内存,参数内存可读写。
82 DWORD dwSize = sizeof(THREAD_PARAM);
83 pBuffer[0] = VirtualAllocEx(hProcessHandle, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
84 if (!WriteProcessMemory(hProcessHandle, pBuffer[0], (LPVOID)¶m, dwSize, NULL)) {
85 printf("写入参数失败,错误码:%d", GetLastError());
86 return FALSE;
87 }
88
89 // 向目标进程分配代码内存,参数内存读写-可执行。
90 dwSize = (DWORD)CodeInject - (DWORD)ThreadProc;
91 pBuffer[1] = VirtualAllocEx(hProcessHandle, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
92 if (!WriteProcessMemory(hProcessHandle, pBuffer[1], (LPVOID)ThreadProc, dwSize, NULL)) {
93 printf("写入代码失败,错误码:%d", GetLastError());
94 return FALSE;
95 }
96
97 // 创建远程线程并执行
98 HANDLE hThread = CreateRemoteThread(hProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)pBuffer[1], (LPVOID)pBuffer[0], 0, (LPDWORD )&Res);
99 if (!hThread) {
100 printf("创建远程线程失败,错误码:%d", GetLastError());
101 return FALSE;
102 }
103 printf("线程结果:%d\n", Res);
104 // 关闭资源句柄
105 WaitForSingleObject(hThread, INFINITE);
106 CloseHandle(hMoudle);
107 CloseHandle(hThread);
108
109
110 return TRUE;
111 }
112
113 BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
114 {
115 TOKEN_PRIVILEGES tp;
116 HANDLE hToken;
117 LUID luid;
118
119 if (!OpenProcessToken(GetCurrentProcess(),
120 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
121 &hToken))
122 {
123 wprintf(L"OpenProcessToken error: %u\n", GetLastError());
124 return FALSE;
125 }
126
127 if (!LookupPrivilegeValue(NULL, // lookup privilege on local system
128 lpszPrivilege, // privilege to lookup
129 &luid)) // receives LUID of privilege
130 {
131 wprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());
132 return FALSE;
133 }
134
135 tp.PrivilegeCount = 1;
136 tp.Privileges[0].Luid = luid;
137 if (bEnablePrivilege)
138 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
139 else
140 tp.Privileges[0].Attributes = 0;
141
142 // Enable the privilege or disable all privileges.
143 if (!AdjustTokenPrivileges(hToken,
144 FALSE,
145 &tp,
146 sizeof(TOKEN_PRIVILEGES),
147 (PTOKEN_PRIVILEGES)NULL,
148 (PDWORD)NULL))
149 {
150 wprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError());
151 return FALSE;
152 }
153
154 if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
155 {
156 wprintf(L"The token does not have the specified privilege. \n");
157 return FALSE;
158 }
159
160 return TRUE;
161 }
162
163 int main(int argc,char*argv[])
164 {
165
166 // 在本地创建一个文件夹
167 // HANDLE hFile = CreateFileA("一个文件", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
168 //CloseHandle(hFile);
169 // 现在实现代码注入,要求在notepad.exe中运行上段代码。
170
171 //判断参数个数
172 if (argc != 2) {
173 printf("\n USAGE : %s <pid>\n", argv[0]);
174 return 1;
175 }
176
177 if (!SetPrivilege(SE_DEBUG_NAME, TRUE)) {
178 printf("提权失败!\n");
179 }
180 else {
181 printf("提权成功!\n");
182 if (CodeInject(atol(argv[1]))) {
183 printf("开启成功1!\n");
184 }
185 else {
186 printf("开启失败!\n");
187 }
188 }
189
190
191 getchar();
192
193 }
