Рыцарь
Регистрация: 06.08.2007
Сообщений: 223
Сказал Спасибо: 6
Имеет 67 спасибок в 34 сообщенях
|
Delphi Код:
library Pi;
uses
System.SysUtils,
Messages,//для SendMessage, PostMessage
Windows,
System.Classes,
TlHelp32;//для работы CreateToolhelp32Snapshot-сделать снимок процессов, узнать PID
type TMyRec = Record
Age :Cardinal;
Name :String[20];
Fam :String[20];
End;{интересный факт, что такая структура (string[20] , string[20] , Cardinal) взвешивается SizeOF(MyRec) в 48 байт}
type //хлам
TCopyDataStruct = packed record
dwData: DWORD; // до 32 бит, которые нужно передать
// приложению-получателю
cbData: DWORD; // размер, в байтах данных, указателя lpData
lpData: Pointer; // Указатель на данные, которые нужно передать
// приложению-получателю. Может быть NIL.
end;
var
LogFile: TextFile;//файл который будет сохраняться на диск
pFunc1: pointer;//адрес функции Func1
pFunc2: pointer;//адрес функции Func2
g_MyRec: TMyRec;//структура с данными которую попробуем отправить в САР
// g_CopyDataStruct: TCopyDataStruct;
g_String: string[40];
g_PipeRead: THandle;
g_PipeWrite: THandle;
g_tempCardinal:cardinal;
g_s: string[40];
g_car: cardinal;
{$R *.res}
//это ваши функции
//их нужно объявить после $R *.res
{Обратите внимание: все функции нашей DLL используют соглашение stdcall, которое
обеспечивает совместимость новых функций с функциями API Windows 32. Мы могли бы
не указывать это соглашение; в этом случае компилятор использовал бы более эффективное
соглашение register, но обращение к нашей DLL из программ, написанных на других
языках программирования, в общем случае стало бы невозможным.
Если вы создали DLL для "внешнего" исользования (внеDelphi), объявляйте подпрограммы
с директивой stdcall или safecall!}
function Func1(a:integer):integer;stdcall;
begin
Result:=a+1;
end;
function Func2(a: Cardinal):Cardinal;stdcall;
begin
//пишем лог файл.
AssignFile (LogFile, 'C:\log\Func2.txt');//инициализация
ReWrite (LogFile);//если файла нет то создаст его. если есть то перезапишет
WriteLn (LogFile, 'Log Func2.dll'); //содержимое
CloseFile (LogFile);//закрыть работу с файлом
Result := 0;
end;
{//здесь вы можете указать, какие из ваших функций будут видны для внешнего вызова из DLL
Раздел Exports помогает компилятору и компоновщику создать специальный заголовок DLL-модуля,
в котором перечисляются имена подпрограмм и адреса их точек входа.
перечисляемые в Exports подпрограммы должны быть описаны где-то выше по тексту библиотеки.
Для использования подпрограмм из DLL необходимо описать их как внешние, добавив за словом External
имя библиотеки в апострофах: procedure MyProc; External 'MyDLL';
функцию можно будет вызвать по имени или по индексу.
Если нужно сослаться на индекс функции, за именем библиотеки указывается
слово index и индекс: procedure MyProc; External 'MyDLL' index 2;}
Exports
{plus index 1 name 'plus' resident;
экспортировать функцию plus, присвоить ей индекс 1, и внешнее имя plus. resident- это тайна
а мы експортируем по умолчанию просто указав имя функции}
Func1,
Func2;
begin
//здесь пишется код, который выполняется при инициализации DLL
{полагаю поток повиснет на выводе этого окошка до тех пор пока ОК не нажать}
//MessageBox(0, 'DLL LOADED', 'Pi', 0);
{при загрузке ДЛЛ в чужой процесс- выполнится автоматически эта основная процедура.
узнаю сразу адреса всех своих функций внутри длл в чужом процессе и отошлю их
в CAP(control aplication). адреса можно передать с помощью SendMessage.
его достаточно чтобы передать 4+4 байта информации(WParam и LParam) (или там по 8 байт поля хз) плюс еще
сам заголовок(название) сообщения- несет в себе полезную информацию о том что именно мы передаем в WParam и LParam.
однако большой обьем информации так передать неполучится. Передавать любые данные можно
записывая их в буфер в CAP а с помощью SendMessage передавать адрес буфера. тогда можно передать любой
объем информации. но это требует отладочных привелегий для жертвы. чего делать не хочу.
Именно так мы писали путь к ДЛЛ в память чужого процесса а потом использовали эти данные для вызова ДЛЛ.
но мы это делали имея отладочные привы у приложения САР (писателя данных в чужой процесс).
вначале CAP должна узнать адреса всех нужных функций (областей памяти с данными возможно).
DLL должна уведомить CAP об адресах всех своих функций.
CAP должна запомнить где находятся адреса нужных функций внутри ДЛЛ инжектированой.
SendMessage может быть полезной для какойто синхронизации работы. для асинхронной работы PostMessage.
так как для тестов я использую всплывающее сообщение MessageBox(0, 'DLL LOADED', 'Pi', 0); то
оно останавливает поток который выполняет эту главную процедуру ДЛЛ а это тот жэ самый поток что
и инжектирует ДЛЛ. Для тестов я использовал PostMessage(FindWindow(nil , 'Form1'), WM_USER+1, 123, 456);
принимающая сторона должна прописать обработчик сообщений в классе формы
type
TForm1 = class(TForm)
...
private
(Private declarations)
Procedure MesGet(var Msg:TMessage); message MyMes1;
а потом описать его тело
Procedure TForm1.MesGet(var Msg:TMessage);
begin
end;
также следует прописать константы перед type
const
MyMes1=WM_USER+1; }
{попробуем отправить что-нибудь в CAP. Отправлять будем адреса функций. Так как в данный момент
выполняется основная процедура ДЛЛ это значит что ДЛЛ уже разместилась в памяти чужого процесса.
мы узнаем адреса интересующих нас функций и отправляем эти адреса в САР. таким образом САР узнает
по каким адресам расселись разные функции инжектированной ДЛЛ, и сможет их вызвать удаленным потоком.
конечно для того чтобы была возможность вызвать функцию удаленным потоком из САР нужно чтоб функция имела
совместимый прототип. function Func2(a: Cardinal):Cardinal;stdcall; такой. 4байта аргумент. 4 байта результат.
аргументом и результатом обычно выступают указатели. можно использовать PVOID или (возможно)Pointer.}
pFunc1 := GetProcAddress(GetModuleHandle('pi.dll'), PAnsiChar('Func1'));
pFunc2 := GetProcAddress(GetModuleHandle('pi.dll'), PAnsiChar('Func2'));
SendMessage(FindWindow(nil , 'Form1'), WM_USER+1, cardinal(pFunc1), 0);
PostMessage(FindWindow(nil , 'Form1'), WM_USER+2, cardinal(pFunc2), 0);
// PostMessage(FindWindow(nil , 'Form1'), WM_USER+3, 123, 456);
{теперь усложним задачу. нужно переслать в САР данные произвольной длинны, аля массив байтов=буфер.}
//подготовим структуру для отправки.
g_MyRec.Name:='Lex';
g_MyRec.Fam:='Lexov';
g_MyRec.Age:=23;
g_String:='123456';
CreatePipe(g_PipeRead , g_PipeWrite, NIL , 40);
g_tempCardinal:=0;
WriteFile(g_PipeWrite , g_string , length(g_string)+1 , g_tempCardinal , NIL);
{чтобы работать с безымянным пайпом из другого процесса(САР) нужно создать дубликат, копию хендлов
с помощью DuplicateHandle апи. это можно сделать тут в длл и выслать в САР дубликат хендла
специально настроенного для работы в процессе САР либо можно это сделать внутри САР процесса а отослать
оригинал. безразницы. мы отошлем оригинал хэндла в САР, и в САР продублируем его.
второй вариант работать с именнованным пайпом. тогда обращиться к пайпу можно будет по его имени,
как к файлу, без хэндлов. типа //./MyPipeName.pip хотя насчет расширения я не уверен нужно ли оно и не заглючит ли с ним.
настроить (создать) именнованный пайп на мой взгляд сложновато, параметров много непонятных. поэтому я выбрал
безымянный пайп.}
SendMessage(FindWindow(nil , 'Form1'), WM_USER+3, cardinal(g_PipeRead), length(g_string));
{в процессе САР данные из пайпа будут прочитаны. прочитанные данные из пайпа удаляются.
это отличие пайпа от файла. в файл можно записать данные и читать их потом много раз передвигая курсор указатель.
а в пайпе данные типа одноразовые прочел- и они стерлись. поэтому и называется труба(PIPE) .
типа одна и таже вода на выходе из трубы не может быть выпита дважды. пожтому никакие апи по установке
позиции курсора чтения/записи в пайпе не работают. я в этом убедился. однако пайп можно читать
частями по несколько байт, в несколько операций. то что прочли то и удалится остальное прочесть такимже
способом можно указав сколько байт читать.}
{тут я хочу опять прочесть чтото из пайпа теперь на стороне ДЛЛ. если пайп пустой (САР оттуда все прочел)
а мы попытаемся его повторно тут прочесть то это приведет к подвисанию потока. Подвиснет поток не критично.
поток будет ждать когда в пайп кто-то, откуда-то запишет какие-то данные и их можно будет прочесть.
тогда апи чтения прочтет пайп и поток продолжит свою работу. мыже предварительно запишем новую порцию информации
в пайп и прочтем его уже на стороне ДЛЛ и выведем отчет об этом в логфайл}
g_string := 'qwerty';
WriteFile(g_PipeWrite , g_string , length(g_string)+1 , g_tempCardinal , NIL);
ReadFile(g_PipeRead , g_s , length(g_string)+1 , g_car, nil);
//g_car := FileRead(g_PipeRead , g_s , length(g_string)+1);//и так тоже можно
closeHandle(g_PipeRead);
closeHandle(g_PipeWrite);
//пишем лог файл.
AssignFile (LogFile, 'C:\log\PiLog.txt');//инициализация
ReWrite (LogFile);//если файла нет то создаст его. если есть то перезапишет
writeln (LogFile, 'Log Pi.dll'); //пишем строку в файл
writeln (LogFile, 'Func1=' + inttostr(cardinal(pFunc1)));
writeln (LogFile, 'Func2=' + inttostr(cardinal(pFunc2)));
writeln (LogFile, 'длинна строки length(g_string)=' + inttostr(length(g_string)));
writeln (LogFile, 'записали байт в пайп=' + inttostr(g_tempCardinal));
writeln (LogFile, 'прочли байт из пайпа=' + inttostr(g_car));
writeln (LogFile, 'прочли строку из пайпа=' + g_s);
CloseFile (LogFile);//закрыть работу с файлом
end.
Добавлено через 44 секунды
впринципе и все. что я смог сделать.
Последний раз редактировалось VORON, 29.01.2025 в 15:18.
Причина: Добавлено сообщение
|