Возврат из шелл-кода на OEP

Тема в разделе "WASM.BEGINNERS", создана пользователем dobryakov, 24 апр 2025.

Метки:
  1. dobryakov

    dobryakov New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2025
    Сообщения:
    2
    Доброго всем времени!
    Осваиваю немного внедрение в ПЕ файл своего кода.
    Сам шелл-код генерируется одной из утилит типа DLL2ShellCode.
    В своем коде я
    Запоминаю значение старой точки входа OldEntryPoint = EntryPoint + ImageBase;
    Добавляю новую секцию
    Загружаю туда шеллкод
    Меняю все необходимые поля заголовков и т.д.

    В общем на тестовом примере все работает, но мне в конце нужно вернуть управление на OldEntryPoint

    пытался сделать как то так(это сама длл, которая внедряется как шеллкод в целевой ПЕ):
    Код (C++):
    1. #define WIN32_LEAN_AND_MEAN
    2. #include <Windows.h>
    3.  
    4. BOOL APIENTRY DllMain( HMODULE hModule,
    5.                        DWORD  ul_reason_for_call,
    6.                        LPVOID lpReserved
    7.                      )
    8. {
    9.     DWORD64 rwx = 7083504; // это для теста жестко прошит адрес OEP
    10.     FARPROC fp = (FARPROC)rwx;
    11.     switch (ul_reason_for_call)
    12.     {
    13.     case DLL_PROCESS_ATTACH:
    14.         //OutputDebugStringA("DLL_PROCESS_ATTACH");
    15.         MessageBoxA(0, "DllMain!", "", 0);
    16.         fp(); // По идее тут должен вызваться код по OEP...
    17.         break;
    18.     case DLL_THREAD_ATTACH:
    19.     case DLL_THREAD_DETACH:
    20.     case DLL_PROCESS_DETACH:
    21.         break;
    22.     }
    23.     return TRUE;
    24. }
    25.  
    но это не работает(
    Подозреваю, я что то глобально не допонимаю, но что?
    подскажите, куда копать
     
  2. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    2.036
    Например то, что надо использовать отладчик, а не гадать.
     
    miilalex нравится это.
  3. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    384
    Код (C++):
    1. DWORD64 rwx = 7083504; // адрес OEP
    2.  
    3. DWORD WINAPI JumpToOEP(LPVOID lpParam)
    4. {
    5.     MessageBoxA(0, "DllMain!", "", 0);
    6.    
    7.     // Переходим на OEP
    8.     ((void(*)())rwx)();
    9.    
    10.     return 0;
    11. }
    12.  
    13. BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    14. {
    15.     switch (ul_reason_for_call)
    16.     {
    17.     case DLL_PROCESS_ATTACH:
    18.         CreateThread(NULL, 0, JumpToOEP, NULL, 0, NULL);
    19.         break;
    20.     }
    21.     return TRUE;
    22. }
     
  4. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    583
    про релоки как?
     
    Prober нравится это.
  5. Prober

    Prober Member

    Публикаций:
    0
    Регистрация:
    4 дек 2008
    Сообщения:
    50
    Ahimov нравится это.
  6. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    384
    она еще рекомендует обновляться до 11 версии
    --- Сообщение объединено, 24 апр 2025 ---
    Вообще для такой задачи используется LoadPE
     
    M0rg0t нравится это.
  7. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    254
    Вам нужен загрузчик пе из памяти. Так как используется модуль, а не шелл код, релоки импорт и тп.

    В длл потоки нельзя асинхронно создавать.
     
  8. f13nd

    f13nd Well-Known Member

    Публикаций:
    0
    Регистрация:
    22 июн 2009
    Сообщения:
    2.036
    Так у него и есть загрузчик пе из памяти, как минимум импорт он обрабатывать может. Но нет номера техподдержки, куда можно звонить, если не работает.
     
  9. miilalex

    miilalex New Member

    Публикаций:
    0
    Регистрация:
    8 сен 2024
    Сообщения:
    19
    тут нет информации, что и как именно не работает

    в двухтысячных меня третировали женщины бальзаковского возраста из бухгалтерии звонками с лейтмотивом "ваш чертов принтер не работает, идите и делайте его"
    при этом в пункте назначения на экране перед дамой висело окошко "Insert a paper sheet and press Enter"

    тут вопрос первый, вызывается ли в принципе DllMain хоть как-нибудь
    я бы проверял до switch - а почему нет, собственно.

    и вопрос второй, правильно ли компилируется вызывающий OEP код и туда ли куда надо указывает указатель в момент выполнения.

    можно или дизассемблировать эту длл, или чтобы не возиться, пометить уникальной строкой процедуру и найдя в условном hiew убедиться, что вызов скомпилирован в то, что ожидалось.

    и по указателю убедиться, что он указывает не в молоко
     
  10. Research

    Research Active Member

    Публикаций:
    1
    Регистрация:
    6 янв 2024
    Сообщения:
    249
  11. dobryakov

    dobryakov New Member

    Публикаций:
    0
    Регистрация:
    24 апр 2025
    Сообщения:
    2
    --- Сообщение объединено, 25 апр 2025 ---
    Проверил, не переходит...
    Поток создается, но не переходит на OEP

     
    aa_dav нравится это.
  12. miilalex

    miilalex New Member

    Публикаций:
    0
    Регистрация:
    8 сен 2024
    Сообщения:
    19
    "перейти на OEP" означает, что у нас
    а) известен адрес исходной точки входа (OEP) в уже загруженном в память приложении и
    б) наш код выполняет туда jmp, call или ret (если перед ret в стек положить адрес, куда мы хотим перейти)

    то есть вернуться из DllMain нужно не в вызвавший её код, т е в винду например, а в нами заданное место где-то в адресах кода приложения.

    поэтому код вида
    Код (Text):
    1. mov eax, 12345
    2. ret
    3.  
    и не должен сработать с желаемым эффектом, потому что это обычный возврат в вызвавший DllMain код но + с каким-то числом в eax

    вот если бы перед ret была инструкция вида push eax, тогда ret возьмет из стека в данном случае 12345 и поместит в IP.
    то есть произойдет безусловный переход по адресу 12345. (для 32-бит режима, кстати, а не 64 бит)

    или можно написать jmp eax или call eax
    учитывая разницу в положении указателя стека.

    кроме этого, в отладчике адреса скорее всего представлены в hex, а константа в примере десятичная

    а чтобы си это сам скомпилировал, нужен синтаксис вида
    ((void(*)())rwx)();
    как предложил galenkane
    то есть rwx мы вынуждаем считать указателем на функцию и вызываем

    в итоге должен сгенерироваться код а-ля
    Код (Text):
    1.  
    2. mov rax, qword ptr [rwx]
    3. call rax
    4.  
     

    Вложения:

    • asm i86-64.pdf
      Размер файла:
      645,4 КБ
      Просмотров:
      114
    dobryakov и M0rg0t нравится это.
  13. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    254
    oep тема не простая, был определен критерий - абсолютная адресация и крутилосъ это под визорами, но это были протекторы. Что вы собираете, что за шелл не понятно. И причем там оеп?

    > а) известен адрес исходной точки входа (OEP) в уже загруженном в память приложении

    Да, это адрес запуска стартап кода криптора.
     
  14. miilalex

    miilalex New Member

    Публикаций:
    0
    Регистрация:
    8 сен 2024
    Сообщения:
    19
    на мой взгляд, у топик стартера примерно одна и та же проблема нарисовалась бы вне зависимости от функционала кода, которому он планирует передать управление, будь то стартап код рантайма си, стартап не-си АКА делфи и тд и криптор и любой другой код

    потому что любой такой вызов упирается в то, как синтаксически оформить на си вызов функции по заранее известному и фиксированному адресу, а не в тело другой функции/процедуры из этого же исходника или стандартных библиотек

    полагаю, что вопрос был узко в этом.

    и если 7083504 из фрагмента кода выше - это не начало программы, а начало её обёртки т.е. пакера/криптора/etc то поведение программы не поменяется и задача решена пока в только той части, что некий свой код отработал до собствено старта кода программы.
     
  15. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    384
    Пусть автор допилит под себя.
     

    Вложения:

    • OEP_Test.zip
      Размер файла:
      438,8 КБ
      Просмотров:
      68
    Research нравится это.
  16. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    583
    Код (C):
    1. // End marker to calculate size
    2. extern "C" __declspec(dllexport) void ShellcodeEnd() {}
    3.  
    4. // Simple function to get the size of the shellcode
    5. extern "C" __declspec(dllexport) DWORD GetShellcodeSize()
    6. {
    7.     return (DWORD)((BYTE*)ShellcodeEnd - (BYTE*)ShellcodeStart);
    8. }
    9.  
    а где гарантия что линкер не переставит фунции ?
     
  17. miilalex

    miilalex New Member

    Публикаций:
    0
    Регистрация:
    8 сен 2024
    Сообщения:
    19
    линкер может переставить местами только объектные файлы = куски кода от разных двух исходных cpp файлов

    но в пределах одного исходника, для которого компилятором (!) генерится один общий сегмент кода, я бы сказал "очень вряд ли" возможны рандомные перетасовывания тел функций в адресах относительно начала этого куска сегмента кода

    мы не обсуждаем инлайн, которые по определению втираются побайтно посреди использующего их кода )
     
  18. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    583
    гарантий нету -

    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.
     
OSZAR »