Как написать инжектор dll

Внедрение своего кода в адресное пространство процессов

Время на прочтение
5 мин

Количество просмотров 39K

Intro

Внедрение своего кода( динамически ) в чужие процессы — штука достаточно интересная. Это может служить как во благо, так и во зло. Хотя, понятие «зло», местами, весьма абстрактно в информационном мире, я не могу провести точную границу между тем, что «плохо», а что «хорошо», тем более, если это касается внедрения кода…

В данной статье мы займемся созданием своего DLL инжектора. Что это такое, думаю, знают все. Такой способ внедрения стороннего кода достаточно популярен и удобен.

Писать DLL Injector мы будем на C++ в среде Microsoft Visual Studio 2010. Для создания динамически подключаемой библиотеки можно использовать любой инструмент, который вам по душе. Я же для создания библиотеки выбрал CodeGear RAD Studio 2009, язык Delphi( Object Pascal ).

Как же работает DLL Injection ?

Схема работы данного метода проста:

1) поиск и получение дескриптора нужного процесса
2) выделение памяти в процессе и последующая запись пути в DLL`ке по адресу, где произошло выделение памяти
3) создание нового потока в виртуальном пространстве процесса, дескриптор которого был получен.

Начнем с создания DLL.

Как я уже говорил, для этой цели будет использоваться язык Delphi:

Теперь добавим в DLL некоторые ресурсы, а именно — форму и кнопки для управления:

Сама DLL состоит лишь из нескольких строчек кода, которые будут отображать созданную форму. Также можно удалить все комментарии для более удобного визуального восприятия кода:

Теперь программируем интерфейс формы DLL. На форме есть две кнопки. Первая будет «убивать» родительский процесс( т.е процесс, в виртуальном пространстве которого был создан поток, в котором, в свою очередь, выполняется код DLL ). Вторая — рисовать 10 квадратов размером 25x25px в контексте всех окон приложения, принадлежащих процессу:

procedure TForm1.Button1Click(Sender: TObject);
var _curr_process:DWORD;
    p_handle: THANDLE;
begin
// Убиваем родительский процесс
_curr_process:=GetCurrentProcessId();
p_handle:=OpenProcess(PROCESS_ALL_ACCESS,false,_curr_process);
TerminateProcess(p_handle,4);
end;

var dw:DWORD;  // PID текущего процесса

procedure drawGroup(h:HWND);
var canvas:TBitMap;
rect:TRect;
x,y:integer;
i: Integer;
begin
// рисуем 10 квадратов
for i := 1 to 10 do
begin
canvas:=TBitMap.Create();
canvas.Canvas.Handle:=GetDC(h);  // устанавливаем дескриптор контекста
randomize;
canvas.Canvas.Brush.Color:=rgb(random(255),random(255),random(255));
GetWindowRect(h,rect);
x:=random(rect.Right-rect.Left-25);
y:=random(rect.Bottom-rect.Top-25);
canvas.Canvas.Rectangle(x,y,x+25,y+25);
end;
end;

function func(h:HWND):BOOL;  stdcall;
var pid:DWORD;
begin
GetWindowThreadProcessId(h,pid);
// запускаем функцию рисования в окне, если оно принадлежит нужному процессу:
if(pid=dw) then drawGroup(h);
result:=true;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
// Запускаем функцию рисования квадратов
// ( в перечислителе окон системы )
dw:=GetCurrentProcessId();
EnumWindows(@func,0);    // перечисляем окна
end;

Исходный код интерфейсной части можно посмотреть здесь .

Тут все достаточно просто.
Итак, динамическая библиотека написана. Теперь компилируем ее и на выходе получаем скомпилированный файл с расширением «.dll», который можно переименовать для удобства.

Я переименую библиотеку в «inj.dll».

Создание DLL завершено.
Осталось лишь скопировать нашу DLL`ку в системную директорию Windows, чтобы любое приложение могло отыскать её лишь по одному имени.

Переходим к разработке инжектора. Идем в Visual Studio и создаем Пустой проект( File->New->Project->Visual C++->General->Empty Project). Вся разработка будет производиться на «чистом» WinApi.

Первым делом создадим простейший визуальный интерфейс: форма, кнопка и текстовое поле. Выглядеть это будет примерно так:

