Сейчас на форуме: hgdagon (+5 невидимых)

 eXeL@B —› Программирование —› Как правильно вызвать из чужой проги функцию из своей ДЛЛ
. 1 . 2 . >>
Посл.ответ Сообщение

Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 31 июля 2007 18:53 · Поправил: rmzvoid
· Личное сообщение · #1

Начал осваивать увеличение функциональности программы, надо вобщем пропатчить несколько переменных и вставить пару хуков в прогу. Начал с простого, написал простую прогу:

#include <stdio.h>

int i = 1;
int j = 2;
int k = 3;

int main()
{
k = i + j;
printf("%i",k);
return 0;
}


эту прогу и буду патчить

затем написал ДЛЛ:

// DLL.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include <stdio.h>

#define DllExport __declspec( dllexport )

void Hello();

DllExport BOOL WINAPI DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
{
DWORD dwProcessId = GetCurrentProcessId();
HANDLE hMainInst = OpenProcess(PROCESS_ALL_ACCESS,NULL,dwProcessId);
DWORD oldProtect = NULL;


if (!hMainInst)
{
MessageBoxA(NULL,"OpenProcess failed. Error Code: " + GetLastError(),"DLL",MB_OK);
}
else
{
if (VirtualProtect((LPVOID)(0x00401018), 6, PAGE_EXECUTE_READWRITE, &oldProtect)!=0)
{
memcpy((BYTE *)(0x0040101A), Hello, sizeof(DWORD));
// MessageBoxA(NULL,"Changed variable","DLL",MB_OK);
}
else
MessageBoxA(NULL,"Failed to change variable","DLL",MB_OK);
}

}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}

return TRUE;
}


void Hello()
{
printf("Hello from DLL\n");
}


прога падает.

Кусок Проги дизасеблерованый:

.text:00401000 _main proc near ; CODE XREF: start+13Ep
.text:00401000 mov eax, dword_403010
.text:00401005 mov ecx, dword_403014
.text:0040100B add eax, ecx
.text:0040100D push eax
.text:0040100E push offset unk_40207C
.text:00401013 mov dword_403018, eax
.text:00401018 call ds:printf
.text:0040101E add esp, 8
.text:00401021 xor eax, eax
.text:00401023 retn
.text:00401023 _main endp


после пропатчивания кода моей ДЛЛ-кой:

00401000 mov eax,dword ptr ds:[00403010h]
00401005 mov ecx,dword ptr ds:[00403014h]
0040100B add eax,ecx
0040100D push eax
0040100E push 40207Ch
00401013 mov dword ptr ds:[00403018h],eax
00401018 call dword ptr ds:[00812C68h]
0040101E add esp,8
00401021 xor eax,eax
00401023 ret


до пропатчивания ИДА показывает что принтф находица тут:
.rdata:00402050 printf dd ? ; DATA XREF: _main+18r

откуда береца адрес 00812C68h и почему программа крешится, судя по дебагеру адрес 00812C68h указывает кудато далеко и совсем не в ту степь

Дизасемблируя ДЛЛ посмотрел что адрес Hello() = 0x10001000

Пропатчивание переменных получается на ура, а вот call не могу




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 31 июля 2007 19:21
· Личное сообщение · #2

проверь CRT linking. Скорее всего в проге он dynamic а в dll static (или наоборот).



Ранг: 29.0 (посетитель)
Активность: 0.010
Статус: Участник

Создано: 31 июля 2007 19:27
· Личное сообщение · #3

какой адрес пишется в 0x0040101A, относительный или прямой?




Ранг: 120.9 (ветеран), 5thx
Активность: 0.080
Статус: Участник
Programmer and reverser

Создано: 31 июля 2007 19:45 · Поправил: Executioner
· Личное сообщение · #4

Ты не то патчишь. Тебе надо патчить не само значение call dword ptr ds:[00812C68h] а данные, на которые оно указывает. То есть надо патчить значение по адресу 00402050h в данном случае.

-----
Уважайте других и пишите грамотно.





Ранг: 120.9 (ветеран), 5thx
Активность: 0.080
Статус: Участник
Programmer and reverser

Создано: 31 июля 2007 19:48 · Поправил: Executioner
· Личное сообщение · #5

На то он и dword ptr - указатель на двойное слово. По этому адресу ты должен записать адрес своей функции Hello.

-----
Уважайте других и пишите грамотно.





Ранг: 120.9 (ветеран), 5thx
Активность: 0.080
Статус: Участник
Programmer and reverser

Создано: 31 июля 2007 20:33
· Личное сообщение · #6

