一、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 }