Как видно, это приложение, обладающее минимальным дизайном и простейшим интерфейсом. В нем присутствуют два текстовых поля, предназначенных для ввода имени процесса( или его определенной части ), в который требуется произвести инжектирование DLL и для ввода имени самой DLL. Кнопка, соответственно, запускает процесс инжекта.

Для инжекта DLL в адресное пространство процессов понадобятся следующие WinApi функции:

Для поиска нужного процесса:
CreateToolHelp32SnapShot
Process32First
Process32Next

Для инжекта:

OpenProcess
GetProcAddress
VirtualAllocEx
WriteProcessMemory
CreateRemoteThread

Итак, обдумаем, как же, собственно организовать «умную» архитектуру приложения, которая была бы понятной и простой.
Я предлагаю начать с обработки щелка по кнопке :)

MSG msg;
    while (GetMessage(&msg,NULL,0,0))
	{

		// Button Click
		if(msg.hwnd==button && msg.message==WM_LBUTTONUP) 
		{
			// Getting Process Name
			GetWindowText(edit_proc,buff,sizeof(buff));
			strncpy(_p_name,buff,BUFF);
			// Getting DLL Name
			ZeroMemory(buff,sizeof(buff));
			GetWindowText(edit_dll,buff,BUFF);
			strncpy(_dll_name,buff,BUFF);
			// Start Injection
			StartInjection();
		}

Ок, далее, переходим к реализации функции

StartInjection();

, которая служит для поиска процесса по его имени:

int StartInjection()
{
	// Searching of Process
	PROCESSENTRY32 pe;
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	if(snapshot==INVALID_HANDLE_VALUE) 
	{
		ShowMessage("SnapShot Failed.");
		return 0;
	}
	pe.dwSize = sizeof(PROCESSENTRY32);
	int curr = Process32First(snapshot,&pe);
	while(curr)
	{		
		CharLowerBuff(pe.szExeFile,lstrlen(pe.szExeFile));
		if(strstr(pe.szExeFile,_p_name)) {pid = pe.th32ProcessID; break;}
	    curr = Process32Next(snapshot,&pe);
	}
	if(pid==NULL) 
	{
		ShowMessage("Searching of process failed.");
		return 0;
	}

	bool result = Inject();
	if(result==false) 
	{
		ShowMessage("Injection failed.");
		return 0;
	}

}

И, наконец, завершающая процесс инжектирования функция

Inject()

:

// Injection
bool Inject()
{
	if(pid==NULL) return false;

	// Получение дескриптора процесса
	HANDLE process = OpenProcess(PROCESS_ALL_ACCESS,false,pid);
	if(process==NULL) return false;

	// "Вытягивание" функции из системной библиотеки для динамической  
	// подгрузки DLL в адресное пространство открытого процесса
	LPVOID fp = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA");
	if(fp==NULL) return false;

	// Выделение участка памяти размером strlen(_dll_name) для последующей 
	// записи имени библеотеки в память процесса.
	LPVOID alloc = (LPVOID)VirtualAllocEx(process,0,strlen(_dll_name), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
	if(alloc==NULL) return false;

	// Запись имени инжектируемой DLL в память
	BOOL w = WriteProcessMemory(process,(LPVOID)alloc,_dll_name,strlen(_dll_name),0);
	if(w==NULL) return false;

	// Создание "удаленного" потока в адресном пространстве
	// открытого процесса и последующая подгрузка нашей DLL.
	HANDLE thread = CreateRemoteThread(process,0,0,(LPTHREAD_START_ROUTINE)fp,(LPVOID)alloc,0,0);
	if(thread==NULL) return false;

	CloseHandle(process);
	return true;

}

Полный код инжектора можно посмотреть здесь.

Тестируем работоспособность инжектора:

Сначала инжектимся «сами в себя». При клике на кнопку «Draw» происходит рисование 10 квадратов. При клике на «Crash it!» происходит немедленное завершение «родительского» процесса. Теперь попробуем инжектиться во что-нибудь посерьезнее, например, в браузер Mozilla Firefox. Для этого необходимо поменять лишь имя процесса в первом текстовом поле и нажать на кнопку:

Как видно, инжект успешно удался. Квадраты рисуются во всех окнах, принадлежащих родительскому процессу браузера. При нажатии на кнопку «Crash it!» Mozilla FireFox немедленно закрывается.

Outro

Для чего нам это вообще нужно? Ведь наша основная цель — это взлом( игр, софта ). Так вот в дальнейшем мы, наверняка, будем использовать этот инжектор для внедрения своего кода в чужое адресное пространство. Благодаря этому можно сэкономить немало времени, сил и нервов :)

Удачи!

EZcheats

Загрузка…

Simple C++/C DLL injector

Простенький инъектор .dll, созданный как альтернатива для GameOwner. Использует метод LoadLibrary.

Как использовать?

  1. Скачать injector.exe
  2. Распаковать в любую папку (желательно на флешку)
  3. Перенести вашу .dll в папку с инъектором
  4. Запустить инъектор и ввести название .dll
  5. Profit

Компиляция кода

Для компиляции кода нужен любой компилятор C++/C (например, GNU GCC Compiler).
Копируете injector.cpp и компилируете, полная копи-паста компатибилити чо.

В случае если Вы будете компилировать на чистом Си, нужно будет добавить в самое начало:

или же

typedef int bool;
#define true 1
#define false 0

Модификация кода

В сурс коде я прописал все как можно подробнее, все должно быть понятно.

Например, чтобы код инъекции работал не только в hl2.exe, а в любом, то можно написать следующий код:

char process_name[64];
scanf("%s",&process_name);
DWORD pID = GetTargetThreadIDFromProcName(process_name);

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

Darwin


  • #1

Как написать Инжектор с нуля на C++​

В данной статье я вам расскажу и покажу как работает инжектор dll и как написать инжектор с нуля с методом LoadLibrary, я вам расскажу о каждом методе и последовательно как и что происходит, для новичков будет особенно полезная статья потому-что в интернете мало где можно найти написание инжектора с нуля на языке C++ давайте начнем

Что такое Инжектор? — это метод используемый для запуска кода в адресном пространстве другого процесса, заставляя его загружать библиотеку динамической компоновки
Соответственно для инжектора нам нужна динамическая библиотека которая будет выполнять код в адресном пространстве который мы освободим и создадим поток!

Давайте начнем писать сам инжектор, а с динамической библиотекой вы уже сами разберетесь или можете найти на форуме

Для начало нам нужно подключить несколько инклудов
#include <Windows.h>
#include <TlHelp32.h>

Нам нужна будет функция с помощью которой мы будем получать PID процесса, при открытий программ он генерируется всегда рандомно
Поэтому нам нужно получить его что бы на основе его открыть ручку процесса и предоставить нам полный доступ к процессу в User Mode

C++:

DWORD GetProcessID(const char* processName)
    {
        if (!processName)
            return 0;

        DWORD id = 0;
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap)
        {
            PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
            if (Process32First(hSnap, &pe32))
            {
                while (Process32Next(hSnap, &pe32))
                {
                    if (strcmp(processName, pe32.szExeFile) == 0)
                    {
                        id = pe32.th32ProcessID;
                        break;
                    }
                }
            }
        }CloseHandle(hSnap);
        return id;
    }

