Подмена кода функции в секции EXPORT

Тема в разделе "WASM.WIN32", создана пользователем Dmitry_Koteroff, 22 ноя 2005.

  1. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia
    Может быть, кто-то из присутствующих решал такую задачу. Есть exe-шник, у него - секция (директория?) EXPORT, там - функция с определенным именем. Необходимо найти в exe-шнике, где начинается код этой функции, и вставить туда



    xor eax, eax

    ret



    Т.е. сделать вместо функции "заглушку". Весь вопрос в том, как найти адрес кода, зная имя функции.



    Собственно, я тут полчасика почитал разные документы про PE-заголовки, и понял, что вручную это все реализовывать - замучаешься. Там смешения на смещениях сидят и смещением погоняют. Несколько дней на отладку точно обеспечено.



    По сему - если у кого-то случайно завалялся код, выполняющий примерно описанные действия, очень прошу им поделиться.
     
  2. Ms Rem

    Ms Rem New Member

    Публикаций:
    0
    Регистрация:
    17 апр 2005
    Сообщения:
    1.057
    Адрес:
    С планеты "Земля"
    Для начала нужно загрузить этот экзешник с помощью LoadLibraryEx и с помощью GetProcAddress определить адрес интересующей функци.

    Затем вычисляем ее RVA который будет равен полученому адресу - адрес который вернула LoadLibraryEx.

    Затем нужно преобразовать RVA в RAW offset, для этого сначала получаем указатель на IMAGE_NT_HEADERS с помощью следующего макроса:
    Код (Text):
    1. #define NT_HEADERS(Image) ((PIMAGE_NT_HEADERS)(RVATOVA(Image, ((PIMAGE_DOS_HEADER)Image)->e_lfanew)))


    Затем получаем указатель на заголовок первой секции PE файла:
    Код (Text):
    1. #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER)        \
    2.     ((ULONG_PTR)ntheader +                                              \
    3.      FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) +                 \
    4.      ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader   \
    5.     ))


    Затем делаем цикл по секциям и проверяем на принадлежность полученого RVA каждой из них. Размер секции равен Section->VirtualSize (если он = 0, то использовать SizeOfRawData), RVA секции - Section->VirtualAddress. Если нужная секция найдена, то вычисляем смещение в секции Offset = MyRVA - Section->VirtualAddress и получаем смещение в файле. FileOffset = Section->PointerToRawData + Offset.

    Вот собственно и все, по полученому смещению можно патчить сам файл.
     
  3. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Dmitry_Koteroff

    > "вручную это все реализовывать - замучаешься"



    Зачем же вручную, c помощью OllyDbg, например, это делается легко и просто за пару минут

    Открываешь свой exe или dll, переключаешь на него View, делаешь Search for Name (label) in current module и находишь интересующее имя (+адрес,секция,тип export\import), жмешь Follow in Disassembler и попадаешь на точку входа. Правишь код (Binary \ Edit) и копируешь изменения в экзешник Copy to executable - Оля сама посчитает все смещения и запишет куда надо
     
  4. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia


    Мало того, что нужно именно "вручную", это еще и нужно реализовать на Perl. :) Специфика задачи. Сразу скажу, что это не вирус и не троян, и вообще - цели преследуются исключительно добрые (а именно - нужно "доработать" одну бесплатную и OpenSource-программу под MinGW, но не перекомпилируя ее каждый раз, т.к. новые версии выходят очень часто, и компилировать снова и снова в будущем - замучаешься).





    Вот как раз этого-то делать и не хотелось бы. В принципе, прочитав 10 раз Ваше сообщения, я его наконец-то понял (это моя вина, Вы как раз очень хорошо все описали). Нельзя ли получить RVA непосредственно из секции, не загружая exe-шник в память?
     
  5. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    Dmitry_Koteroff

    можно посмотреть директорию экспорта - там будет rva твоей функции
     
  6. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia
    Замечательно! Только, догадываюсь, до этой "директории экспорта" добраться ничуть не проще, чем описанные выше инструкции. :)
     
  7. infern0

    infern0 New Member

    Публикаций:
    0
    Регистрация:
    7 окт 2003
    Сообщения:
    811
    Адрес:
    Russia
    до PE header знаешь как добратся ?

    далее

    + 78h DWord Export Table RVA

    + 7Ch DWord Export Data Size



    идешь туда. Там будет



    typedef struct _IMAGE_EXPORT_DIRECTORY {

    ULONG Characteristics;

    ULONG TimeDateStamp;

    USHORT MajorVersion;

    USHORT MinorVersion;

    ULONG Name;

    ULONG Base;

    ULONG NumberOfFunctions;

    ULONG NumberOfNames;

    PULONG *AddressOfFunctions;

    PULONG *AddressOfNames;

    PUSHORT *AddressOfNameOrdinals;

    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;



    ищещь свое имя и получаешь rva функции.
     
  8. Ms Rem

    Ms Rem New Member

    Публикаций:
    0
    Регистрация:
    17 апр 2005
    Сообщения:
    1.057
    Адрес:
    С планеты "Земля"
    Dmitry_Koteroff

    Вот приблизительный код GetProcAddress на сях, правда там много лишнего (поддержка экспортов по ордилалам, форвардинга и.т.д.).

    Я понамаю, что этот код далеко не лучший для обучения пример, но возможно он будет чем-нибудь полезен.


    Код (Text):
    1. PVOID GetProcAddressEx(
    2.            IN PVOID Image,
    3.            IN PCSTR lpProcName,
    4.            IN GETMODULEHANDLE CsGetModuleHandle
    5.            )
    6. {
    7.     PIMAGE_DOS_HEADER dHeader = Image;
    8.     PVOID             Result = NULL;
    9.  
    10.     if (dHeader->e_magic == IMAGE_DOS_SIGNATURE)
    11.     {
    12.         PIMAGE_NT_HEADERS ntHeaders = RVATOVA(Image, dHeader->e_lfanew);
    13.  
    14.         if (ntHeaders->Signature == IMAGE_NT_SIGNATURE)
    15.         {
    16.             ULONG ExpRVA  = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress;
    17.             ULONG ExpSize = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. Size;
    18.  
    19.             if (ExpRVA && ExpRVA < ntHeaders->OptionalHeader.SizeOfImage)
    20.             {
    21.                 PIMAGE_EXPORT_DIRECTORY Export = RVATOVA(Image, ExpRVA);
    22.                 ULONG  NamesAddr = (ULONG)RVATOVA(Image, Export->AddressOfNameOrdinals);
    23.                 ULONG  NameAddr  = (ULONG)RVATOVA(Image, Export->Name);
    24.                 PULONG pName     = (PULONG)RVATOVA(Image, Export->AddressOfNames);
    25.                 ULONG r;
    26.  
    27.                 for (r = 0; r < Export->NumberOfFunctions; r++)
    28.                 {
    29.                     BOOLEAN bFindByOrdinal = ((ULONG)lpProcName == (USHORT)lpProcName);
    30.                     ULONG   nOrdinal, dwFuncRva;
    31.  
    32.                     if (r * 2 + NamesAddr == NameAddr) break;
    33.  
    34.                     nOrdinal  = ((PUSHORT)NamesAddr)[r];
    35.                     dwFuncRva = ((PULONG)RVATOVA(Image, Export->AddressOfFunctions))[nOrdinal];
    36.  
    37.                     nOrdinal += (USHORT)Export->Base;
    38.  
    39.                     if (!bFindByOrdinal && pName && r < Export->NumberOfNames)
    40.                     {
    41.                         if (!_stricmp(lpProcName,(PCHAR)RVATOVA(Image, *pName)))
    42.                         {
    43.                             if (IN_REGION(dwFuncRva, ExpRVA, ExpSize))
    44.                             {
    45.                                 CHAR  DllName[0x100];
    46.                                 PCHAR fwdName = RVATOVA(Image, dwFuncRva);
    47.                                 PCHAR fnName  = strchr(fwdName, '.');
    48.                                 PVOID pLibrary;
    49.  
    50.                                 if (fnName)
    51.                                 {
    52.                                     memset(DllName, 0, sizeof(DllName));
    53.  
    54.                                     strncpy(DllName, fwdName, fnName - fwdName);
    55.                                     strcat(DllName, ".dll");
    56.  
    57.                                     fnName++;
    58.  
    59.                                     pLibrary = CsGetModuleHandle(DllName);
    60.  
    61.                                     if (pLibrary)
    62.                                     {
    63.                                         Result = GetProcAddressEx(pLibrary, fnName, CsGetModuleHandle);
    64.                                     }
    65.                                 }
    66.                                
    67.                             } else  Result = RVATOVA(Image, dwFuncRva);
    68.                             break;
    69.                         }
    70.                     } else
    71.                     if (bFindByOrdinal && (nOrdinal == (USHORT)lpProcName))
    72.                     {
    73.                         Result = RVATOVA(Image, dwFuncRva);
    74.                         break;
    75.                     }
    76.  
    77.                     pName++;
    78.                 }              
    79.             }
    80.         }
    81.     }
    82.  
    83.     return Result;
    84. }
     
  9. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia




    Иду туда, но там, похоже, нету. Причем Data Size верно вычисляется, похоже (брал 1 exe-шник и 1 dll с потолка, смотрел, что выдает скрипт, потом - что выдает PE Editor из Far, размеры совпали). Но вот Export Table RVA показывает куда-то в пустоту. Вот скрипт:


    Код (Text):
    1.  
    2. #!perl -w
    3.  
    4. open(F, "u1230_32.dll");
    5. binmode(F);
    6.  
    7. seek(F, 60, 0);
    8. my ($e_lfanew) = unpack("L", fread(*F, 4));
    9. print "e_lfanew = $e_lfanew\n";
    10.  
    11. seek(F, $e_lfanew+0x7C, 0); # DWord Export Table RVA
    12. my ($exportTableRVA) = unpack("L", fread(*F, 4));
    13. print "exportTableRVA = $exportTableRVA\n";
    14. my ($exportDataSize) = unpack("L", fread(*F, 4));
    15. print "exportDataSize = $exportDataSize\n";
    16.  
    17. seek(F, $exportTableRVA+18, 0); #  IMAGE_EXPORT_DIRECTORY.Name
    18. my ($numberOfFunctions) = unpack("L", fread(*F, 4));
    19. print "numberOfFunctions = $numberOfFunctions\n";
    20. my ($numberOfNames) = unpack("L", fread(*F, 4));
    21. print "numberOfNames = $numberOfNames\n";
    22. my ($addressOfFunctions) = unpack("L", fread(*F, 4));
    23. print "addressOfFunctions = $addressOfFunctions\n";
    24. my ($addressOfNames) = unpack("L", fread(*F, 4));
    25. print "addressOfNames = $addressOfNames\n";
    26.  
    27. sub fread
    28. {
    29.     my ($h, $sz) = @_;
    30.     read($h, my $data, $sz) or die "Cannot read\n";
    31.     return $data;
    32. }
    33.  
     
  10. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia
    Там только комментарий неправильный, не IMAGE_EXPORT_DIRECTORY.Name, а NumberOfFunctions, но это дела не меняет.



    Собственно, получаются неправдоподобные numberOfFunctions.



    В коде выше, где эмуляция GetProcAddressEx, делается то же самое, насколько я понимаю.
     
  11. Ms Rem

    Ms Rem New Member

    Публикаций:
    0
    Регистрация:
    17 апр 2005
    Сообщения:
    1.057
    Адрес:
    С планеты "Земля"


    Функция GetProcAddressEx работает с уже отображенным образом PE файла а не с его RAW данными. Нужно сначала отмапить файл по секциям, либо переводить все RVA в RAW offset.
     
  12. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia
    Ага, так вот где собака порылась... Нужно делать цикл по секциям и все такое...
     
  13. Dmitry_Koteroff

    Dmitry_Koteroff New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2003
    Сообщения:
    20
    Адрес:
    Russia
    Ну вот, после многочасового сеанса отладки все, наконец, получилось. Решение на Perl тут:



    http://forum.dklab.ru/perl/advises/PoluchenieAdresaNachalaFunktsiiSOpr edelennimImenemVTeleExe-fayla.html

    Огромное спасибо Ms Rem! Без его подсказок вообще бы ничего не получилось, это уж точно - ибо документации на данную тему практически нет (все патчат EXE-шники в памяти, мало кто - в файлах).