Print This Post Делаем собственный инжектор

Суббота, 14. Август 2010
Раздел: Assembler, C/C++, Windows, автор:

Давным-давно, во времена мифов и легенд, когда древние Боги были мстительны и жестоки и обрушивали на программистов все новые и новые проклятия... На хабре была опубликована статья про сплайсинг.

В качестве примера в статье был приведен довольно масштабный код, который воспринимался не особо легко. Захотелось разобраться в процессе инжекта, а также написать более простой и менее громоздкий код.
Вкратце, инжектинг - это подгрузка нашей библиотеки в сторонний процесс, а сплайсинг - перехват какой-либо функции (мы перехватывали 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 начиналась так:

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 байт - это уже их тело. Их перехват можно осуществить, например, изменив адреса в таблице импортов, если они там есть, но этот материал в данной статье не затронут. Что ж, теперь к коду. Я решил написать несколько макросов, чтобы удобно ставить и убирать хуки, а также вызывать оригинал функции.

.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 и поставить в нем перехваты на желаемые функции (разумеется, не убирая перехват, как в примере). После чего просто нужно написать тело нашего перехватчика, который и будет подменять параметры или возвращаемое значение переваченных функций, реализуя наши коварные цели.

Теперь рассмотрим код самого инжектора:

#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 процессы есть свои заморочки, которые остаются за рамками данной статьи.

С тех самых времен прошло много столетий... Боги были усмирены человеком, но теперь мы имеем новые проблемы, которые порой страшнее гнева всевышних - это, конечно, бурление говн недовольных разработчиков, считавших самих себя Богами программирования под всякие социальные ресурсы нынешнего времени, но свергнутых также, как когда-то и настоящие Боги.

В общем, делайте инжекты, ломайте чужой софт. Этот мир интересней, чем вам кажется. (с)

Также рекомендую почитать

 Обсудить на форуме


Получать обновления на почту:     

Метки: , , , , .

Комментариев: 10 к “Делаем собственный инжектор”


  1. ММ :

    нифига не понял с первого раза) надо будет поучиться) эт значит что ты не будешь больше делать инжекты?

    [Ответить]

    dx:

    Будет. Мы просто поделились опытом, знаниями и исходниками, мало ли кто еще захочет попробовать.

    [Ответить]

  2. какие виды прог (на каких языках) можно ломать? как определить подходящую прогу ?=)

    [Ответить]

    M_script:

    Любые проги. WinXP, например, можешь взломать - для этого в начале кода инжектора допиши #include

    [Ответить]

    M_script:

    #include <haknut_vindu.h>

    [Ответить]


  3. salamandra :

    Всё это конечьно хорошо,но лучше автоматезировать процесс.Для того что б не париться с прологами можно прикрутить дизассемблер длин и буфер под сохраняемые байты выделять по ходу пьессы,после чего ставить джамп на остаток функции.Так же проверять на уже установленные перехваты и в случае чего мапить либу с диска и оттуда брать оригинальные байты.Либо ставить и снимать перехват,снова ставить и опять снимать.

    [Ответить]

    dx:

    Полностью согласен, так можно было бы перехватывать в принципе любые функции длиннее пяти байт (ну или чуть больше пяти, если инструкции первые в 5 не укладываются). Если будет время, попробую усовершенствовать макросы, чтобы можно было хукать любые функции.

    [Ответить]


  4. salamandra :

    Вот моя "универсальная реализация"

    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

    [Ответить]


  5. worms :

    и че с этим всем делать? куда засунуть и как сохранить?

    [Ответить]


  6. pilulkin :

    А у меня работает только если использовать VirtualProtectEx (на Windows7). В остальном все прекрасно. Огромное спасибо за отличный сайт.

    [Ответить]


Оставьте ваш комментарий