Теперь давайте в нашем main() найдем процесс нужной нам игры куда будем внедрять, откроем дескриптор процесса (получаем полный доступ) и освободим память и создадим наш поток адресного пространства

C++:

int main() {
   
    DWORD gameProcess = 0; // Создаем переменную под наш GetProcessID метод
    const char* DllPath = "anonymcheats.dll"; // Наша DLL которую инжектим, в данном случае он найдет рядом с инжектором (только)
   
    gameProcess = GetProcessID("csgo.exe");
    if (!gameProcess) { // Если наш процесс не был найден
        printf("Process csgo was not found");
    }
   
    // Если процесс найден открываем ручку
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, gameProcess);
    if (hProcess == INVALID_HANDLE_VALUE) {
        // Если нашу ручку не удалось открыть
        printf("Handle Error");
    }
   
    void* allocated = VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Освобождаем память под нашу DLL
            if (!WriteProcessMemory(hProcess, allocated, DllPath, strlen(DllPath) + '', 0)) // Записываем нашу DLL и проверяем что если неудалось записать
            {
                CloseHandle(hProcess);
                return false;
            }
            void* hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)LoadLibraryA, allocated, 0, 0); // Создаем поток адресса которого мы освободили это наша DLL
            if (hThread != 0) // закрываем Handle
                CloseHandle(hThread);
   
            CloseHandle(hProcess); // Закрываем Handle Process
   
            printf("Inject Successfulled"); // Инжект произошел успешно
            return 0; // Возвращаем 0
}

  • #3

