с нуля изучаю новую для себя тему. ДЛЛ инъекция в чужой процесс. и обмен данными из своего приложения с зараженным чужим процессом. ну типа это начало заготовка для пакетхака. первые шаги к большому делу.
я программист так себе. поэтому Крутяги программеры не ругайтесь что я что-то сделал не по ГОСТу. или совсем криво. главное что что-то получилось и кому-то это поможет с чего-то начать.
Сам код в пояснениях не нуждается. там обилие комментариев и так.
Пояснений требует лишь как все настроить и собрать в нечто работоспособное при нажатии на кнопку Play в дельфи.
Програмка внедряет свою длл в процесс жертвы и обменивается с ней данными.
Жертвой выбран стандартный калькулятор вин10 32битный. скачать его не проблема в сети. думаю жертвой может быть и чтото другое.
На диске C следует подготовить пустую папку C:\log\ туда программа скидывает логи порой из длл. когда надо чтото подсматреть что там в голове у длл- мы пишем лог файлы в эту папку.
в настройках проекта ДЛЛ выбрать само собой 32 битность, и настроить путь куда собирать готовую ДЛЛку. ее нужно собирать в папку в которой собирается САР. чтоб руками не переносить каждый раз.
среда разработки RAD STUDIO12 дельфи.
мы запускаем жертву и приложуху САР. жмем кнопку 2- и читаем логи в САР. как происходит инъекция и обмен данными.
далее нажимая кнопку3 мы запускаем одну из наших функций внутри инжектированной длл на стороне жертвы и ее выполнение подтверждается созданием лог файла.
ознакомьтесь на скрине как это работает у меня.
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:26.
unit Unit1;
{в коментариях будет использоваться абревиатура CAP и DLL. это ДЛЛ инжектированная, а программа управления CAP}{то что я понял по указателям.
тип Pointer это число 4 байта которое есть адрес чегото (пусть будет обьекта)
Cardinal тоже может хранить в себе адрес.
MyPointer:=pointer(MyCardinal); - указатель наполнился смыслом. он теперь содерржит адрес чейто.
который до этого хранился в MyCardinal;
после такой операции MyPointer и MyCardinal будут хранить в себе одно и тоже число. которое есть чейто адрес.
а значит условие if cardinal(MyPointer) = MyCardinal then - сработает. также как и
if MyPointer = pointer(MyCardinal) then ..
и получается что вместо MyPointer можно использовать каст pointer(MyCardinal) это будет одно и тожэ. значит для
понимания мы вапще можем не пользоваться типом Pointer а использовать тип Cardinal с кастом pointer(MyCardinal),
и понимать что в числе MyCardinal хранится адрес чего-то.
c Cardinal нам дуступны математические операции +/- отступить или прибавить к начальному адресу 4 байта например,
а с Pointer чета это не пашет.
например в функции в качестве входного аргумента используется указатель на данные и длинна этих данных.
вот пример WriteProcessMemory(THandle, хэндл процесса в который будем писать
Pointer, адрес по которому надо записать данные (куда писать)
Pointer, адрес какие данные надо записать (что писать)
Cardinal, сколько байт писать (сколько писать)
Cardinal); (OUT) сколько байт получилось переписать (результат работы)
а в качестве данных (которые надо записать кудато в память по определенному адресу) у нас структура RECORD
тогда мы подсунем в качестве 3го аргумента @MyRecord - адрес по которому хранится наша структура.
это значит операция @MyCardinal- вернет (или компилятор эту запись подменит на..) адрес по которому
хранится переменная MyCardinal.
Возникает желание: если есть у нас область памяти: адрес и длинну этой области мы знаем, то хорошобы туда
переселить жить структуру такогоже размера как эта область.. и тогда структура поселившись там жить в эту область памяти
сразу в себя вберет все данные (заполнится). типа сделать так: @MyRecord:=pointer(MyMemAdr); круто я придумал?
но хрен там так нельзя. во первых получится утечка по старому адресу прописки структуры. во вторых новый адрес структуры
может быть уже камуто принадлежит , другой структуре. такие фокусы какбы нам недоступны. однако логически так сделать можно
было бы.
тем не менее вот пример tempBOOL := DuplicateHandle(g_DllHandle, OldPipeHandle, CapHandle, @DupPipeHandle, 0, false, DUPLICATE_SAME_ACCESS);
4й параметр выходной, @DupPipeHandle. понимать наверное это стоит так.. это всетаки не выходной параметр а входной.
апи принимает адрес по которому она запишет какието данные. вопщем переселение объектов с адреса на адрес нам не доступно.
}{то что я понял по хэндлам.
часто мы просим систему с помощью апи создать какойто обьект за пределами памяти нашего приложения(в ядре оси)
это может быть например Pipe(пайп, канал анонимный, или недофайл). мы просим систему его создать с помощью апи,
система его создает у себя в ядре гдето, а нам для управления им дает хэндлы этого обьекта. ведь таких обьектов может быть
в системе куча созданных, как их различать? отличаются они номерком (типа порядковый номер обьекта, ИДешник индивидуальный)
итак мы сможем манипулировать Пайпом этим зная его хэндл. говорить системе с помощью апи: сделай такоето действие с папом под
номером таким-то. итак хэндл это номерок обьекта который создан нами за пределами нашего приложения гдето в системе.
вот почему нам нужно следить- раз мы его создали внутри оси то должны потом его и удалить. а то приложение отвалится а
обьект останется. хотя вроде при отвале юзерской прилы все что она насоздавала подчищается и в оси тожэ. но это не точно.
в любом случа даже во ремя работы прилы (без отвалов) стоит понимать где и что мы создали и если лишнее то почистить за собой.
итак Хэндл- это 4 байта число- которое несет в себе смысл- номерок обьекта созданного за пределами нашего приложения
гдето внутри ядра оси. взаимодействовать с этим обьектом мы можем через апи, указывая этот номерок.}interfaceuses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.StdCtrls,
TlHelp32;//для работы CreateToolhelp32Snapshot-сделать снимок всех процессов, узнать PID{номерки WinMessages для которых мы создали тут обработчики. ДЛЛ будет отсылать нам сообщения в САР
а мы их обрабатывать. Задумка такая- наладить взаимный обмен данными ДЛЛ и САР в обе стороны.
в синхронном режиме а также асинхронном режиме. что это значит?
например из из САР в ДЛЛ улетели данные (каким либо способом).
для синхронного режима нужно дождаться в САР чтоб ДЛЛ эти данные получила обработала и дала какойто ответ. тогда САР
получив ответ продолжит свою работу. это синхронно.
для асинхронного режима нужно отослать какието данные и не париться об их дальнейшей судьбе. САР отослала и продолжила
заниматься своими делами. (послал и забыл) это асинхронно.
для направления САР-ДЛЛ мы тут первым делом научимся писать данные в область памяти процесса жертвы. в кнопке №2.
а также вызывать одну из своих функций. которая сможет прочесть записанные ранее САРом данные и дать ответ. вот и синхронность.
там еще есть апи которая ожидает завершения работы вызванного ранее удаленного потока WaitForSingleObject. вопщем синхронность
достигается.
а для направления ДЛЛ-САР так сделать неполучится. потомучто чтоб писать в чужую память, приложуха писатель должна
иметь отладочные привелегии. для САР своего мы это конечно можем настроить. а для чужого процесса это делать нехочется.
мы и так в него код внедряем, еще и прав лишить и всякой прочей самостоятельности-)
для направления ДЛЛ-САР мы поступим подругому. создадим Пайп, запишем в него чтото. и уведомим САР об этом.
отправив сообщение через PostMessage. это асинхронно.
а если нам нужна синхронность то сообщение отправим через SendMessage, тогда поток в ДЛЛ отправителе- подвиснет в ожидании
когда обработчик сообщения в САР завершит свою работу (по вытаскиванию данных из пайпа) и возможно что даст какойто ответ сразу
еще надо понимать что очередь сообщений разгребает окно. наше приложение САР окно имеет и обработает сообщения.
а вот послать сообщение из САР в ДЛЛ нельзя. в длл нет такой возможности разгребать сообщения. это должен быть
постоянно включенный поток в ожидании сообщения. но вопщем то нам это и не нужно для направления САР-ДЛЛ.
повторюсь для САР-ДЛЛ мы можем скидывать данные 2мя способами, просто в область памяти их писать чужому приложению либо
в пайп также. а уведомлять об отправке данных ДЛЛ мы можем вызвав одну из своих функций из ДЛЛки (удаленным потоком)
эту технику мы тут делать научимся все легко. пример вызова своей функции из длл удаленным потоком это когда мы инжектим ДЛЛ.
аналогичным способом можно любую функцию свою вызвать.}const
DLL_MES_1=WM_USER+1;
DLL_MES_2=WM_USER+2;
DLL_MES_3=WM_USER+3;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private{ Private declarations }Procedure GetDllMes1(var Msg:TMessage); message DLL_MES_1;//обработчики сообщений от ДЛЛкиProcedure GetDllMes2(var Msg:TMessage); message DLL_MES_2;
Procedure GetDllMes3(var Msg:TMessage); message DLL_MES_3;
Procedure GetDllMesCD(var Msg:TWMCopyData); message WM_COPYDATA;
public{ Public declarations }end;
type TMyRec = Record//для експериментов.
Age :Cardinal;
Name :String[20];
Fam :String[20];
End;
{это если длл прицеплять к САР, сам к себе цеплять и юзать одну из функций в длл.
это в кнопке №1 показано как сам к себе прицепить длл. я с этого начал. но потом длл усложнилась
а именно в длл добавилось основная процедура с кодом. и поэтому прицепив эту ДЛЛ к сам к себе к САР чтото пойдет не так
но код останется тут. для ознакомления он рабочий просто в ДЛЛ надо вычистить основной блок. и имена функций в порядок привести}
TPlusDLL = function(a: integer): integer; stdcall;
var
Form1: TForm1;
//RemoteThreadResult:Cardinal;
pAdrFunc1: Pointer;//адреса функций из длл инъектированной
pAdrFunc2: Pointer;
g_DllHandle: THandle;//сюда сохраним хэндл чужого процесса в который инжектируем длл. он нам нужен потом для дублирования хэндлов пайпов{для удобства код из кнопки 2 частично вынес в функции. однако кнопку 2 не трогал. кнопка 2 какбы самостоятельна
без функций работает, чтоб весь код в кучке и последовательно перед глазами был в кнопке2}function GetRemotePID(ProcName:string):Cardinal;
function SetDebugPrivilege(boolOnOff:boolean):Boolean;
implementation
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:11.
{$R *.dfm}Procedure TForm1.GetDllMes1(var Msg:TMessage);
begin{запомним в глобальную переменную чтоб потом использовать этот адрес для вызова функции удаленным потоком из ДЛЛ внутри жертвы}
pAdrFunc1:= pointer(Msg.WParam);
form1.memo1.lines.Add('DLL говорит Mes1: адрес Func1= ' + inttostr(cardinal(Msg.WParam)));
end;
Procedure TForm1.GetDllMes2(var Msg:TMessage);
begin
pAdrFunc2:= pointer(Msg.WParam);
form1.memo1.lines.Add('DLL говорит Mes2: адрес Func2= ' + inttostr(cardinal(Msg.WParam)));
end;
Procedure TForm1.GetDllMes3(var Msg:TMessage);
var
tempBOOL: Boolean;
OldPipeHandle: THandle;
DupPipeHandle: THandle;
car:cardinal;
s: string[40];
CapHandle:THandle;
begin
form1.memo1.lines.Add('DLL говорит Mes3. WParam=' + inttostr(Msg.WParam) + ' LParam=' + inttostr(Msg.LParam));
//в WParam мы принимаем Handle для чтения из пайпа. чтобы с ним работать надо сделать дубликат для САР процесса{function DuplicateHandle(
hSourceProcessHandle, // дескриптор процесса источника
hSourceHandle, // исходный дескриптор
hTargetProcessHandle: THandle; // дескриптор процесса приемника
lpTargetHandle: PHandle; // дубликат исходного дескриптора
dwDesiredAccess: DWORD; // флаги доступа к объекту
bInheritHandle: BOOL; // наследование дескриптора
dwOptions: DWORD // дополнительные необязательные флаги
): BOOL; stdcall;
хэндлы (их 2 на чтение и запись) на пайп созданный нами в ДЛЛ действительны только в пределах процесса
который их создал (жертва,длл) мы их сюда переслали сообщением в САР но они тут не работоспособными оказываются.
чтобы вернуть им работоспособность в другом процессе нужно их прогнать через апи DuplicateHandle которая
создаст екземляр хэндла специально настроенного для этого приложения. сам пайп останется прежним а хэндл изменится.}//вытаскиваем из сообщения хэндл на чтение, который надо приспособить для работы в САР. он изменится поэтому назвал Old
OldPipeHandle:=cardinal(Msg.WParam);
{открываем свой этот процесс САР чтобы получить к нему хэндл управления. этот хэндл используется как
аргумент потом для DuplicateHandle}
CapHandle := OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId);
{ну и создаем работоспособную копию хэндла для чтения из пайпа. делаем типа локальную копию хэндла, но не самого пайпа}
tempBOOL := DuplicateHandle(g_DllHandle, OldPipeHandle, CapHandle, @DupPipeHandle, 0, false, DUPLICATE_SAME_ACCESS);
{ранее в кнопке2 мы открыли хэндл чужого процесса(ДЛЛ) и запомнили его в глобальную переменную.g_DllHandle
и специально не закрыли его в конце инъекции. он остался открытым. если мы там его закроем то дублирование не сработает.
закроем хэндл чужого процесса потом как нибудь.. мы про него помним.. верим.. предлагаю читателю самому придумать место где
его закрыть и когда.}{если Дублирование глючит разбираемся что за ошибка, анализируя значение car в гугле применительно к DuplicateHandle}//if tempBOOL = false then car:=getlasterror;//Form1.memo1.lines.Add('err=' + inttostr(car) );//если дублирование сработало то равно =1 (тру)
Form1.memo1.lines.Add('DuplicateHandle=' + inttostr(cardinal(tempBOOL)));
{в LParam мы принимаем длинну данных которая ожидает обработки в пайпе. но никак не используем эти данные.
у кавото появятся идеи потом как юзать второе поле с какойто целью.}//теперь попробуем воспользоваться дубликатом хэндла для чтения из пайпа.SetLength(s , Msg.Lparam);
ReadFile(DupPipeHandle , s , Msg.LParam+1 , car, nil);
{мб это к файлам относится но не к пайпам но мысль такая: При вызове функции чтения/записи функция сразу возвращает
управление со значение FALSE и с кодом ошибки ERROR_IO_PENDING. Объект событие, указанное в поле hEvent
переводится в так называемое «сигнальное» состояние только после завершения операции. Если надо подождать
завершения операции надо использовать функцию WaitForSingleObject.}
Form1.memo1.lines.Add('прочли байт из пайпа =' + inttostr(car));
Form1.memo1.lines.Add('длл нам прислала пакет с данными =' + string(s));
Form1.memo1.lines.Add('length(s) =' + inttostr(length(s)));
{как я понял Pipe (канал) это по сути файл. и писать и читать из него используются апи работы с файлами.
ReadFile WriteFile. но в отличии от настоящего файла пайп по стеку дров до диска не доходит..
а тока до какогото уровня в стеке дров.. там где файл как таковой формируется.. на диск он не уходит..
гдето в ядре виндоса в голове он живет. а доступ к нему по имени может быть как у файла типа "//./name" или по хендлу,
мы работаем с безымянным, анонимным пайпом. у него имени нет а есть только 2 хэндла для записи и чтения, которые задаются
системой при его создании. пусть будет Пайп это виртуальный файл. или недофайл.}{иногда в логах возникает возникает ситуация что сначала mes3 обрабатывается а потом mes2. а порой сначала mes2 а потом mes3
вопщем то норм. mes2 мы посылаем через PostMessage асинхронно. а значит она обработается хз когда непредсказуемо.}
CloseHandle(CapHandle);//убрались за собой
CloseHandle(DupPipeHandle);
CloseHandle(g_DllHandle);
//ну мы помним что в памяти у нас еще 2 открытых хэндла остались висеть. хэндл на пайп и хэндл на жертву.{пользуйтесь ProcessExplorer чтобы следить выгрузился процесс или нет и пришибить его силой.
там видно как ДЛЛ заинектилась к жертве. полезно.}end;
Procedure TForm1.GetDllMesCD(var Msg:TWMCopyData);
begin
form1.memo1.lines.Add('medCD');
form1.memo1.lines.Add('DLL говорит MesCD. WParam=' + Msg.Msg.ToString + ' LParam=' + inttostr(0));
{тут я хотел прием сообщения типа WM_COPYDATA заюзать но нихрена не вышло, с помощью WM_COPYDATA можно
область памяти (структуру) из одного приложения в другое переслать. 2мя параметрами указатель и длинна данных.
я не смог это реализовать и поэтому раскурил пайпы которые мне кажутся очень простыми и понятными в использовании}end;
procedure TForm1.Button1Click(Sender: TObject);
{в кнопке 1 динамически подключается ДЛЛка (сами к себе) (к САР) и вызывается одна из ее функций.
статическое подключение- это когда при запуске все ДЛЛки загружаются сразу с ЕХЕ прогой
а динамическое подключение может быть во время выполнения программы}var
Func1: TPlusDLL;
PiHandle: THandle;//Дескриптор — это уникальный номер, который присваивается каждому объекту, созданному с помощью Win API.//Конкретное значение дескриптора не несёт для программиста никакой полезной информации и может быть использовано//только для того, чтобы при вызове функций из Win API указывать, с каким объектом требуется выполнить операцию.begin//Программа может загружать DLL и без External с помощью трех cтандартных функций : LoadLibrary, GetProcAddress И FreeLibrary.//получаем идентификатор(Хэндл) библиотеки//при загрузки библиотеки выполнится код из основного блока библиотеки. begin..end - в конце ДЛЛки это ее основной блок
PiHandle := LoadLibrary('Pi.dll');
if PiHandle <> 0then memo1.Lines.Add('Pi.dll загружена в память')else memo1.Lines.Add('Pi.dll загрузить не вышло');
//получаем точку входа в подпрограмму (адрес) которую экспортирует библиотека по имени подпрограммы(функции)//можно узнать адрес и по индексу подпрограммы @AddC := GetProcAddress(Handle, PChar(Longint(1))); При вызове по индексу//младшее слово PChar должно содержать индекс, поэтому делаем приведение типов
@Func1:= GetProcAddress(PiHandle, 'Func1');
//используем импортированную подпрограмму из длл, вызываем ее.
memo1.Lines.Add(inttostr(Func1(2)));
//выгружаем библиотеку
FreeLibrary(PiHandle);
end;
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:45.
Причина: Добавлено сообщение
procedure TForm1.Button2Click(Sender: TObject);
{тут ДЛЛ-инъекция}var//список ниже для выяснения PID процесса
ProcName:string;//имя процесса, так как его видит утилита ProcessExplorer
DllNamePAnsiChar:PAnsiChar;//используется точный путь либо известные системе места
tempS:string;
ProcPID:cardinal;//вроде можно и DWORD использовать
hSnapshoot:THandle;{хэндл это ИД. ИД(номер) снимка. все обьекты в системе имеют номер
индивидуальный(ИД или Хндл одно и тоже). занимает 4 байта. можно заменить на Cardinal}
ProcSnapshot:TProcessEntry32;//структура описывает один процесс. сюда записывается описание процесса из снимка для анализа
tempBOOL:boolean;
//список ниже для установки отладочных привелегий
hToken:THandle;//Хэндл токена безопасности.
szPrivilege: PChar;//название уровня безопасности. в системе уже существуют//имена мы будем использовать szPrivilege:='SeDebugPrivilege'
luid: TLargeInteger;
NewState: TTokenPrivileges;
fEnable: Boolean;//включить или отключить режим отладки для нашего(этого) приложения
ReturnLength: DWord;
//прочие
hProc: THandle;//открывая процесс по его PID- получаем его хэндл
pMyMemAdr: pointer;//адрес памяти начиная с которого мы выделили место и чтото туда записали.//это может быть исполняемый код который можно выполнить либо просто данные.
BytesWriten :cardinal;//результат работы функции- сколько байт получилось записать в чужой процесс в выденленную ранее память
pLoadLibAdr: pointer;//адрес с которого начинается функция LoadLibraryA внутри KERNEL32.DLL
hRemoteThread: THandle;//хэндл созданого удаленного потока
pRemoteThread: THandle;//пока не понимаю на что этот указательbegin{задаем имя процесса жертвы, так как его видит система. воспользуйтесь приложухой ProcessExplorer чтобы
с точностью до зоглавных букв выяснить имя это}
ProcName := 'calc1.exe';
{задаем путь к длл которую будем инжектить. вот проводник всегда первую букву заглавную пишет. хотя на самом деле
там строчная. нам надо точное имя. я не уверен что прям регистр тут важен но это может быть так для имени длл}
tempS:=ExtractFilePath(ParamStr(0)) + 'pi.dll';
{апи типа LoadLibraryA работают с анси строками, индекс А об этом и говорит кстатти. есть еще юникодные версии этих апи
с буквой W на конце типа LoadLibraryW}
DllNamePAnsiChar:=PAnsiChar(AnsiString(tempS));
ProcPID := 0;
{Ищем ProcessID (PID) интересующего нас процесса, для этого делается снимок
всех процессов в системе, он перебирается и сравнивается имя процесса с заданным
как тока путем перебора нашли нужный нам процесс (по имени) мы запомним его PID.
можно узнать PID в ручном режиме подглядя его в приложухе ProcessExplorer.
нельзя путать PID с Хэндлом процесса. PID это глобальный идентификатор процесса.
если рассматривать весь процесс как обьект- то PID его номер в системе.
а хэндл на процесс который выдается при открытии процесса через OpenProcess- это создается
дополнительный какойто обьект в системе с помощью которого возможно както взаимодействовать с процессом}
hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshoot = INVALID_HANDLE_VALUE then memo1.Lines.add('не удалось получить список процессов');
if hSnapshoot <> INVALID_HANDLE_VALUE thenbegintry
ProcSnapshot.dwSize := SizeOf(ProcSnapshot);
tempBOOL := Process32First(hSnapshoot, ProcSnapshot);//получаем первый процесс в снимкеif tempBOOL = falsethen memo1.Lines.add('нет первого процесса в списке');
while tempBOOL dobegin
memo1.Lines.Add(ProcSnapshot.szExeFile + ' ' + inttostr(ProcSnapshot.th32ProcessID));
if(ProcSnapshot.szExeFile = ProcName)then ProcPID:=ProcSnapshot.th32ProcessID;
tempBOOL := Process32Next(hSnapshoot, ProcSnapshot);//получаем следующий процессif tempBOOL = falsethen memo1.Lines.add('функция получения следующего процесса вернула FALSE');
end;
finally
CloseHandle(hSnapshoot);//освобождаем хэндл снимкаend;
end;
memo1.Lines.add('нашли процесс ' + ProcName + ' запомнили его PID=' + inttostr(ProcPID));
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:14.
{Устанавливаем отладочные привилегии для своего(этого) процесса САР, т.к. без данных
привилегий код внедрения работать не будет.
я не смогу это прокоментировать так как нихрена это не понимаю ну смысл понятен там и так.
код самодостаточен и вродебы переносим копипастом.
пусть работает главное не трогать руками}
szPrivilege:='SeDebugPrivilege';
fEnable:=true;//вкл или выкл отладочные привы, это если в функцию собрать этот код удобно
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken);
LookupPrivilegeValue(nil, szPrivilege, luid);
NewState.PrivilegeCount := 1;
NewState.Privileges[0].Luid := luid;
if(fEnable)thenbegin
NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
endelsebegin
NewState.Privileges[0].Attributes := 0;
end;
AdjustTokenPrivileges(hToken, False, NewState, SizeOf(NewState), nil, ReturnLength);
CloseHandle(hToken);
// Открываем процесс жертвы. чтобы это не значило... но знайте что это не то что вы знаете или подумали..{цель получить хэндл на процесс с помощью которого можно манипулировать процессом}
hProc := OpenProcess(PROCESS_ALL_ACCESS, false, ProcPID);
{сохраним в глобальную переменную этот хэндл, потом он пригодится чтобы создать локальную копию Хэндла
для чтения из пайпа}
g_DllHandle:=hProc;
memo1.Lines.Add('открываем процесс получаем его ХЭНДЛ=' + inttostr(cardinal(hProc)));
{Выделяем память внутри чужого процесса, в которую запишем какие-либо данные.
в нашем случае мы запишем туда строку- путь к длл. которую потом будем использовать
в качестве аргумента для запуска LoadLibraryA(путь к длл) }
pMyMemAdr := VirtualAllocEx(hProc, nil, Length(DllNamePAnsiChar), MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE);
memo1.Lines.Add('выделяем память в чужом процессе, получаем pointer=' + inttostr(cardinal(pMyMemAdr)));
//Пишем данные в выделенную память
WriteProcessMemory(hProc, pMyMemAdr, DllNamePAnsiChar, Length(DllNamePAnsiChar), BytesWriten);
memo1.Lines.Add('пишем данные в выделенную память, записано байт=' + inttostr(cardinal(BytesWriten)));
{когда нам эта память не нужна то надо ее освободить! а мы этого тут не делаем!
протекли. если мы так бездумно будем выделять память и писать туда чтото каждый раз
то ничем хорошим это не закончится. вопщем за памятью следить надо. чтото создали-
если лишнее то освободить.}{ищем адрес функции LoadLybraryA в памяти текущего (этого, нашего) процесса.
логика такая, КЕРНЕЛ32.ДЛЛ загружается ко всем вапще процессам и к чужому и к нашему(этому)
и располагает свои функции по одним и темже адресам, поэтому узнав адрес функции в нашем приложении
он будет таким же как и в чужом приложении. LoadLibraryA и LoadLibraryW отличаются кодировкой
Ansi и Юникод для аргумента. в какойто статье прочел что версии функций с индексом А на конце возвращают
адрес ,а с индексом W возвращают хендл}{про память и адреса скажу так.. загружаясь процесс погружается в виртуальный мир адресного пространства,
который любезно предоставляет ему система. большой мир адресов 4х байтный (32 бит) . и там и живет какбудто он один
во всем мире. но этот мир памяти процесса виртуален. т.е. например 2 процесса могут по одному адресу внутри себя
иметь совершенно разные данные. потому что их виртальные адресные пространства не пересекаются никак. у каждого
процесса свой персональный виртуальный адресный мир-адресное пространство. длл туда подселяется располагает
свои функции внутри этого виртуального мира. и по какойто причине КЕРНЕЛ32.ДЛЛ всегда располагает свои
функции по одним и темже виртуальным адресам внутри виртуального адресного пространства приложения.
взаимодействие приложухи с внешним миром (за пределами виртуального адресного пространства) происходит через
апи. мы просим систему чегото сделать за пределами нашего виртуального мира с помощью апи.}
pLoadLibAdr := GetProcAddress(GetModuleHandle('KERNEL32.DLL'), PAnsiChar('LoadLibraryA'));
memo1.Lines.Add('ищем адрес функции LoadLybraryA, pointer=' + inttostr(cardinal(pLoadLibAdr)));
// Запускаем удаленный поток{CreateRemoteThread может завершиться успешно даже если начальный адрес старта- белеберда
тем не менее это наверняка приведет к краху чужого приложения,
запускать удаленный поток нужно с какогото работоспособного кода (его адреса) который может выполниться.
часто таким началом старта является начало какойто функции, процедуры, подпрограммы. но это может быть и иначе.
мы можем и сами в какуюто область памяти что угодно понаписать байты, и запустить эти байты на выполнение начиная например
с 5-го байта от начала области памяти.. ну это к примеру. нам удобно указывать в качестве начала выполнения кода(потока)
адрес какойто функции.
вот функция LoadLibraryA которую предоставила (загрузила в адресное пространство процесса) дллка КЕРНЕЛ32.ДЛЛ
это подходящее место для старта потока в чужом процессе. аргумент для этой функции в виде строки (путь к длл)
мы уже записали в выделенную память в виртуальное адресное пространство чужого процесса. осталось запустить поток.
эта апи CreateRemoteThread ,зная о поводках пользователей в том что они любят стартовать с начала какойто функции,
любезно предоставляет нам сервис чтобы еще и аргумент подсунуть для этой стартовой функции. без такой фишечки
поток мы бы стартонули с начала какойто функции но как туда аргумент подсунуть была бы проблема.
однако если мы захотим стартонуть функцию которая требует например 2 аргумента для своей работы. вот так вот нельзя.
прототип стартовой функции потока должен быть таким чтоб тока 1 аргумент был и этот аргумент есть указатель на чтото.
хотя он может быть весьма длинным аргументом. строкой. мне тут самому не ясно вот мы указатель на область памяти
указываем которую нужно воспринимать как аргумент для стартовой функции, а где окончание области памяти этой, размер?
видимо используется ноль терминатор #0 для окончания.}
hRemoteThread := CreateRemoteThread(hProc, nil, 0, pLoadLibAdr, pMyMemAdr, 0, pRemoteThread);
memo1.Lines.Add('запуск удаленного потока, Хэндл=' + inttostr(cardinal(hRemoteThread)) + ' Pointer=' + inttostr(cardinal(pRemoteThread)));
{можно конечно подождать завершения инъекции.. но тогда код отправки сообщений из главной функции длл
отправит данные а тут они не обработаются.. потомучто мы ждем.. получатся взаимождущие петли происходящие.
и поэтому я код оставлю но закоменчу.
типа вот так можно ожидать когда удаленный поток завершит свою работу и можно сразу получить какойто
результат его работы, в виде кода возврата. который сюда както вернет отработавший удаленный поток.}// Ждем пока удаленный поток отработает...//WaitForSingleObject(hRemoteThread, INFINITE);//получаем код возврата удаленного потока. который по задумке должен содержать адрес загрузки длл//RemoteThreadResult := 0;//GetExitCodeThread(hRemoteThread, &RemoteThreadResult);//memo1.Lines.Add('код возврата =' + inttostr(cardinal(RemoteThreadResult)));}
Closehandle(hRemoteThread);
//освобождаем хэндл открытого процесса//Closehandle(hProc);//подержим хэндл открытым он нам нужен чтобы дубликат хэндла для пайпа сделать. закроем при выходе.end;
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:14.
procedure TForm1.Button3Click(Sender: TObject);
var
ProcPID: Cardinal;
hProc: THandle;
hRemoteThread: THandle;
pRemoteThread: Cardinal;
begin{пробуем запустить нужную нам функцию удаленным потоком внутри жертвы}
ProcPID := GetRemotePID('calc1.exe');
memo1.Lines.Add('PID=' + inttostr(cardinal(ProcPID)));
//вот мы какбы тут еще один хэндл делаем на жертву. хотя он открыт в кнопке2. ну пофиг. открыли закрыли. хуже не будет.
hProc := OpenProcess(PROCESS_ALL_ACCESS, false, ProcPID);
hRemoteThread := CreateRemoteThread(hProc, nil, 0, pAdrFunc2, nil, 0, pRemoteThread);
//если функция в чужом процессе отработала(Func2 мы тестим) то создастся лог файл c:\Log\Func2.txt
CloseHandle(hProc);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseHandle(g_DllHandle);
end;
function GetRemotePID(ProcName:string):Cardinal;
var
hSnapshoot: THandle;//дескриптор объекта- созданного снимка всех процессов.
ProcSnapshot:TProcessEntry32;//структура описывает один процесс. сюда записывается описание одного процесса из снимка для анализа
tempBOOL:Boolean;
begin{Ищем ProcessID (PID) интересующего нас процесса, для этого делается снимок
всех процессов в системе, он перебирается и сравнивается имя процесса с заданным
как тока путем перебора нашли нужный нам процесс (по имени) мы запомним его PID.
входной аргумент функции с именем процесса чувствителен к регистру Calc1.exe или calc1.exe}
Result:=0;
hSnapshoot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshoot = INVALID_HANDLE_VALUE thenExit;
if hSnapshoot <> INVALID_HANDLE_VALUE thenbegintry
ProcSnapshot.dwSize := SizeOf(ProcSnapshot);
tempBOOL := Process32First(hSnapshoot, ProcSnapshot);//получаем первый процесс в снимкеif tempBOOL = falsethenExit;
while tempBOOL dobeginif(ProcSnapshot.szExeFile = ProcName)then Result:=ProcSnapshot.th32ProcessID;
tempBOOL := Process32Next(hSnapshoot, ProcSnapshot);//получаем следующий процессend;
finally
CloseHandle(hSnapshoot);//я думаю что это выгружает обьект с указанным ИДend;
end;
end;
function SetDebugPrivilege(boolOnOff:boolean):Boolean;
var
hToken:THandle;//Хэндл токена безопасности.
szPrivilege: PChar;//название уровня безопасности. в системе уже существуют//имена мы будем использовать szPrivilege:='SeDebugPrivilege'
luid: TLargeInteger;
NewState: TTokenPrivileges;
ReturnLength: DWord;
begin{Устанавливаем отладочные привилегии для своего(этого) процесса, т.к. без данных
привилегий код внедрения работать не будет}
szPrivilege:='SeDebugPrivilege';
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken);
LookupPrivilegeValue(nil, szPrivilege, luid);
NewState.PrivilegeCount := 1;
NewState.Privileges[0].Luid := luid;
if(boolOnOff)thenbegin
NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
endelsebegin
NewState.Privileges[0].Attributes := 0;
end;
result:=AdjustTokenPrivileges(hToken, False, NewState, SizeOf(NewState), nil, ReturnLength);
CloseHandle(hToken);
end;
end.
Добавлено через 59 секунд
это было САР (control application)
теперь код ДЛЛ...
__________________
Цитата:
Сообщение от pybukon
прежде чета попросить я немнога раскажу чтоб вы понили как мне плоха
Цитата:
Сообщение от Byrger
А как сделать мой скрипт бесконечным?
Цитата:
Сообщение от XKOR
.. каждый день ионизированной ногой протирает больную)
Цитата:
Сообщение от PsyR
Вылоджите пожалуйста скрипт на рыбалку желательно что бы сам в ВХ клал адаптированый под шоки и так же скрипт на ТТ рец, тоже с диалогами
Последний раз редактировалось VORON, 29.01.2025 в 15:15.
Причина: Добавлено сообщение
library Pi;
uses
System.SysUtils,
Messages,//для SendMessage, PostMessage
Windows,
System.Classes,
TlHelp32;//для работы CreateToolhelp32Snapshot-сделать снимок процессов, узнать PIDtype TMyRec = Record
Age :Cardinal;
Name :String[20];
Fam :String[20];
End;{интересный факт, что такая структура (string[20] , string[20] , Cardinal) взвешивается SizeOF(MyRec) в 48 байт}type//хлам
TCopyDataStruct = packedrecord
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.
Причина: Добавлено сообщение
да случайно зашел. начинаешь писать ссыль в браузере, а он сам тебе предлагает куда сходить. вот чот я C начал вбивать... смотрю coderx - о! надо посмотреть... и тут бац оказывается живое не спамботное сообщение. абалдеть!