Доброго всем времени! Осваиваю немного внедрение в ПЕ файл своего кода. Сам шелл-код генерируется одной из утилит типа DLL2ShellCode. В своем коде я Запоминаю значение старой точки входа OldEntryPoint = EntryPoint + ImageBase; Добавляю новую секцию Загружаю туда шеллкод Меняю все необходимые поля заголовков и т.д. В общем на тестовом примере все работает, но мне в конце нужно вернуть управление на OldEntryPoint пытался сделать как то так(это сама длл, которая внедряется как шеллкод в целевой ПЕ): Код (C++): #define WIN32_LEAN_AND_MEAN #include <Windows.h> BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { DWORD64 rwx = 7083504; // это для теста жестко прошит адрес OEP FARPROC fp = (FARPROC)rwx; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: //OutputDebugStringA("DLL_PROCESS_ATTACH"); MessageBoxA(0, "DllMain!", "", 0); fp(); // По идее тут должен вызваться код по OEP... break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } но это не работает( Подозреваю, я что то глобально не допонимаю, но что? подскажите, куда копать
Код (C++): DWORD64 rwx = 7083504; // адрес OEP DWORD WINAPI JumpToOEP(LPVOID lpParam) { MessageBoxA(0, "DllMain!", "", 0); // Переходим на OEP ((void(*)())rwx)(); return 0; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(NULL, 0, JumpToOEP, NULL, 0, NULL); break; } return TRUE; }
galenkane, MS настоятельно не рекомендует вызывать CreateThread() из DllMain(). https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
она еще рекомендует обновляться до 11 версии --- Сообщение объединено, 24 апр 2025 --- Вообще для такой задачи используется LoadPE
Вам нужен загрузчик пе из памяти. Так как используется модуль, а не шелл код, релоки импорт и тп. В длл потоки нельзя асинхронно создавать.
Так у него и есть загрузчик пе из памяти, как минимум импорт он обрабатывать может. Но нет номера техподдержки, куда можно звонить, если не работает.
тут нет информации, что и как именно не работает в двухтысячных меня третировали женщины бальзаковского возраста из бухгалтерии звонками с лейтмотивом "ваш чертов принтер не работает, идите и делайте его" при этом в пункте назначения на экране перед дамой висело окошко "Insert a paper sheet and press Enter" тут вопрос первый, вызывается ли в принципе DllMain хоть как-нибудь я бы проверял до switch - а почему нет, собственно. и вопрос второй, правильно ли компилируется вызывающий OEP код и туда ли куда надо указывает указатель в момент выполнения. можно или дизассемблировать эту длл, или чтобы не возиться, пометить уникальной строкой процедуру и найдя в условном hiew убедиться, что вызов скомпилирован в то, что ожидалось. и по указателю убедиться, что он указывает не в молоко
--- Сообщение объединено, 25 апр 2025 --- Проверил, не переходит... Поток создается, но не переходит на OEP
"перейти на OEP" означает, что у нас а) известен адрес исходной точки входа (OEP) в уже загруженном в память приложении и б) наш код выполняет туда jmp, call или ret (если перед ret в стек положить адрес, куда мы хотим перейти) то есть вернуться из DllMain нужно не в вызвавший её код, т е в винду например, а в нами заданное место где-то в адресах кода приложения. поэтому код вида Код (Text): mov eax, 12345 ret и не должен сработать с желаемым эффектом, потому что это обычный возврат в вызвавший DllMain код но + с каким-то числом в eax вот если бы перед ret была инструкция вида push eax, тогда ret возьмет из стека в данном случае 12345 и поместит в IP. то есть произойдет безусловный переход по адресу 12345. (для 32-бит режима, кстати, а не 64 бит) или можно написать jmp eax или call eax учитывая разницу в положении указателя стека. кроме этого, в отладчике адреса скорее всего представлены в hex, а константа в примере десятичная а чтобы си это сам скомпилировал, нужен синтаксис вида ((void(*)())rwx)(); как предложил galenkane то есть rwx мы вынуждаем считать указателем на функцию и вызываем в итоге должен сгенерироваться код а-ля Код (Text): mov rax, qword ptr [rwx] call rax
oep тема не простая, был определен критерий - абсолютная адресация и крутилосъ это под визорами, но это были протекторы. Что вы собираете, что за шелл не понятно. И причем там оеп? > а) известен адрес исходной точки входа (OEP) в уже загруженном в память приложении Да, это адрес запуска стартап кода криптора.
на мой взгляд, у топик стартера примерно одна и та же проблема нарисовалась бы вне зависимости от функционала кода, которому он планирует передать управление, будь то стартап код рантайма си, стартап не-си АКА делфи и тд и криптор и любой другой код потому что любой такой вызов упирается в то, как синтаксически оформить на си вызов функции по заранее известному и фиксированному адресу, а не в тело другой функции/процедуры из этого же исходника или стандартных библиотек полагаю, что вопрос был узко в этом. и если 7083504 из фрагмента кода выше - это не начало программы, а начало её обёртки т.е. пакера/криптора/etc то поведение программы не поменяется и задача решена пока в только той части, что некий свой код отработал до собствено старта кода программы.
Код (C): // End marker to calculate size extern "C" __declspec(dllexport) void ShellcodeEnd() {} // Simple function to get the size of the shellcode extern "C" __declspec(dllexport) DWORD GetShellcodeSize() { return (DWORD)((BYTE*)ShellcodeEnd - (BYTE*)ShellcodeStart); } а где гарантия что линкер не переставит фунции ?
линкер может переставить местами только объектные файлы = куски кода от разных двух исходных cpp файлов но в пределах одного исходника, для которого компилятором (!) генерится один общий сегмент кода, я бы сказал "очень вряд ли" возможны рандомные перетасовывания тел функций в адресах относительно начала этого куска сегмента кода мы не обсуждаем инлайн, которые по определению втираются побайтно посреди использующего их кода )
гарантий нету - LTO can indeed change the layout order of functions as they appear in a C++ source file. Since LTO performs whole-program analysis and optimization, it can alter function placements for performance improvements. PGO - Profile-Guided Optimization (PGO) – If runtime profiling data is available, LTO can rearrange functions based on actual execution frequency.