Как написать Инжектор с нуля на C++​

В данной статье я вам расскажу и покажу как работает инжектор dll и как написать инжектор с нуля с методом LoadLibrary, я вам расскажу о каждом методе и последовательно как и что происходит, для новичков будет особенно полезная статья потому-что в интернете мало где можно найти написание инжектора с нуля на языке C++ давайте начнем

Что такое Инжектор? — это метод используемый для запуска кода в адресном пространстве другого процесса, заставляя его загружать библиотеку динамической компоновки
Соответственно для инжектора нам нужна динамическая библиотека которая будет выполнять код в адресном пространстве который мы освободим и создадим поток!

Давайте начнем писать сам инжектор, а с динамической библиотекой вы уже сами разберетесь или можете найти на форуме

Для начало нам нужно подключить несколько инклудов
#include <Windows.h>
#include <TlHelp32.h>

Нам нужна будет функция с помощью которой мы будем получать PID процесса, при открытий программ он генерируется всегда рандомно
Поэтому нам нужно получить его что бы на основе его открыть ручку процесса и предоставить нам полный доступ к процессу в User Mode

C++:

DWORD GetProcessID(const char* processName)
    {
        if (!processName)
            return 0;

        DWORD id = 0;
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap)
        {
            PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
            if (Process32First(hSnap, &pe32))
            {
                while (Process32Next(hSnap, &pe32))
                {
                    if (strcmp(processName, pe32.szExeFile) == 0)
                    {
                        id = pe32.th32ProcessID;
                        break;
                    }
                }
            }
        }CloseHandle(hSnap);
        return id;
    }

Теперь давайте в нашем main() найдем процесс нужной нам игры куда будем внедрять, откроем дескриптор процесса (получаем полный доступ) и освободим память и создадим наш поток адресного пространства

C++:

int main() {
  
    DWORD gameProcess = 0; // Создаем переменную под наш GetProcessID метод
    const char* DllPath = "anonymcheats.dll"; // Наша DLL которую инжектим, в данном случае он найдет рядом с инжектором (только)
  
    gameProcess = GetProcessID("csgo.exe");
    if (!gameProcess) { // Если наш процесс не был найден
        printf("Process csgo was not found");
    }
  
    // Если процесс найден открываем ручку
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, gameProcess);
    if (hProcess == INVALID_HANDLE_VALUE) {
        // Если нашу ручку не удалось открыть
        printf("Handle Error");
    }
  
    void* allocated = VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Освобождаем память под нашу DLL
            if (!WriteProcessMemory(hProcess, allocated, DllPath, strlen(DllPath) + '', 0)) // Записываем нашу DLL и проверяем что если неудалось записать
            {
                CloseHandle(hProcess);
                return false;
            }
            void* hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)LoadLibraryA, allocated, 0, 0); // Создаем поток адресса которого мы освободили это наша DLL
            if (hThread != 0) // закрываем Handle
                CloseHandle(hThread);
  
            CloseHandle(hProcess); // Закрываем Handle Process
  
            printf("Inject Successfulled"); // Инжект произошел успешно
            return 0; // Возвращаем 0
}

А теперь как обойти античит:)

  • #5

А как сделать что-бы с сайта дллка скачивалась

  • #6

А как сделать что-бы с сайта дллка скачивалась

C++:

URLDownloadToFile(0, L"ссылка на прямое скачивание", L"Путь, куда она скачается", 0, 0);

  • #7

Как написать Инжектор с нуля на C++​

В данной статье я вам расскажу и покажу как работает инжектор dll и как написать инжектор с нуля с методом LoadLibrary, я вам расскажу о каждом методе и последовательно как и что происходит, для новичков будет особенно полезная статья потому-что в интернете мало где можно найти написание инжектора с нуля на языке C++ давайте начнем

