Давным-давно, во времена мифов и легенд, когда древние Боги были мстительны и жестоки и обрушивали на программистов все новые и новые проклятия... На хабре была опубликована статья про сплайсинг.
В качестве примера в статье был приведен довольно масштабный код, который воспринимался не особо легко. Захотелось разобраться в процессе инжекта, а также написать более простой и менее громоздкий код.
Вкратце, инжектинг - это подгрузка нашей библиотеки в сторонний процесс, а сплайсинг - перехват какой-либо функции (мы перехватывали WinAPI) и модификация её работы средствами этой самой библиотеки.
Пример будет состоять из двух частей: 1 - библиотека, 2 - инжектор, который будет внедрять библиотеку в целевой процесс. Библиотеку будем писать на masm, что позволит в разы сократить объемы кода, а инжектор - на Си.
Начнем с кода библиотеки [last updated on 15 aug 2010]. Что она делает? При подгрузке она заменяет первые 5 байт функции WinAPI, которую мы хотим перехватить, на JUMP на наш перехватчик. Мы можем это сделать в большинстве случаев, когда функция имеет пролог (то есть 100%, если функция принимает больше нуля аргументов, в этом случае в начале ее будут инструкции mov edi,edi; push ebp; mov ebp;esp, или же если функция имеет локальные переменные). Иногда функция не имеет пролога, но мы все равно можем осуществить перехват, если в начале функции имеются несущественные команды, например, NOP'ы (такое было обнаружено в функции GetTickCount на Windows Vista x64). Таких функций мало, судя по всему. Я приведу пример перехвата GetTickCount, но далеко не факт, что он сработает на вашей системе. Скажем, в XP SP3 кучи NOP'ов в начале тела функции нет. Всегда смотрите, как начинается функция без аргументов в системных библиотеках, прежде чем осуществлять ее перехват. У меня GetTickCount начиналась так:
1 2 3 4 5 6 7 8 9 |
Address Hex dump Command Comments 756F1110 EB 05 JMP SHORT 756F1117 ; INT kernel32.GetTickCount(void) 756F1112 90 NOP 756F1113 90 NOP 756F1114 90 NOP 756F1115 90 NOP 756F1116 90 NOP 756F1117 8B0D 2403FE7F MOV ECX,DWORD PTR DS:[7FFE0324] ... |
Слишком короткие функции без пролога перехватить так просто не удастся (например, GetCurrentProcess или GetCommandLineA), потому что первые 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 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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
.486 ; create 32 bit code .model flat, stdcall ; 32 bit memory model option casemap :none ; case sensitive ;подключаем макросы и библиотеки include \masm32\include\windows.inc include \masm32\macros\macros.asm include \masm32\macros\windows.asm uselib kernel32, user32, masm32 ;структурка, в которую мы запишем свой jump ;на код нашего перехватчика функции JUMPNEAR STRUCT opcd BYTE ? reladdr DWORD ? JUMPNEAR ENDS global_hook_cnt = 0 ;Макрос для вызова оригинальной функции ;с оригинальными параметрами из тела перехватчика ;(пример - далее) ;func - имя перехваченной функции ;args - число аргументов ;have_prologue - имеет ли функция пролог ;(то есть, есть ли в ее начале команды mov edi,edi, push ebp; mov ebp, esp) ;(не задавайте для функций с числом аргументов больше 0, они всегда имеют пролог) HOOK_ORIGINAL_CALL MACRO func:REQ, args:=< 0 >, have_prologue:=< 1 > cnt = 0 REPEAT args push [ebp+(&args - cnt + 1)*4] cnt = cnt + 1 ENDM push offset @CatStr(next_inst_, %global_hook_cnt) IF have_prologue EQ 1 push ebp mov ebp,esp ENDIF mov eax,@CatStr(&func, _hook) add eax,5 jmp eax @CatStr(next_inst_, %global_hook_cnt): global_hook_cnt = global_hook_cnt + 1 ENDM ;Макрос для вызова оригинальной функции ;с произвольными параметрами из любого места программы ;(если перехват установлен) ;(пример - далее) ;func - имя перехваченной функции ;have_prologue - имеет ли функция пролог ;(не задавайте для функций с числом аргументов больше 0, они всегда имеют пролог) ;params - список аргументов функции HOOK_ORIGINAL_CALL_PARAM MACRO func:REQ, have_prologue:=< 1 >, params:VARARG count = 0 FOR xparam, <params> count = count + 1 @CatStr(var,%count) TEXTEQU @CatStr(&xparam) ENDM REPEAT count push @CatStr(var,%count) count = count - 1 ENDM push offset @CatStr(next_inst_, %global_hook_cnt) IF have_prologue EQ 1 push ebp mov ebp,esp ENDIF mov eax,@CatStr(&func, _hook) add eax,5 jmp eax @CatStr(next_inst_, %global_hook_cnt): global_hook_cnt = global_hook_cnt + 1 ENDM ;Макрос для установки перехвата ;(пример - далее) ;lib - имя библиотеки, в которой содержится функция ;func - имя функции ;hook_label - название метки, по которой лежит наше тело перехватчика ;ifload - загрузить ли предварительно библиотеку (0 по умолчанию) ;have_prologue - имеет ли функция пролог ;(не задавайте для функций с числом аргументов больше 0, они всегда имеют пролог) SET_HOOK MACRO lib:REQ, func:REQ, hook_label:REQ, ifload:=< 0 >, have_prologue:=< 1 > %ECHO [The hook on &func is SET on @CatStr(%@Line) Line] .data? @CatStr(libn, %global_hook_cnt) dd ? IFNDEF &func&_hook_ @CatStr(&func, _hook) dd ? @CatStr(&func, _hook_) EQU <1> ENDIF IF have_prologue EQ 0 IFNDEF &func&_prologue_ @CatStr(&func, _prologue1) dw ? @CatStr(&func, _prologue2) db ? @CatStr(&func, _prologue3) dw ? @CatStr(&func, _prologue_) EQU <1> ENDIF ENDIF @CatStr(protect_, %global_hook_cnt) dd ? .code IF ifload EQ 1 mov @CatStr(libn, %global_hook_cnt), FUNC(LoadLibrary,chr$("&lib")) ELSE mov @CatStr(libn, %global_hook_cnt), FUNC(GetModuleHandle,chr$("&lib")) ENDIF mov @CatStr(&func, _hook), FUNC(GetProcAddress, @CatStr(libn, %global_hook_cnt), chr$("&func")) invoke VirtualProtect, @CatStr(&func, _hook), sizeof JUMPNEAR, PAGE_READWRITE, offset @CatStr(protect_, %global_hook_cnt) mov eax, @CatStr(&func, _hook) IF have_prologue EQ 0 mov cx, word ptr [eax] mov @CatStr(&func, _prologue1), cx mov cl, byte ptr [eax+2] mov @CatStr(&func, _prologue2), cl mov cx, word ptr [eax+3] mov @CatStr(&func, _prologue3), cx ENDIF assume eax: ptr JUMPNEAR mov [eax].opcd, 0e9h mov ecx, offset &hook_label sub ecx,@CatStr(&func, _hook) sub ecx,5 mov [eax].reladdr,ecx assume eax:nothing invoke VirtualProtect, @CatStr(&func, _hook), sizeof JUMPNEAR, @CatStr(protect_, %global_hook_cnt), offset @CatStr(protect_, %global_hook_cnt) global_hook_cnt = global_hook_cnt + 1 ENDM ;Макрос для снятия перехвата ;(пример - далее) ;func - имя функции ;have_prologue - имеет ли функция пролог ;(не задавайте для функций с числом аргументов больше 0, они всегда имеют пролог) REMOVE_HOOK MACRO func:REQ, have_prologue:=< 1 > %ECHO [The hook on &func is REMOVED on @CatStr(%@Line) Line] .data? @CatStr(protect_, %global_hook_cnt) dd ? .code invoke VirtualProtect, @CatStr(&func, _hook), sizeof JUMPNEAR, PAGE_READWRITE, offset @CatStr(protect_, %global_hook_cnt) mov eax, @CatStr(&func, _hook) IF have_prologue EQ 0 mov cx, @CatStr(&func, _prologue1) mov word ptr [eax], cx mov cl, @CatStr(&func, _prologue2) mov byte ptr [eax+2], cl mov cx, @CatStr(&func, _prologue3) mov word ptr [eax+3], cx ELSE mov word ptr [eax], 0ff8bh mov byte ptr [eax+2], 55h mov word ptr [eax+3], 0e589h ENDIF invoke VirtualProtect, @CatStr(&func, _hook), sizeof JUMPNEAR, @CatStr(protect_, %global_hook_cnt), offset @CatStr(protect_, %global_hook_cnt) global_hook_cnt = global_hook_cnt + 1 ENDM ;начало примера .code ;Тело нашего перехватчика для функции MessageBoxA MyFunc: push ebp mov ebp,esp ;сюда можно поместить любой код ;[ebp+8] будет содержать первый аргумент функции ;[ebp+12] - второй аргумент и т.д. ;[ebp+4] будет содержать адрес возврата из функции ;вызываем оригинальную функцию с переданными параметрами HOOK_ORIGINAL_CALL MessageBoxA, 4 ;это эквивалентно записи ;HOOK_ORIGINAL_CALL_PARAM MessageBoxA,1, [ebp+8], [ebp+12], [ebp+16], [ebp+20] ;а теперь подменим параметры (текст месадж бокса и иконку): HOOK_ORIGINAL_CALL_PARAM MessageBoxA,1, [ebp+8], chr$("ахаха, перехват быдлозащиты!"), [ebp+16], MB_SYSTEMMODAL or MB_ICONERROR ;сюда тоже можно поместить любой код ;[ebp+8] будет содержать первый аргумент функции ;[ebp+12] - второй аргумент и т.д. ;[ebp+4] будет содержать адрес возврата из функции ;регистр eax будет содержать возвращенное функцией значение ;его можно заменить здесь pop ebp retn 4*4; (число аргументов функции MessageBoxA) * 4 ;Тело перехватчика GetTickCount (помните, не факт, что это заработает на вашей системе, как я уже говорил!) MyFunc2: push ebp mov ebp,esp ;сюда можно поместить любой код ;[ebp+4] будет содержать адрес возврата из функции ;вызываем оригинальную функцию HOOK_ORIGINAL_CALL GetTickCount, 0, 0 ;это эквивалентно записи ;HOOK_ORIGINAL_CALL_PARAM GetTickCount,0 ;сюда тоже можно поместить любой код ;[ebp+4] будет содержать адрес возврата из функции ;регистр eax будет содержать возвращенное функцией значение, его можно заменить здесь mov eax,1337 ;вот мы и подменили ответ функции pop ebp ret ;(0 аргументов у GetTickCount, поэтому просто ret) ;Точка входа в DLL LibMain proc instance:DWORD,reason:DWORD,reserved:DWORD LOCAL buf [20] :byte ;локальная переменная для хранения значения, возвращенного GetTickCount .if reason == DLL_PROCESS_ATTACH ;если наша DLL свежезагружена ;устанавливаем перехват MessageBoxA SET_HOOK user32.dll, MessageBoxA, MyFunc ;этот вызов будет перехвачен invoke MessageBox,0,chr$("Hooked message box"),chr$("Test"),MB_SYSTEMMODAL or MB_ICONINFORMATION ;этот вызов не будет перехвачен - он выполняется в обход тела перехватчика HOOK_ORIGINAL_CALL_PARAM MessageBoxA,1, 0,chr$("Message box call without hook"),chr$("Test"),MB_SYSTEMMODAL or MB_ICONINFORMATION ;снимаем перехват REMOVE_HOOK MessageBoxA ;этот вызов уже не перехватывается invoke MessageBox,0,chr$("Original message box call"),chr$("Test"),MB_SYSTEMMODAL or MB_ICONINFORMATION ;теперь установим перехват GetTickCount (эта функция не имеет пролога) SET_HOOK kernel32.dll, GetTickCount, MyFunc2, 0, 0 ;этот вызов перехвачен, и значение тут подменено - 1337 invoke GetTickCount ;преобразуем ответ функции в текстовый вид invoke wsprintf,addr buf,chr$("%u"),eax ;выведем ответ, который вернула GetTickCount (он тут подменен) invoke MessageBox,0,addr buf,chr$("Test"),MB_SYSTEMMODAL or MB_ICONINFORMATION ;убираем перехват REMOVE_HOOK GetTickCount, 0 ;этот вызов уже не перехвачен invoke GetTickCount ;преобразуем ответ функции в текстовый вид invoke wsprintf,addr buf,chr$("%u"),eax ;выведем ответ, который вернула GetTickCount invoke MessageBox,0,addr buf,chr$("Test"),MB_SYSTEMMODAL or MB_ICONINFORMATION mov eax,1 ;сообщаем об успешной загрузке нашей DLL .endif ret LibMain ENDP end LibMain |
Вообще, данный пример ставит перехваты и перехватывает функции, которые сам же и вызывает. Если мы хотим перехватить функции чужого процесса, нужно скомпилировать этот ассемблерный листинг как 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 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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
#undef UNICODE #include <stddef.h> #include <stdio.h> #include <windows.h> #include <psapi.h> #include <sys/stat.h> #define BUFSIZE 100 //Имя внедряемой библиотеки #define DLL_NAME "mem.dll" //структура описывает поля, в которых содержится код внедрения #pragma pack(push, 1) struct INJECTORCODE { BYTE instr_push_loadlibrary_arg; //инструкция push DWORD loadlibrary_arg; //аргумент push WORD instr_call_loadlibrary; //инструкция call DWORD adr_from_call_loadlibrary; BYTE instr_push_exitthread_arg; DWORD exitthread_arg; WORD instr_call_exitthread; DWORD adr_from_call_exitthread; DWORD addr_loadlibrary; DWORD addr_exitthread; //адрес функции ExitThread BYTE libraryname[100]; //имя и путь к загружаемой библиотеке }; #pragma pack(pop) BOOL InjectDll(DWORD pid, char *lpszDllName) { HANDLE hProcess; BYTE *p_code; INJECTORCODE cmds; DWORD wr, id; //Открыть процесс с нужным доступом hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|0x1000|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, pid); if(hProcess == NULL) { MessageBoxA(NULL, "You have not enough rights to attach dlls", "Error!", 0); return FALSE; } //Зарезервировать память в процессе p_code = (BYTE*)VirtualAllocEx(hProcess, 0, sizeof(INJECTORCODE), MEM_COMMIT, PAGE_READWRITE); if(p_code == NULL) { MessageBox(NULL, "Unable to alloc memory in remote process", "Error!", 0); return FALSE; } //Инициализировать машинный код cmds.instr_push_loadlibrary_arg = 0x68; //машинный код инструкции push cmds.loadlibrary_arg = (DWORD)((BYTE*)p_code + offsetof(INJECTORCODE, libraryname)); cmds.instr_call_loadlibrary = 0x15ff; //машинный код инструкции call cmds.adr_from_call_loadlibrary = (DWORD)(p_code + offsetof(INJECTORCODE, addr_loadlibrary)); cmds.instr_push_exitthread_arg = 0x68; cmds.exitthread_arg = 0; cmds.instr_call_exitthread = 0x15ff; cmds.adr_from_call_exitthread = (DWORD)(p_code + offsetof(INJECTORCODE, addr_exitthread)); cmds.addr_loadlibrary = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); cmds.addr_exitthread = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitThread"); if(strlen(lpszDllName) > 99) { MessageBox(NULL, "Dll Name too long", "Error!", 0); return FALSE; } strcpy((char*)cmds.libraryname, lpszDllName ); /*После инициализации cmds в мнемонике ассемблера выглядит следующим образом: push adr_library_name ;аргумент ф-ции loadlibrary call dword ptr [loadlibrary_adr] ;вызвать LoadLibrary push exit_thread_arg ;аргумент для ExitThread call dword ptr [exit_thread_adr] ;вызвать ExitThread */ //Записать машинный код по зарезервированному адресу WriteProcessMemory(hProcess, p_code, &cmds, sizeof(cmds), &wr); //Выполнить машинный код DWORD old_rights; HANDLE z = CreateRemoteThread(hProcess, NULL, 0, (unsigned long (__stdcall *)(void *))p_code, 0, 0, &id); //Ожидать завершения удаленного потока WaitForSingleObject(z, INFINITE); //Освободить память VirtualFreeEx(hProcess, (void*)p_code, sizeof(cmds), MEM_RELEASE); return TRUE; } //Функция установки привилегий отладчика int AdjustPrivileges() { HANDLE hToken; TOKEN_PRIVILEGES tp; TOKEN_PRIVILEGES oldtp; DWORD dwSize = sizeof(TOKEN_PRIVILEGES); LUID luid; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) return 1; return 0; } if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { CloseHandle(hToken); return 0; } ZeroMemory(&tp, sizeof(tp)); tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize)) { CloseHandle(hToken); return 0; } CloseHandle(hToken); return 1; } //Функция проверки существования файла int file_exists(char * fileName) { struct stat buf; return stat(fileName, &buf) ? 0 : 1; } int gogo(DWORD pid) { //Настраиваем привилегии if(!AdjustPrivileges()) { MessageBox(NULL, "Can't adjust privileges", "Error", 0); return 0; } if(pid) { if(file_exists(DLL_NAME)) { //Получаем полный путь к библиотеке и инжектим её в целевой процесс char dll_path[BUFSIZE]; GetFullPathName(DLL_NAME, BUFSIZE, dll_path, NULL); InjectDll(pid, dll_path); } else { MessageBox(NULL, "Can't find dll file", "Error", 0); } } else { MessageBox(NULL, "Can't find process", "Error", 0); } return 0; } //Интерфейс программы LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND button, edit, label; HINSTANCE hInst; HANDLE hFont = CreateFont ( 14, 0, 0, 0, FW_EXTRALIGHT, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Courier New" ); switch(msg) { case WM_CREATE: //Создаем элементы интерфейса button = CreateWindow ( "button", "Inject!", WS_CHILD | WS_VISIBLE, 85, 10, 60, 22, hwnd, (HMENU) 2, NULL, NULL ); edit = CreateWindow ( "Edit", "1", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER, 30, 10, 50, 22, hwnd, (HMENU) 3, NULL, NULL ); label = CreateWindow ( "static", "PID", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 5, 13, 20, 22, hwnd, (HMENU) 4, NULL, NULL ); //Устанавливаем шрифт для элементов интерфейса SendMessage(button, WM_SETFONT, WPARAM(hFont), TRUE); SendMessage(edit, WM_SETFONT, WPARAM(hFont), TRUE); SendMessage(label, WM_SETFONT, WPARAM(hFont), TRUE); break; case WM_COMMAND: //Обрабатываем нажатие на клавишу if(LOWORD(wParam) == 2) { char buf[10]; DWORD pid; GetWindowText(edit, buf, 10); sscanf(buf, "%u", &pid); gogo(pid); } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } //Точка входа int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg ; WNDCLASS wc = {0}; wc.lpszClassName = "Edit Control"; wc.hInstance = hInstance; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpfnWndProc = WndProc; wc.hCursor = LoadCursor(0, IDC_ARROW); RegisterClass(&wc); CreateWindow ( wc.lpszClassName, "Injector", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, 220, 220, 155, 80, 0, 0, hInstance, 0 ); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } |
Код основан на этой статье с RSDN.
Исходный код dll'ки и инжектора одним архивом: скачать
Таким образом, мы получили комплект, позволяющий легко и непринужденно внедряться в чужие процессы. Также его всегда можно модифицировать под свои цели и использовать для взлома различных быдлопрограмм, что было продемонстрировано в предыдущих постах. Следует отметить, что при внедрении в .NET процессы есть свои заморочки, которые остаются за рамками данной статьи.
С тех самых времен прошло много столетий... Боги были усмирены человеком, но теперь мы имеем новые проблемы, которые порой страшнее гнева всевышних - это, конечно, бурление говн недовольных разработчиков, считавших самих себя Богами программирования под всякие социальные ресурсы нынешнего времени, но свергнутых также, как когда-то и настоящие Боги.
В общем, делайте инжекты, ломайте чужой софт. Этот мир интересней, чем вам кажется. (с)
нифига не понял с первого раза) надо будет поучиться) эт значит что ты не будешь больше делать инжекты?
Будет. Мы просто поделились опытом, знаниями и исходниками, мало ли кто еще захочет попробовать.
какие виды прог (на каких языках) можно ломать? как определить подходящую прогу ?=)
Любые проги. WinXP, например, можешь взломать - для этого в начале кода инжектора допиши #include
#include <haknut_vindu.h>
Всё это конечьно хорошо,но лучше автоматезировать процесс.Для того что б не париться с прологами можно прикрутить дизассемблер длин и буфер под сохраняемые байты выделять по ходу пьессы,после чего ставить джамп на остаток функции.Так же проверять на уже установленные перехваты и в случае чего мапить либу с диска и оттуда брать оригинальные байты.Либо ставить и снимать перехват,снова ставить и опять снимать.
Полностью согласен, так можно было бы перехватывать в принципе любые функции длиннее пяти байт (ну или чуть больше пяти, если инструкции первые в 5 не укладываются). Если будет время, попробую усовершенствовать макросы, чтобы можно было хукать любые функции.
Вот моя "универсальная реализация"
proc SetHook ModuleName,OldFuncAdr,NewFuncAdr,Old
locals
BaseMap dd 0
fApi dd 0
OldProtect dd ?
JmpGate dd ?
endl
pushad
mov edi,[OldFuncAdr]
mov edi,[edi]
mov [fApi],edi
push edi
pop esi
xor ecx,ecx
@@:
cmp ecx,5
jnb @1
call Catchy32 ;Дизассемблер длин
cmp eax,0
jnz @2
jmp @3
@2:
add esi,eax
add ecx,eax
jmp @b
@1:
push ecx
mov ebx,ecx
add ebx,5
invoke VirtualAlloc,0,ebx,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE
test eax,eax
je @3
mov [JmpGate],eax
cmp [ModuleName],0
je @5
cmp byte [edi],0e9h
je @f
cmp byte [edi],0e8h
jne @5
@@:
invoke GetModuleHandle,[ModuleName]
cmp eax,0
je @3
mov ecx,edi
sub ecx,eax
lea eax,[fApi]
lea edx,[BaseMap]
stdcall MapDll,[ModuleName],ecx,eax,edx
cmp [BaseMap],0
je @3
@5:
pop ecx
mov eax,[JmpGate]
stdcall MemSet,eax,ebx,90h
stdcall MemCpy,eax,[fApi],ecx
mov byte [eax+ecx],0e9h
sub esi,eax
sub esi,ecx
sub esi,5
mov [eax+ecx+1],esi
lea ebx,[OldProtect]
invoke VirtualProtect,edi,5,PAGE_EXECUTE_READWRITE,ebx
mov eax,[NewFuncAdr]
sub eax,edi
sub eax,5
mov byte [edi],0e9h
mov [edi+1],eax
invoke VirtualProtect,edi,5,[OldProtect],ebx
mov eax,[Old]
mov ecx,[JmpGate]
mov [eax],ecx
cmp [BaseMap],0
je @3
invoke UnmapViewOfFile,[BaseMap]
@3:
popad
ret
endp
и че с этим всем делать? куда засунуть и как сохранить?
А у меня работает только если использовать VirtualProtectEx (на Windows7). В остальном все прекрасно. Огромное спасибо за отличный сайт.
Я правильно понял, что в этом фрагменте кода загружается два байта,
то есть адрес перехваченной функции в регистр сx,где eax адресс записанной функции,
[eax+2],[eax+3] задают смещение и адресс искомой функции подменяется на адресс 0e9h в строке mov [eax].opcd, 0e9h-?
А точнее подменяют адресс функции имеющей пролог-?
IF have_prologue EQ 0
mov cx, word ptr [eax]
mov @CatStr(&func, _prologue1), cx
mov cl, byte ptr [eax+2]
mov @CatStr(&func, _prologue2), cl
mov cx, word ptr [eax+3]
mov @CatStr(&func, _prologue3), cx
ENDIF
assume eax: ptr JUMPNEAR
mov [eax].opcd, 0e9h
mov ecx, offset &hook_label
sub ecx,@CatStr(&func, _hook)
sub ecx,5
mov [eax].reladdr,ecx
assume eax:nothing
Если я правильно понял:
Вариант один
1. Открываем процес, получаем привилегии
2. В память пишем dll
3. В память вставляем прыжок на dll
Вариант два
Такой метод используется в enbsiries(графическая настройка к играм)
Подменяем dll на свою...
Первый вариант не так.
1. Открываем процесс.
2. В память пишем код, который вызовет LoadLibrary (в качестве аргумента будет путь к нашей библиотеке) и завершит поток.
3. Создаем новый поток в целевом процессе (CreateRemoteThread), в качестве точки старта указываем адрес, где расположен вышеупомянутый код, ждем завершения потока, закрываем хендл.
Я частично понял суть хуков прочитав вот это:
u.to/wu6_Bg
u.to/5PC_Bg
u.to/CfG_Bg
Все равно нифига не понятно. Можите привести краткий план сплайсинга
Т.е.:
1. Узнать адрес оригинальной функции
2. Скопировать оригинальные аргументы функции
3. Поставить переход на нашу функцию
4. Выполнить необходимые действия
(5. Исполнить функцию с оригинальными аргументами)
6. ...
Так или не так?
Вроде того
Kaimi, а почему инжект не через создание удаленного потока через CreateRemoteThread по адресу LoadLibraryA ( полученному из GetProcAddress ), это же проще?
STARTUPINFO *si = reinterpret_cast(VirtualAlloc(0, sizeof(si), MEM_COMMIT, PAGE_READWRITE));
PROCESS_INFORMATION *pi = reinterpret_cast(VirtualAlloc(0, sizeof(pi), MEM_COMMIT, PAGE_READWRITE));
si->cb = sizeof(si);
CreateProcess(0, SystemPath, 0, 0, 0, CREATE_SUSPENDED | CREATE_NO_WINDOW, 0, 0, si, pi);
HANDLE RemoteProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pi->dwProcessId);
LPVOID LoadLibAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
LPVOID MemAlloc = VirtualAllocEx(RemoteProc, 0, strlen(DLL_NAME) + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(RemoteProc, (LPVOID)MemAlloc, DLL_NAME, strlen(DLL_NAME) + 1, NULL);
CreateRemoteThread(RemoteProc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddress, (LPVOID)MemAlloc, NULL, NULL);
CloseHandle(RemoteProc);
В посте используется CreateRemoteThread и LoadLibrary, в чем вопрос?
Скажите мне как ето все использовать, точнее как сделать чтоб можно было использовать программу для внедрения dll в другую программу, и как сделать dll)
Извините за нубские запроси!
а где взять windows.asm из исходника асм...в стандартном пакете нету у масм32
В архиве отсюда должен быть https://kaimi.io/?p=464