А это твое 00812C68h очень похоже на начало команды
68 2C810010 PUSH 1000812C с учетом того, что ImageBase твоей DLL, видимо, 0x10000000,
ибо функцией
memcpy((BYTE *)(0x0040101A), Hello, sizeof(DWORD));
ты копируешь по адресу 0x0040101A первые четыре байта функции Hello, насколько я понимаю. Хотя плохо знаю Си, могу ошибаться.

-----
Уважайте других и пишите грамотно.





Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 31 июля 2007 21:21 · Поправил: s0larian
· Личное сообщение · #7

Executioner, точняк - ок копирует 4 байта из указателя а надо так:


unsigned int *p = (unsigned int *)0xxxxxx;
*p = (unsigned int *)Hello;


но дело вот в чём - тебе надо пересчитать адрес и вбить его в инструкцию (структура примерно такая: [prefix] opcode operand). Глянь в интеловскую доку. Покажи байты твоего call-a - он может быть относительным и абсолютным. (самое простое, конечно, это mov eax, address; call eax" но для этого никогда нету места если патчишь одну инструкцию. С другой стороны, можешь забить любой набор инструкций в начало функции - твой hook запустится, и ты сможешь потом вызвать оригинал + смещение. (ессно надо вызвать не в середине инструкции и сохранить то что делали первые несколько оригинальных инструкций.




Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 31 июля 2007 21:34
· Личное сообщение · #8

E8 cw CALL rel16 Call near, relative, displacement relative to next instruction.
E8 cd CALL rel32 Call near, relative, displacement relative to next instruction.

FF /2 CALL r/m16 Call near, absolute indirect, address given in r/m16.
FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32.




Ранг: 120.9 (ветеран), 5thx
Активность: 0.080
Статус: Участник
Programmer and reverser

Создано: 31 июля 2007 21:56
· Личное сообщение · #9

s0larian пишет:
ересчитать адрес

Нафига? Ты ж сам выше привел
FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32.
У нас есть вызов вида
call dword ptr ds:[00402050h]
Таким образом, если мы по адресу 00402050h запишем адрес нашей функции Hello, то будет вызываться именно она.

-----
Уважайте других и пишите грамотно.





Ранг: 387.4 (мудрец)
Активность: 0.170
Статус: Участник
системщик

Создано: 01 августа 2007 00:45
· Личное сообщение · #10

Executioner пишет:
Таким образом, если мы по адресу 00402050h запишем адрес нашей функции Hello, то будет вызываться именно она.

Опять ты прав... чего-то я криво читаю сегодня. В данно случае это import, и можно патчить указатель. Я говорил про случая когда надо патчить функцию из проги.




Ранг: 120.9 (ветеран), 5thx
Активность: 0.080
Статус: Участник
Programmer and reverser

Создано: 01 августа 2007 03:18
· Личное сообщение · #11

memcpy((BYTE *)(0x0040101A), (unsigned int *)Hello, sizeof(DWORD));
Вроде так должно работать. Но не ручаюсь. Си не знаю толком.

-----
Уважайте других и пишите грамотно.




Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 03:52
· Личное сообщение · #12

Сделал как писал s0larian

unsigned int *p = (unsigned int *)0x0040101A;
*p = (unsigned int)(unsigned int *)Hello;


теперь код после патча выглядит более как надо (вродебы), т.е. call вызывает по адресу по которому находится Hello 0x10001000:

00401018 FF 15 00 10 00 10 call dword ptr ds:[10001000h]

Но прога крашится всё равно с сообщением:

Unhandled exception at 0x00206c68 in DLLPlug.exe: 0xC0000005: Access violation reading location 0x00206c68.

0x00206c68 это же кусок кода из Hello:

10001000 68 6C 20 00 10 push 1000206Ch

в чем теперь трабла?



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 03:59
· Личное сообщение · #13

s0larian пишет:
проверь CRT linking. Скорее всего в проге он dynamic а в dll static (или наоборот).


а черт его знает где это смотреть



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 04:13
· Личное сообщение · #14

Кстати call ds:printf ссылается не на точку входа в процедуру а на какое-то непонятное (не парю в ассемблере дюже):

.rdata:00402000 ; Section 2. (virtual address 00002000)
.rdata:00402000 ; Virtual size : 0000031A ( 794.)
.rdata:00402000 ; Section size in file : 00000400 ( 1024.)
.rdata:00402000 ; Offset to raw data for section: 00000800
.rdata:00402000 ; Flags 40000040: Data Readable
.rdata:00402000 ; Alignment : 16 bytes ?
.rdata:00402000 ;
.rdata:00402000 ; Imports from KERNEL32
.rdata:00402000 ;
.rdata:00402000 ; ====================================================================== =====
.rdata:00402000
.rdata:00402000 ; Segment type: Pure data
.rdata:00402000 ; Segment permissions: Read
.rdata:00402000 _rdata segment para public 'DATA' use32
.rdata:00402000 assume cs:_rdata
.rdata:00402000 ;org 402000h
.rdata:00402000 ; HMODULE __stdcall GetModuleHandleA(LPCSTR lpModuleName)
.rdata:00402000 GetModuleHandleA dd ? ; DATA XREF: start+Fr
.rdata:00402004 dd 0
.rdata:00402008 ;
.rdata:00402008 ; Imports from MSVCR71
.rdata:00402008 ;
.rdata:00402008 ; void __cdecl exit(int)
.rdata:00402008 _exit dd ? ; DATA XREF: start+180r
.rdata:0040200C _XcptFilter dd ? ; DATA XREF: sub_4011BCr
.rdata:00402010 _cexit dd ? ; DATA XREF: start:loc_40117Br
.rdata:00402014 exit dd ? ; DATA XREF: start+151r
.rdata:00402018 __p___initenv dd ? ; DATA XREF: start+12Ar
.rdata:0040201C _amsg_exit dd ? ; DATA XREF: __amsg_exitr
.rdata:00402020 __getmainargs dd ? ; DATA XREF: start+103r
.rdata:00402024 _initterm dd ? ; DATA XREF: __inittermr
.rdata:00402028 _c_exit dd ? ; DATA XREF: start:loc_4011AAr
.rdata:0040202C _adjust_fdiv dd ? ; DATA XREF: start+9Dr
.rdata:00402030 __p__commode dd ? ; DATA XREF: start+8Fr
.rdata:00402034 __p__fmode dd ? ; DATA XREF: start+81r
.rdata:00402038 __set_app_type dd ? ; DATA XREF: start+6Cr
.rdata:0040203C _except_handler3 dd ? ; DATA XREF: .text:loc_4012F0r
.rdata:00402040 __dllonexit dd ? ; DATA XREF: ___dllonexitr
.rdata:00402044 ; _onexit_t __cdecl onexit(_onexit_t)
.rdata:00402044 _onexit dd ? ; DATA XREF: __onexit+9r
.rdata:00402048 _controlfp dd ? ; DATA XREF: __controlfpr
.rdata:0040204C __setusermatherr dd ? ; DATA XREF: start+C0r
.rdata:00402050 printf dd ? ; DATA XREF: _main+18r <--- сюда ссылается, а что это не знаю, у меня же после патча получается что call ds:0x10001000 ссылается на первую инструкцию функции Hello. Наскоко я думал и наскоко я не знаю ассемблера должны вызываться инструкции по 10001000h . Но чето не то, пойду книшку скачаю по ассемблеру



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:07
· Личное сообщение · #15

rmzvoid пишет:
Сделал как писал s0larian

unsigned int *p = (unsigned int *)0x0040101A;
*p = (unsigned int)(unsigned int *)Hello;


ты сделал не так. запутался с указателями.
замени 0x0040101A на 0x00402050, если я правильно понимаю, что тебе надо.
насчет dynamic и static не скажу, что будет проще. при dynamic - call будет вида FF 15, а при static - E8. если чувствуешь разницу между этими опкодами, должен понимать, в чем проблема.



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:11
· Личное сообщение · #16

ну или еще можно:

// в глобальных переменных:
unsigned int HelloAddr;
...
// патчинг:
HelloAddr = (unsigned int)Hello;
unsigned int *p = (unsigned int *)0x0040101A;
*p = (unsigned int)HelloAddr;




Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:12
· Личное сообщение · #17

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



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 05:25
· Личное сообщение · #18

sotona пишет:
при dynamic - call будет вида FF 15, а при static - E8. если чувствуешь разницу между этими опкодами, должен понимать, в чем проблема.


FF 15 50 20 40 00 у мя, но не понимаю до конца что такое 00402050 ) E8 это вроде что-то типа jmp (E9)?