Что такое Инжектор? — это метод используемый для запуска кода в адресном пространстве другого процесса, заставляя его загружать библиотеку динамической компоновки
Соответственно для инжектора нам нужна динамическая библиотека которая будет выполнять код в адресном пространстве который мы освободим и создадим поток!

Давайте начнем писать сам инжектор, а с динамической библиотекой вы уже сами разберетесь или можете найти на форуме

Для начало нам нужно подключить несколько инклудов
#include <Windows.h>
#include <TlHelp32.h>

Нам нужна будет функция с помощью которой мы будем получать PID процесса, при открытий программ он генерируется всегда рандомно
Поэтому нам нужно получить его что бы на основе его открыть ручку процесса и предоставить нам полный доступ к процессу в User Mode

C++:

DWORD GetProcessID(const char* processName)
    {
        if (!processName)
            return 0;

        DWORD id = 0;
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap)
        {
            PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
            if (Process32First(hSnap, &pe32))
            {
                while (Process32Next(hSnap, &pe32))
                {
                    if (strcmp(processName, pe32.szExeFile) == 0)
                    {
                        id = pe32.th32ProcessID;
                        break;
                    }
                }
            }
        }CloseHandle(hSnap);
        return id;
    }

Теперь давайте в нашем main() найдем процесс нужной нам игры куда будем внедрять, откроем дескриптор процесса (получаем полный доступ) и освободим память и создадим наш поток адресного пространства

C++:

int main() {
 
    DWORD gameProcess = 0; // Создаем переменную под наш GetProcessID метод
    const char* DllPath = "anonymcheats.dll"; // Наша DLL которую инжектим, в данном случае он найдет рядом с инжектором (только)
 
    gameProcess = GetProcessID("csgo.exe");
    if (!gameProcess) { // Если наш процесс не был найден
        printf("Process csgo was not found");
    }
 
    // Если процесс найден открываем ручку
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, gameProcess);
    if (hProcess == INVALID_HANDLE_VALUE) {
        // Если нашу ручку не удалось открыть
        printf("Handle Error");
    }
 
    void* allocated = VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Освобождаем память под нашу DLL
            if (!WriteProcessMemory(hProcess, allocated, DllPath, strlen(DllPath) + '', 0)) // Записываем нашу DLL и проверяем что если неудалось записать
            {
                CloseHandle(hProcess);
                return false;
            }
            void* hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)LoadLibraryA, allocated, 0, 0); // Создаем поток адресса которого мы освободили это наша DLL
            if (hThread != 0) // закрываем Handle
                CloseHandle(hThread);
 
            CloseHandle(hProcess); // Закрываем Handle Process
 
            printf("Inject Successfulled"); // Инжект произошел успешно
            return 0; // Возвращаем 0
}

ты забыл про
#include <stdio.h> ?)

  • #8

Как написать Инжектор с нуля на C++​

В данной статье я вам расскажу и покажу как работает инжектор dll и как написать инжектор с нуля с методом LoadLibrary, я вам расскажу о каждом методе и последовательно как и что происходит, для новичков будет особенно полезная статья потому-что в интернете мало где можно найти написание инжектора с нуля на языке C++ давайте начнем

Что такое Инжектор? — это метод используемый для запуска кода в адресном пространстве другого процесса, заставляя его загружать библиотеку динамической компоновки
Соответственно для инжектора нам нужна динамическая библиотека которая будет выполнять код в адресном пространстве который мы освободим и создадим поток!

Давайте начнем писать сам инжектор, а с динамической библиотекой вы уже сами разберетесь или можете найти на форуме

Для начало нам нужно подключить несколько инклудов
#include <Windows.h>
#include <TlHelp32.h>

Нам нужна будет функция с помощью которой мы будем получать PID процесса, при открытий программ он генерируется всегда рандомно
Поэтому нам нужно получить его что бы на основе его открыть ручку процесса и предоставить нам полный доступ к процессу в User Mode

C++:

DWORD GetProcessID(const char* processName)
    {
        if (!processName)
            return 0;

        DWORD id = 0;
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

        if (hSnap)
        {
            PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
            if (Process32First(hSnap, &pe32))
            {
                while (Process32Next(hSnap, &pe32))
                {
                    if (strcmp(processName, pe32.szExeFile) == 0)
                    {
                        id = pe32.th32ProcessID;
                        break;
                    }
                }
            }
        }CloseHandle(hSnap);
        return id;
    }

Теперь давайте в нашем main() найдем процесс нужной нам игры куда будем внедрять, откроем дескриптор процесса (получаем полный доступ) и освободим память и создадим наш поток адресного пространства

C++:

int main() {
  
    DWORD gameProcess = 0; // Создаем переменную под наш GetProcessID метод
    const char* DllPath = "anonymcheats.dll"; // Наша DLL которую инжектим, в данном случае он найдет рядом с инжектором (только)
  
    gameProcess = GetProcessID("csgo.exe");
    if (!gameProcess) { // Если наш процесс не был найден
        printf("Process csgo was not found");
    }
  
    // Если процесс найден открываем ручку
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, gameProcess);
    if (hProcess == INVALID_HANDLE_VALUE) {
        // Если нашу ручку не удалось открыть
        printf("Handle Error");
    }
  
    void* allocated = VirtualAllocEx(hProcess, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Освобождаем память под нашу DLL
            if (!WriteProcessMemory(hProcess, allocated, DllPath, strlen(DllPath) + '', 0)) // Записываем нашу DLL и проверяем что если неудалось записать
            {
                CloseHandle(hProcess);
                return false;
            }
            void* hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)LoadLibraryA, allocated, 0, 0); // Создаем поток адресса которого мы освободили это наша DLL
            if (hThread != 0) // закрываем Handle
                CloseHandle(hThread);
  
            CloseHandle(hProcess); // Закрываем Handle Process
  
            printf("Inject Successfulled"); // Инжект произошел успешно
            return 0; // Возвращаем 0
}

  • #9

C++:

URLDownloadToFile(0, L"ссылка на прямое скачивание", L"Путь, куда она скачается", 0, 0);

Спасибо

  • #12

Будет информация по поиску base для engine.dll и client.dll
И поиску паттернов, таких который hazedumper выкладывает?

  • #16

Когда нибудь может и пригодится, но я уверен, что очень годно)

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 masm32includewindows.inc

include masm32macrosmacros.asm

include masm32macroswindows.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

// получаем хэндл процесса
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcId("gta_sa.exe"));
    // Указываем путь до нашей dll
    const char* DllPath = "C:\test.dll";
    // получаем размер строки нашего пути
    size_t SizePatch = strlen(DllPath) + 1;
    // выделяем память в gta_sa с размером нашей строки
    LPVOID pDllPath = VirtualAllocEx(hProcess, NULL, SizePatch, MEM_COMMIT, PAGE_READWRITE);
    // записываем наш путь в выделенную память
    WriteProcessMemory(hProcess, pDllPath, (LPVOID)DllPath, SizePatch, NULL);
    HMODULE kernel = GetModuleHandleA("Kernel32.dll"); // Получаем адрес kernel32
    DWORD Adr = (DWORD)GetProcAddress(kernel, "LoadLibraryA"); // получаем адрес функции LoadLibraryA
    // Вызываем LoadLibraryA в gta_sa.exe с аргументом "C:\test.dll" и возвращаем адрес нашего потока
    HANDLE hThread = CreateRemoteThread(hProcess, 0, 0,(LPTHREAD_START_ROUTINE)Adr, pDllPath, 0, 0);
    // Ожидаем завершение нашего потока(ждем завершения DllMain с аргументом DLL_PROCESS_ATTACH)
    WaitForSingleObject(hThread, INFINITE);
    // Закрываем дескриптор потока
    CloseHandle(hThread);
    // Освобождаем выделенную память
    VirtualFreeEx(hProcess, pDllPath, SizePatch, MEM_RELEASE);
    // Закрываем дескриптор процесса
    CloseHandle(hProcess);

Понравилась статья? Поделить с друзьями:
  • Как написать инди рок
  • Как написать индекс на пустом конверте
  • Как написать индекс на айфоне
  • Как написать индекс казахстана
  • Как написать индеец правильно