Как вызвать в MASM32 функцию по ординалу / номеру?

Тема в разделе "WASM.BEGINNERS", создана пользователем Scholium, 17 июл 2010.

  1. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Меня больше интересует директива extrn, а не proto. «Ида» генерит прекрасные листинги экспортирования функций. Это полностью избавляет от необходимости использовать Include\*.inc файлы от Iczelion’a. А lib-файлы можно взять из разных источников. Проблема только в том, что экспортирование по ординалам в «Иде» не такое изящное и, по-видимому, требует переделки lib-файлов.
     
  2. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Scholium
    PROTO вообще-то можно заменить на extrn.

    SHAllocShared PROTO :lol: WORD,:lol: WORD,:lol: WORD

    Вариант через extrn
    Второй способ немного лучше. Посмотришь в отладчике.
     
  3. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Спасибо за совет. Только я в основном работаю с экспортируемыми функциями, сгенерированные «Идой». Поэтому не возникает никакой необходимости иметь дело с proto. В этом смысле стиль «Иды» мне очень нравится и совершенно не хочется переходить на стиль Iczelion’a.
     
  4. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    Scholium,

    Процитировать доку ещё не значит её понять. extrn создаёт в объекте (то, что там названо module) ссылку на внешнее имя и всё! Без библиотеки импорта не взлетит.

    proto немного хитрее: кроме определения прототипа (для invoke) срабатывает аналогичный externdef эффект: имя объявляется public, если определено в этом модуле, и extern в противном случае. Но этого всё равно недостаточно для того чтобы link построил таблицы импорта.

    А вот сама библиотека импорта содержит объекты в которых расшифровывается, как создать соответствующий элемент IAT для конкретного имени, указанного как внешнее в нашем объекте (и то, как оно будет импортировано: по имени или по ординалу). Это имя, кстати, может никак не соотноситься с экспортируемым самой DLL:
    Код (Text):
    1. ;;; Kernel32.Def
    2. LIBRARY Kernel32
    3. EXPORTS
    4.   DieSlow@4 @183; = ExitProcess
    5.   Me@0      @316; = GetCurrentProcess
    6.   DieFast@8 @839; = TerminateProcess
    Код (Text):
    1. ;;; UseK32.masm
    2.         .386
    3.         .model  flat, stdcall
    4.         .code
    5.         includelib Kernel32.Lib
    6.  
    7. DieSlow proto uExitCode: dword
    8. Me proto
    9. DieFast proto hProcess: dword, uExitCode: dword
    10.  
    11. here:
    12.         invoke  DieSlow, 0
    13.         invoke  Me
    14.         invoke  DieFast, eax, 0
    15.  
    16.         end     here
    ----8<----
    s_d_f,

    Это с какой радости загиб с invoke? Если суффиксы @argsize для stdcall функций указаны в соответствии с прототипами, всё линкуется на ура. Другое дело что Lib не реагирует на полный синтаксис определений из раздела EXPORTS, т.е. для импорта ординалами этот метод годится, а вот с именами будет борода: Lib радостно добавит подчёркивание и опа.

    А способ с typedef / extern не то чтобы лучше — он просто другой в смысле механики. Переменные декорируются иначе чем функции (внешним будет объявлено имя _SHAllocShared без суффикса @12), так что приведённого кода для вызова stdcall функции через invoke недостаточно. Можно заменить:
    Код (Text):
    1. ;extern SHAllocShared:PARGS3
    2. extern _imp__SHAllocShared@12: PARGS3
    3. SHAllocShared equ [_imp__SHAllocShared@12]
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Scholium
    Вы не учитываете что загрузчик проделывает туже работу, соответственно задержки на настройку импорта фактически одинаковы. Таким образом недостаток динамического импорта только один и не значительный - увеличивает общий размер кода. Причём код может быть самодостаточным.
     
  6. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Как можно работать с extrn – было уже продемонстрировано в ссылке. Применяемый стиль объявления внешних функций от Ильфака Гильфанова очень даже ничего, по сравнению со стилем Iczelion’a. Об уже также говорилось. А lib-файлы (которые есть также в MS VS C++) подключаются обычным образом: includelib Lib\shlwapi.lib, для рассматриваемого примера. Все это уже было. Поэтому я не совсем понимаю, что Вас не устраивает в этом способе ассемблирования?

    Лично я ничего не имею против proto. И все примеры от Iczelion’a добросовестно использовал именно с этой директивой. Однако стиль «Иды» мне понравился больше. Поэтому я без крайней необходимости не хотел бы переходить на другой стиль.

    Также приводилась ссылка, где обсуждаемая проблема полностью решается в стиле Iczelion’a. Я об этом помню, но все же хочется найти решение в стиле «Иды». Получится – хорошо, нет – придумаем что-нибудь другое.
     
  7. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Хорошо, если это так. Только почему тогда Microsoft втихоря использует биндинг?
     
  8. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    Scholium,

    В общем-то я ничего не имею против различных способов. Просто акцентировал внимание на паре моментов, связанных с названием темы:

    1. Для импорта по ординалу нужна подходящая к этой DLL (в смысле соответствия ординалов, ну и наличия пабликов с ординалами а не именами) библиотека импорта, а не просто из какого-нибудь Platform SDK.

    2. Способ описания внешнего имени (extrn / proto) не влияет на способ импорта (ординал/имя) никоим образом. Естественно предположить, что для перекомпиляции сгенерированного IDA исходника extrn выглядит удобнее.
     
  9. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Допустим соответствующий lib-файл есть. Какое тогда будет определение экспортируемой функции (через extrn) и ее вызов? В msdn’e проскакивают строки вида:

    Код (Text):
    1. C16ThkSL01 = KERNEL.631
    2. ThunkConnect16 = KERNEL.651
    Только подробностей нет.

    Об этом и речь :) .
     
  10. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    Вот кусочек дампа ShLwApi.Lib из Windows SDK 6.0A:
    Код (Text):
    1.   Version      : 0
    2.   Machine      : 14C (x86)
    3.   TimeDateStamp: 4498BDB6 Wed Jun 21 06:32:06 2006
    4.   SizeOfData   : 0000001C
    5.   DLL name     : SHLWAPI.dll
    6.   Symbol name  : _IsCharSpaceW@4
    7.   Type         : code
    8.   Name type    : ordinal
    9.   Ordinal      : 29
    Соответственно в коде делаем так:
    Код (Text):
    1.     includelib ShLwApi.Lib
    2.     extern  IsCharSpaceW@4: near    ; переходник: jmp [__imp__IsCharSpaceW@4]
    3.     extern  _imp__IsCharSpaceW@4: dword; элемент IAT
    4. ;...
    5. ; вызов через переходник
    6.     push    ' '
    7.     call    IsCharSpaceW@4
    8. ;...
    9. ; вызов через элемент IAT
    10.     push    'A'
    11.     call    _imp__IsCharSpaceW@4; вообще-то тут [] подразумеваются, но они необязательны в MASM
    При использовании proto можно пользоваться invoke и код выходит красивше, но это скорее вопрос вкуса (да и IDA вроде не знает про invoke).
     
  11. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Естественно, в таком виде все работает, потому что, кроме ординала есть и имя функции. А Вы возьмите, например 437-ю функцию, у которой имени нет, хотя «Ида» каким-то магическим образом получает это имя (по-видимому, из отладочных символов). Вот код «Иды» (для декомпилированного control.exe):

    Код (Text):
    1. .idata:01001054 ; BOOL __stdcall IsOS(DWORD dwOS)
    2. .idata:01001054                 extrn __imp__IsOS@4:dword
    Однако строка «IsOS» отсутствует в файле shlwapi.dll, поэтому чтобы задействовать «Идашный» вызов

    Код (Text):
    1. .text:0100200D                 push    eax             ; dwOS
    2. .text:0100200E                 call    __imp__IsOS@4   ; IsOS(x)
    нужно, скорее всего, создать собственный shlwapi.lib, в котором ординалу 437 будет поставлено в соответствие имя «_imp__IsOS@4». Тогда будет все, как у «Иды» :) . В принципе для этого существуют def-файлы и утилита polib.exe. осталось только разобраться с синтаксисом и решением проблемы в общем случае (когда подобных преобразований нужно будет делать много). Но это уже толькео вопрос времени.

    Invoke хорош, когда Вы программируете на MASM’e самостоятельно, независимо от внешних условий. Только реально где это может понадобиться, если не считать кодирование вредоносного кода? Большие, самостоятельные проекты на чистом ассемблере я себе очень плохо представляю. Другое дело работа с дизассемблерным кодом «Иды». Если говорить открытым текстом, то кто покупает «Иду»? Точнее, зачем? Хакеры или крэкеры, из-за принципа, поломают или найдут уже поломанную «Иду». А покупают в основном западные фирмы с целью промышленного шпионажа. Мол, не хорошо тырить код друг у друга, но «Иду» мы вам продадим. Типа, курить и пить вредно, но покупайте сигареты и спиртное пока денег хватит. Или, переписывать mp3 – а-йя-йай, но устройства копирования продаются без проблем. Поэтому раз есть «Ида», которая и в демо-варианте достаточно крута, то глупо не воспользоваться ее преимуществами. А с invoke Ильфак не дружит, что, впрочем, для меня лично, не большая проблема. Если уж хочется получить аналог ЯВУ из asm кода, то к нашим услугам плагин hexrays.plw, о котором наш Ильфак в ранних интервью говорил, что, мол, я могу сделать даже си код из асма, но делать этого не собираюсь, так как это, мол, не есть гуд :) . А что мы видим в итоге? Плагин стоит в 2-3 раза дороже самой «Иды» и Ильфак уже не работает на дядю, я является владельцем собственной фирмы hex-rays.
     
  12. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Ну, вот поиск в направлении использовании утилиты polib.exe дал неплохие результаты.

    Командный файл
    Код (Text):
    1. SET FILENAME=shlwapi
    2.  
    3. :: Получение lib и def файлов из dll
    4. bin\polib %FILENAME%.dll /OUT:%FILENAME%.lib /MAKEDEF:%FILENAME%.def /MACHINE:IX86
    генерит файлы def и lib на основе заданной dll.

    В результате имеем для файла shlwapi.def

    Код (Text):
    1. LIBRARY "SHLWAPI.dll"
    2. EXPORTS
    3. "AssocCreate" ; SHLWAPI.dll
    4. "AssocGetPerceivedType" ; SHLWAPI.dll
    5. . . .
    6. "wvnsprintfA" ; SHLWAPI.dll
    7. "wvnsprintfW" ; SHLWAPI.dll
    8. "noname_1" @1 ; SHLWAPI.dll
    9. "noname_2" @2 ; SHLWAPI.dll
    10. . . .
    11. "noname_1b3" @435 ; SHLWAPI.dll
    12. "noname_1b4" @436 ; SHLWAPI.dll
    13. "noname_1b5" @437 ; SHLWAPI.dll
    14. "noname_1b6" @438 ; SHLWAPI.dll
    15. . . .
    16. "noname_235" @565 ; SHLWAPI.dll
    Теперь строку

    Код (Text):
    1. "noname_1b5" @437 ; SHLWAPI.dll
    меняем на строку

    Код (Text):
    1. "_IsOS@4" @437 ; SHLWAPI.dll
    Таких изменений в принципе может быть много, хотя можно ограничиться одной только этой строчкой. Затем из этого модифицированного def-файла с помощью командного файла

    Код (Text):
    1. SET FILENAME=shlwapi
    2.  
    3. :: Получение lib файла из def
    4. bin\polib /DEF:%FILENAME%.def /OUT:%FILENAME%.lib /MACHINE:IX86
    получаем искомый (модифицированный) lib-файл. На его основе код «Иды»

    Код (Text):
    1. push    eax             ; dwOS
    2. call    _imp__IsOS@4   ; IsOS(x)
    прекрасно компилится и перекомпилированное приложение control.exe запускается без проблем. Так что совместными усилиями мы получили более-менее нормальный способ переименования ординала в произвольное имя и вызов функции по этому имени. Единственное, что не радует, что это то, что polib не генерит суффикс и префикс в именах экспортируемых функций, что требует дополнительной обработки напильником получаемых с его помощью def-файлов. Но, хотя бы так :) .
     
  13. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    Беру. Та же многострадальная ShLwApi.Lib:
    Код (Text):
    1. Archive member name at A988: SHLWAPI.dll/    
    2. 4498BDB6 time/date Wed Jun 21 06:32:06 2006
    3.          uid
    4.          gid
    5.        0 mode
    6.       28 size
    7. correct header end
    8.  
    9.   Version      : 0
    10.   Machine      : 14C (x86)
    11.   TimeDateStamp: 4498BDB6 Wed Jun 21 06:32:06 2006
    12.   SizeOfData   : 00000014
    13.   DLL name     : SHLWAPI.dll
    14.   Symbol name  : _IsOS@4
    15.   Type         : code
    16.   Name type    : ordinal
    17.   Ordinal      : 437
    Повторяю ещё раз: в библиотеке импорта всегда есть символическое имя. По нему линкер цепляет кусочек данных (либо ординал, либо ссылку на элемент таблицы hint/name). Это абсолютно не зависит от того, экспортирует DLL имя или только ординал. Ничто ведь не мешает натравить на библиотеку dumpbin и убедиться в этом самому.

    В общем, я понял задачу так: есть сгенерированный IDA исходник, надо его скомпилировать. При чём тут MASM32 и ординалы? Если сервер символов настроен, IDA самостоятельно заменяет генерированные метки вида SHLWAPI.437 на то, что указано в .PDB и задача сводится к тривиальной рихтовке имён элементов IAT, чтобы ml/link всё это переварили. Вот только непонятно, библиотеки импорта предполагаются в наличии или всё-таки нет?

    А откуда ему взять суффикс?

    Не проще ли настроить символы и воспользоваться тем же dumpbin /exports, который выдаёт почти то, что надо:
    Код (Text):
    1. Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
    2. Copyright (C) Microsoft Corporation.  All rights reserved.
    3.  
    4.  
    5. Dump of file shlwapi.dll
    6.  
    7. File Type: DLL
    8.  
    9.   Section contains the following exports for SHLWAPI.dll
    10.  
    11.     00000000 characteristics
    12.     47B56271 time date stamp Fri Feb 15 11:59:13 2008
    13.         0.00 version
    14.            1 ordinal base
    15.          858 number of functions
    16.          314 number of names
    17.  
    18.     ordinal hint RVA      name
    19.  
    20.         356    0 00009D79 AssocCreate = _AssocCreate@24
    21.         500    1 0000CA93 AssocGetPerceivedType = _AssocGetPerceivedType@16
    22.         501    2 00019593 AssocIsDangerous = _AssocIsDangerous@4
    23. ...
    24.         857  138 00007F12 wvnsprintfA = _wvnsprintfA@16
    25.         858  139 00009101 wvnsprintfW = _wvnsprintfW@16
    26.           1      00021D1E [NONAME] _ParseURLA@8
    27.           2      000179EA [NONAME] _ParseURLW@8
    28.           3      00048A7E [NONAME] _PathFileExistsDefExtA@8
    29.           4      00015B37 [NONAME] _PathFileExistsDefExtW@8
    30.           5      00048B31 [NONAME] _PathFindOnPathExA@12
    31.           6      00012676 [NONAME] _PathFindOnPathExW@12
    32.          11      0000B3C0 [NONAME] _SHMapHandle@20
    33.          12      00015822 [NONAME] _SHCreateMemStream@8
    34.          13      00022B84 [NONAME] _RegisterDefaultAcceptHeaders@8
    35.          17      0001BC87 [NONAME] _SHWriteDataBlockList@8
    36. ...
     
  14. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Интересная у Вас версия shlwapi.dll! У меня есть две версии этой библиотеки 6.00.2900.5512 и 6.00.3790.3959 и несколько версий ее lib-файлов. Но нигде не определена 437-я функция (да и масса других в большими номерами). Вообще, я заметил, что в lib-файлах описание ординалов может отсутствовать вообще. Например, команда (см. http://vortex.masmcode.com/files/lib2def21.zip)

    Код (Text):
    1. SET FILENAME=shlwapi
    2.  
    3. :: Получение def файла из lib
    4. bin\lib2def %FILENAME%.lib
    генерит shlwapi.def с содержимым

    Код (Text):
    1. LIBRARY shlwapi
    2. EXPORTS
    3. "_AssocCreate@24"
    4. "_AssocGetPerceivedType@16"
    5. . . .
    6. "_wvnsprintfA@16"
    7. "_wvnsprintfW@16"
    всего 310 записей, тогда как экспортируемых этой dll-кой функций – 858. Другие версии lib-файлов могут дать всего 280 функций. Так что я могу согласиться с Вами, что «в библиотеке импорта всегда есть символическое имя», только ординальные функции там могут быть вообще не представлены.

    Как выясняется проблема в отсутствии подходящего файла shlwapi.lib (в котором отсутствует описание многих функций, в т.ч. 437-й). Поэтому данную библиотеку импорта надо создавать самому. Нужная дополнительная информация есть в файле shlwapi.pdb в виде:

    Код (Text):
    1. // pubsym <rva 0x1054> __imp__IsOS@4
    «Ида» без символов показывает, что

    Код (Text):
    1. .idata:01001054 ; Imports from SHLWAPI.dll
    2. .idata:01001054                 extrn SHLWAPI_437:dword ; IsOS:
    Далее, по этой информации можно построить нужные def и lib файлы самостоятельно. После этого все прекрасно компилируется (правда, нужно сделать глобальную замену «__imp» на «_imp»). Вопрос только один, где Вы берете или как получаете полноценный файл shlwapi.lib, в котором определены все 858 функций?

    Раз существует утилита lib2def.exe, то в качестве основы можно построить хороший def-файл из либа. Polib или dumpbin /exports укажет оставшиеся экспортируемые функции, которые можно обработать напильником на основе информации из отладочных символов (pdb-файлы) и «Иды». А так, если polib ничего не нашел, то dumpbin /exports ничего не найдет:

    Код (Text):
    1. Section contains the following exports for SHLWAPI.dll
    2. . . .
    3.            1 ordinal base
    4.          858 number of functions
    5.          315 number of names
    6.  
    7.     ordinal hint RVA      name
    8.  
    9.         356    0 00011C9C AssocCreate
    10.         500    1 0000BE63 AssocGetPerceivedType
    11. . . .
    12.         857  139 000126BF wvnsprintfA
    13.         858  13A 0000A808 wvnsprintfW
    14.           1      00004A9B [NONAME]
    15.           2      0000F817 [NONAME]
    16. . . .
    17.         436      0000E464 [NONAME]
    18.         437      0001149E [NONAME]
    19.         438      0003D0B2 [NONAME]
    20. . . .
    21. 566 00044FA8 [NONAME]
    Как видите, у Вас и shlwapi.dll другой (с отладочными символами, что-ли).

    Не пойму только о каком «сервере символов» Вы говорите? Демо-версия «Иды» 5.7 отладочные символы из интернета или локально грузит без проблем.
     
  15. baldr

    baldr New Member

    Публикаций:
    0
    Регистрация:
    29 апр 2010
    Сообщения:
    327
    Scholium,

    Сервер символов — это я так перевёл "symbol server", вроде корректно. Он устанавливается, например, в составе Debugging Tools for Windows. Если его настроить (MSDN в помощь), многие утилиты (и dumpbin в том числе) подхватывают .PDB и показывают именно то, что я процитировал.

    ShLwApi.Lib (.Lib, не .DLL!!!) взят из Windows SDK 6.0A, который был установлен вместе с Visual Studio 2008. Я сравнил экспорты этой библиотеки импорта с экспортами shlwapi.dll 6.00.2900.3314 (xpsp_sp2_gdr.080215-1241), вот что вышло:
    Код (Text):
    1.                 Total   Named
    2. shlwapi.dll     858     314
    3. ShLwApi.Lib     360     298
    Пристальное сравнение дало ещё более весёлые результаты. То, что именованные экспорты совпали почти все, понятно, но вот несовпавшие…

    Экспортируемая shlwapi.dll по имени функция IntlStrEqWorker(), к примеру, в .PDB называется StrIsIntlEqual() (то есть результаты dumpbin /exports надо использовать с умом: от декорированного имени, взятого из .PDB, нужно брать суффикс и лепить его к экспортируемому). Она такая не одна: StrCatW() <-> Shlwapi_StrCatW(), StrCpyW() <-> Shlwapi_StrCpyW() и т.п.

    Экспортируемая ShLwApi.Lib функция SHPropertyBag_ReadStrAlloc() для импорта по ординалу 567 в shlwapi.dll соответствует AssocQueryStringByKeyW() — т.е. библиотека импорта неродная. Таких тоже хватает: IStream_Copy() <-> AssocQueryStringW(), IStream_ReadStr() <-> PathCompactPathExW(), не вагон, конечно, но на маленькую тележку хватит. Тем более что отловить такой баг довольно сложно: количество аргументов у них разное, и порушенный стэк может вызвать любые эффекты.
    Проверил shlwapi.dll 6.0.6001.18000 (longhorn_rtm.080118-1840) из Vista, всё попадает тютелька в тютельку.
    Вытащил ShLwApi.Lib из VS.Net, там по ординалу только две функции, они попадают в XP'шные точно.

    На функции, которых в XP нет, я внимания не обращал.

    Вот такие пироги с котятами. Похоже, с импортом по ординалу не всё так просто. Создать библиотеку импорта по конкретной динамической библиотеке особого труда не составит, а вот будет ли собранное приложение работать с другими версиями этой DLL — вопрос.
     
  16. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Я понял, спасибо! «Debugging Tools for Windows» у меня есть, просто он мне сильно пока не нужен был. Но буду иметь в виду о его возможностях.

    Кажется, я понял, как бороться с «плохими» либами, которые, по сути, являются (бинарными) индексированными деф-файлами. Просто нужно написать скрипт, который автоматом генерит нужный деф-файл. Вот пример, подобной процедуры для shlwapi.dll.

    Берем демо-версию «Иды» 5.7 и грузим туда наш файл shlwapi.dll с отладочными символами. Затем копируем через буфер обмена (поскольку демо-версия не позволяет сохранять файлы) листинг этой длл-ки или ее часть содержащей «Export Address Table for SHLWAPI.dll», в файл shlwapi.lst:

    Код (Text):
    1. .text:77F61844 ;
    2. .text:77F61844 ; Export Address Table for SHLWAPI.dll
    3. .text:77F61844 ;
    4. .text:77F61844 off_77F61844    dd rva _ParseURLA@8, rva _ParseURLW@8, rva _PathFileExistsDefExtA@8
    5. .text:77F61844                                         ; DATA XREF: .text:77F61838 o
    6. .text:77F61844                 dd rva _PathFileExistsDefExtW@8, rva _PathFindOnPathExA@12 ; PathBuildRootW(x,x)
    7. . . .
    8. .text:77F61844                 dd rva _UrlHashA@12, rva _UrlHashW@12, rva _UrlIsA@8, rva _UrlIsNoHistoryA@4
    9. .text:77F61844                 dd rva _UrlIsNoHistoryW@4, rva _UrlIsOpaqueA@4, rva _UrlIsOpaqueW@4
    10. .text:77F61844                 dd rva _UrlIsW@8, rva _UrlUnescapeA@16, rva _UrlUnescapeW@16
    11. .text:77F61844                 dd rva _wnsprintfA, rva _wnsprintfW, rva _wvnsprintfA@16
    12. .text:77F61844                 dd rva _wvnsprintfW@16
    13. .text:77F625AC ;
    Мы видим, что все экспортируемые функции shlwapi.dll «Ида» полностью распознала. Тем лучше. Теперь идем на вкладку «Иды» Exports и с радостью обнаруживаем факт соответствия перечня имен в «Export Address Table for SHLWAPI.dll» списку ординалов от 1 до 858. Поэтому осталось сущая ерунда – преобразовать Export Table в def-файл. Что легко делается, например, таким скриптом Visual FoxPro:

    Код (Text):
    1. ******************************************************************************
    2. * lst2def.prg
    3. ******************************************************************************
    4. CLEAR
    5. SET TALK OFF
    6. SET SAFETY OFF
    7.  
    8. filename = "shlwapi"
    9.  
    10. infile = filename + ".lst"
    11. outfile = filename + ".def"
    12.  
    13. Line1 = " ; Export Address Table for "
    14. Line2 = " ;"
    15.  
    16. crlf = CHR(13)+CHR(10)  && Символы завершения строки
    17. tabulators = CHR(9) + CHR(9)
    18.  
    19. file1 = FOPEN(infile)
    20.  
    21. IF file1 < 0
    22.   WAIT 'Не могу открыть файл: ' + infile
    23.   QUIT
    24. ENDIF
    25.  
    26. file2 = FCREATE(outfile)
    27.  
    28. IF file2 < 0
    29.   WAIT 'Не могу создать файл: ' + outfile
    30.   QUIT
    31. ENDIF
    32.  
    33. End1 = FSEEK(file1, 0, 2)   && Определяем размер файла
    34. Top1 = FSEEK(file1, 0)  && Идем в начало. Сама переменная не нужна
    35.  
    36. IF End1 <= 0  && Если файл пуст
    37.    MESSAGEBOX('Пустой файл: ' + infile)
    38.    QUIT
    39. ENDIF
    40.  
    41. FWRITE(file2, 'LIBRARY "' + filename + '.dll"' + crlf)
    42. FWRITE(file2, 'EXPORTS' + crlf)
    43.  
    44. num = 0  
    45. pos = 0
    46. IsLine1 = .F.
    47.  
    48. snum = ""
    49. subline = "not empty"
    50. strline = FGETS(file1)
    51.  
    52. DO WHILE NOT EOF(file1) && На самом деле мы не можем доверять этому условию
    53.     Curpos1 = FSEEK(file1, 0, 1)
    54.    
    55.     IF Curpos1 >= End1  && Достигнут конец файла
    56.         EXIT
    57.     ENDIF
    58.    
    59.     pos = AT(Line1, strline)
    60.    
    61.     IF pos > 0  && Нашли начальную строку
    62.         IsLine1 = .T.
    63.         EXIT
    64.     ENDIF
    65.  
    66.     strline = FGETS(file1)
    67. ENDDO
    68.  
    69. IF NOT IsLine1
    70.     MESSAGEBOX("Искомая строка не найдена!")
    71.     QUIT
    72. ENDIF
    73.  
    74. strline = FGETS(file1)  && Пропускаем одну строку
    75. strline = FGETS(file1)
    76.  
    77. DO WHILE NOT EOF(file1)  && На самом деле мы не можем доверять этому условию
    78.     Curpos1 = FSEEK(file1, 0, 1)
    79.    
    80.     IF Curpos1 >= End1  && Достигнут конец файла
    81.         EXIT
    82.     ENDIF
    83.    
    84.     IF LEN(strline) < 17  && Нашли строку типа ".text:77F62A94 ;"
    85.         && MESSAGEBOX("Конечная строка: '" + strline + "'")
    86.         EXIT
    87.     ENDIF
    88.  
    89.     DO WHILE NOT EMPTY(subline)
    90.         pos = AT(" ;", strline)
    91.        
    92.         IF pos > 0  && Дошли до комментария
    93.             strline = LEFT(strline, pos - 1)  && Отсекаем комментарий
    94.         ENDIF
    95.    
    96.         pos = AT("rva ", strline)
    97.        
    98.         IF pos > 0
    99.             subline = SUBSTR(strline, pos + 4)
    100.            
    101.             num = num + 1
    102.             snum = LTRIM(STR(num))
    103.             pos = AT(",", subline)
    104.        
    105.             IF pos > 0
    106.                 FWRITE(file2, LEFT(subline, pos - 1))
    107.                 subline = SUBSTR(subline, pos + 1)
    108.             ELSE
    109.                 FWRITE(file2, subline)
    110.                 subline = ""
    111.             ENDIF
    112.            
    113.             FWRITE(file2, tabulators + "@" + snum + crlf)
    114.         ELSE
    115.             EXIT  && Нет подстроки "rva " в данной строке
    116.         ENDIF
    117.        
    118.         strline = subline
    119.     ENDDO
    120.    
    121.     strline = FGETS(file1)
    122.     subline = "not empty"
    123. ENDDO
    124.    
    125. CLOSE ALL
    126. QUIT
    127. ******************************************************************************
    128. ******************************************************************************
    В результате мы получим файл shlwapi.def:

    Код (Text):
    1.  LIBRARY "shlwapi.dll"
    2. EXPORTS
    3. _ParseURLA@8        @1
    4. _ParseURLW@8        @2
    5. _PathFileExistsDefExtA@8        @3
    6. _PathFileExistsDefExtW@8        @4
    7. . . .
    8. _CLSIDFromStringWrap@8      @436
    9. _IsOS@4     @437
    10. _SHLoadRegUIStringA@16      @438
    11. . . .
    12. _UrlUnescapeA@16        @853
    13. _UrlUnescapeW@16        @854
    14. _wnsprintfA     @855
    15. _wnsprintfW     @856
    16. _wvnsprintfA@16     @857
    17. _wvnsprintfW@16     @858
    Теперь осталась самая малость – преобразовать def в lib. Что просто делается с помощью командного файла def2lib.bat:

    Код (Text):
    1.  SET FILENAME=shlwapi
    2.  
    3. :: Получение lib файла из def
    4. bin\polib /DEF:%FILENAME%.def /OUT:%FILENAME%.lib /MACHINE:IX86
    Ну, вот и все! Мы получили «правильный» shlwapi.lib, которым заменяем «неправильный» lib-файл.

    Таким образом, с помощью хорошего либа мы можем спокойно перекомпилировать «Идашный» листинг и нам даже не придется сильно париться с вызовами функций в стиле «Иды». Единственный нюанс, это если ординалы на вкладке Exports идут не по порядку и с пропусками (пример, shell32.dll). Но тогда надо просто немного усложнить наш скрипт, учитывая фактический расклад экспорта ординалов. Думаю, для программиста ассемблерщика это не есть проблема, а всего лишь задача :) .

    Как видим, что в случае проблем, всегда можно позвать на помощь «Иду», она очень неплохо разбирает pdb-файлы, чего не скажешь про известную утилиту pdbdump, чем можно воспользоваться для генерации собственных дефов / либов.
     
  17. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    В соседней ветке опубликован более универсальный скрипт lst2def.prg на Visual FoxPro для получения def-файла из листинга «Иды».

    Таким образом, если данная функция не имеет имени, а только ординал, то ее вызов происходит по произвольному имени, которое мы назначаем этому ординалу в соответствующем def-файле. Вызов функции только по ординалу невозможен в MASM32. Но, зная ординал, можно получить адрес функции, например, через GetProcAddress в eax, которую затем можно вызвать непосредственно через call [eax]. Это и будет окончательным ответом на поставленный в заголовке темы вопрос.