и к чему там приставка ds: знаю что дата сегмент, но не более )

Не думал что столько сложностей встречу



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:31
· Личное сообщение · #19

rmzvoid пишет:
Не думал что столько сложностей встречу

ну так прокатил или нет один из двух моих вариантов?!
rmzvoid пишет:
но не понимаю до конца что такое 00402050

0x00402050 - это адрес из таблицы импорта, часть которой ты нам привел выше. конкретно в 0x00402050 лежит адрес функции printf, а сама функция определена внутри DLL типа msvcrt*.dll



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:33 · Поправил: sotona
· Личное сообщение · #20

rmzvoid пишет:
E8 это вроде что-то типа jmp (E9)

да, сходства есть. и после E8, и после E9 идёт длина прыжка относительно адреса следующей команды.
а после FF 15 идёт адрес переменной, в которой лежит адрес функции =)
p.s. кстати аналогичное для jmp - FF 25



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 05:37
· Личное сообщение · #21

sotona пишет:
замени 0x0040101A на 0x00402050, если я правильно понимаю, что тебе надо.


РАБОТАЕТ!!!!!!! Спосибо )))



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 05:39
· Личное сообщение · #22

sotona пишет:
а после FF 15 идёт адрес переменной, в которой лежит адрес функции =)


ВОТ! Теперь это многое объясняет, я думал по этому адресу лежит сама функция ))



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:42
· Личное сообщение · #23

rmzvoid пишет:
РАБОТАЕТ!!!!!!! Спосибо )))

только не забывай, что этот первый вариант - хук конкретного вызова функции.
а второй, если он конечно работает x)) - хук любого вызова.



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 05:43
· Личное сообщение · #24

над чем посоветуете еще попрактиковать? )) чтобы приступить к встраиванию ДЛЛ в чужую прогу?



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 05:44
· Личное сообщение · #25

sotona пишет:
только не забывай, что этот первый вариант - хук конкретного вызова функции.
а второй, если он конечно работает x)) - хук любого вызова.


мне в принципе второй вариант и нужен будет в итоге



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 05:55 · Поправил: sotona
· Личное сообщение · #26

rmzvoid пишет:
над чем посоветуете еще попрактиковать? )) чтобы приступить к встраиванию ДЛЛ в чужую прогу?

не знаю...
еще замечание - ты вначале использовал memcpy. может статься, что тебе нужно будет патчить не просто какие-то небольшие переменные, а достаточно большие куски кода или строки. поэтому неплохо бы, когда патчишь код(да и данные тоже), притормозить thread, в котором выполняется код, а после патча его вновь продолжить. за это отвечают SuspendThread, ResumeThread. список тредов можно получить по-разному, хотя бы средствами toolhelp api - Thread32First,Thread32Next.
а еще ты работаешь с фиксированными адресами - константами типа 0x00402050. чтобы добиться некой универсальности, нужно освоить поиск кода по сигнатурам(типа "FF 15 ?? ?? ?? ??"), и уметь находить адрес нужной ячейки в таблице импорта, чтоб потом подменить её значение.
вобщем, можно много чего придумать прикольного... в зависимости от того, какие задачи будут вставать



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 06:01 · Поправил: rmzvoid
· Личное сообщение · #27

вспомнил - мне надо будет посреди кода чужой программы вызывать кусок своего кода из ДЛЛ, как это можно реализовать? т.е. как-то надо будет добавить пару команд между уже существующими



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 06:32 · Поправил: sotona
· Личное сообщение · #28

rmzvoid пишет:
вспомнил - мне надо будет посреди кода чужой программы вызывать кусок своего кода из ДЛЛ, как это можно реализовать? т.е. как-то надо будет добавить пару команд между уже существующими

1) вставить jmp на свой код в нужное место. можешь E9 использовать, но тогда придется считать длину прыжка. лучший вариант: 68 XX XX XX XX C3, где вместо иксов адрес твоего кода. единственный недостаток - байтов приходится вставлять на один байт больше по сравнению с E9.
2) вставка перехода затрет одну-две команды, поэтому надо бы их где-то сохранить сначала, а потом выполнить.
3) ну и потом возврат из твоего кода. тоже джампом или еще как-нибудь.
4)важный момент - знаешь ли ты, между какими именно командами вставляешь код. потому что если неизвестна длина команды, которую затираешь, то придется использовать дизассемблер длин инструкций. готовые движки есть.

еще можно как-нибудь поставить 0xCC(int3) заместо команды, а потом в VEH/SEH обработать исключение, выполняя свой код в обработчике. но лично мне этот вариант кажется сложнее и тормознее в выполнении. да и не совсем удовлетворяет требованиям.

p.s. и еще, я поинтересуюсь - как заставляешь чужую прогу загрузить твою DLL ?



Ранг: 51.0 (постоянный)
Активность: 0.030
Статус: Участник

Создано: 01 августа 2007 06:36
· Личное сообщение · #29

=) в принципе, все описанные здесь фишки можно вынести в отдельный C-файл, который использовать в DLL-ках.



Ранг: 3.0 (гость)
Активность: 0=0
Статус: Участник

Создано: 01 августа 2007 07:03 · Поправил: rmzvoid
· Личное сообщение · #30

sotona пишет:
p.s. и еще, я поинтересуюсь - как заставляешь чужую прогу загрузить твою DLL ?


CFF Explorer позволяет изменять импорты

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


. 1 . 2 . >>
 eXeL@B —› Программирование —› Как правильно вызвать из чужой проги функцию из своей ДЛЛ
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати