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

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

  1. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Многие dll-ки экспортируют свои функции только по номеру. И даже если нам известно ее имя (по подсказке IdaPro использующей отладочные символы) и прототип, то, как вызвать подобную функцию в собственном ассемблерном коде – непонятно. Ида вызывает данную функцию по имени, которого нет в соответствующем lib-файле, а потому компилятору это не нравиться. А в отладчике мы видим обращение по прямому адресу, который конечно можно вычислить хакерскими способами, но тогда вопрос, а в чем прикол экспорта по ординалу? С таким же успехом я могу обратиться к чужой, не экспортируемой функции. В Оле Дебаговой синтаксис вызова примерно такой:

    Код (Text):
    1. CALL DWORD PTR DS:[<&SHLWAPI.#437>]
    но его не понимает компилятор, а реально вызов будет примерно таким:

    Код (Text):
    1. CALL DWORD PTR DS:[01001054]
    где по адресу [01001054] находится реальный адрес искомой функции SHLWAPI.#437, т.е. код вида

    Код (Text):
    1. 01001054 > . 9E14DB77       DD SHLWAPI.#437
    IdaPro дает следующий код (с отладочными символами)

    Код (Text):
    1. .idata:01001054 ; BOOL __stdcall IsOS(DWORD dwOS)
    2. .idata:01001054                 extrn __imp__IsOS@4:dword
    или (без отладочных символов)

    Код (Text):
    1. .idata:01001054                 extrn SHLWAPI_437:dword ; IsOS
    Вот и спрашивается, разве нельзя вызвать функцию по ее экспортируемому номеру?
     
  2. irrona

    irrona Member

    Публикаций:
    0
    Регистрация:
    26 май 2004
    Сообщения:
    178
    Адрес:
    Тирасполь
    Scholium

    Вызвать ф-цию по ординалу можно, но для этого опять таки нужно вызвать ф-цию
    Код (Text):
    1. GetProcAddress(
    2.     HMODULE hModule
    3.     LPCSTR lpProcName)
    где:
    hModule - указатель на длл, в которой находится вызываемая ф-ция (можно получить вызовом LoadLibrary или GetModuleHandle
    lpProcName - указатель на строку, содержащую название или ординал вызываемой ф-ции.
     
  3. irrona

    irrona Member

    Публикаций:
    0
    Регистрация:
    26 май 2004
    Сообщения:
    178
    Адрес:
    Тирасполь
    Но только Microsoft не рекомендует пользоваться ординалами, т.к. не гарантируют их сохранность в следующих версиях функций. У меня в личной практике, кстати, то же самое. Нередко я сам вставляю в свои длл новые функции в середину списка и меняю последовательность ординалов.
     
  4. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Хорошо, согласен. Как косвенный метод это годится. Но самое смешное, что дизассемблируемая функция не пользуется этим вызовом. «Вскрытие» (без отладочных символов) показывает вызов вида:

    Код (Text):
    1. .idata:01001054                 extrn SHLWAPI_437:dword     ; IsOS
    2. . . .
    3. .text:0100200E                 call    SHLWAPI_437     ; IsOS
    Т.е. если бы этот код понимал бы компилятор, то не было бы проблем. Поэтому, я просто хочу убедиться, что подобным образом писать нельзя. Однако, как-то же подобный код скомпилировали?
     
  5. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    «Microsoft не рекомендует», но сам собственных рекомендаций не придерживается. Данный пример взят из дизассемблированного файла control.exe, где Вы сами можете убедиться в отсутствии функции GetProcAddress. Каким же образом «отцу американских программистов» удается обойтись без вычисления адреса функции SHLWAPI.#437? Может быть, он тупо вбивает в нужное место готовый адрес, на которое обращение идет по ссылке, типа:

    Код (Text):
    1. SHLWAPI_437       DD 9E14DB77
    2. . . .
    3. call    SHLWAPI_437     ; IsOS
    А поскольку Microsoft распространяет собственные файлы операционных систем или их сервис паков сам, то ему ничего не стоит обеспечить совместимость версий?
     
  6. irrona

    irrona Member

    Публикаций:
    0
    Регистрация:
    26 май 2004
    Сообщения:
    178
    Адрес:
    Тирасполь
    К сожалению (или к счастью) не всё, показанное дебаггером, можно снова скомпилировать.
     
  7. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Позвольте с Вами не согласиться :) !

    Вот пример.Файл control.exe предложен в общем списке из нескольких файлов для полной перекомпиляции. Лично я его уже перекомпилировал полностью и он у меня вполне корректно работает. Однако дело в том, что реально код с вызовом функции SHLWAPI.#437 (реализованном у меня тупо, как в отладчике) не используется вовсе. Соответственно ее абсолютные адреса на разных машинах с разными ОСями будут разными. Очень похоже, что мелкософт здесь применяет процедуру биндинга (binding) (см. статью Криса Касперски « Сверхбыстрый импорт API-функций». Если это так, тогда вопрос снимается. Будем, вместо вызова функции по ординалу, заниматься либо биндингом, либо использовать Ваш метод явного вызова функции GetProcAddress, если скорость импорта не имеет значения.
     
  8. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Scholium
    Вы бы спецификацию PE что ли почитали. А то текста много, а смысла минимум.
    Статический импорт по ординалу настолько же легален и прост, насколько и по имени.
     
  9. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Scholium
    Из библиотек можно статически импортировать по ординалам, и ничего сложного в этом нет. Я так делал правда только в masm`e.

    Самый "тупенький" способ.

    1. Создаёшь shlwapi.asm и shlwapi.def и bat`ник для компиляции shlwapi.dll из этих файлов.
    2. shlwapi.asm заполняешь пустышками с именами нужных функций.
    Код (Text):
    1. SHAllocShared proc arg1:DWORD,arg2:DWORD,arg3:DWORD
    2. SHAllocShared endp
    3.  
    4. SHALWAPIBYORD proc
    5. SHALWAPIBYORD endp
    6. ...
    3. В shlwapi.def у всех безымянных функций после @ явно устанавливаешь номера ординалов.
    Код (Text):
    1. EXPORTS
    2. SHAllocShared
    3. SHALWAPIBYORD @437
    4. ...
    4. Компилируешь и получаешь shlwapi.lib.
    5. Создаёшь shlwapi.inc заполняешь прототипами.
    Код (Text):
    1. SHAllocShared PROTO :DWORD,:DWORD,:DWORD
    2. SHALWAPI437 PROTO
    Всё shlwapi.lib и shlwapi.inc готовы.
     
  10. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    А зачем мне PE и COFF спецификация? Там тоже «текста много, а смысла минимум» :) . С таким же успехом меня можно послать на сайт Microsoft. Все что мне нужно это пару строчек примера «статического импорта по ординалу». Не хотите говорить прямо, не надо. Поищем в другом месте. Не все на форуме «начинающих» «законченные» :) .
     
  11. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Спасибо! Я поэкспериментирую с Вашим способом. Только чует мое одно пикантное, не побоимся этого слова импозантное место :) , что существует и более простой способ, например, через директиву extrn, вместо директивы proto. Что ж, будем искать дальше :) .

    P.S. Похоже, что Ваш способ описан в http://www.winasm.net/forum/index.php?showtopic=1102
     
  12. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Scholium
    Существует. В fasm (в masm очень многое осуществляется через "импозантное место"): достаточно при описании импортируемой функции вместо имени указать число.
     
  13. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    Согласен! И не только в FASM. Только вот «Ида» предпочитает работать с MASM32 версии 5.1. А куда ныне без «Иды» :) ?
     
  14. baldr

    baldr New Member

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

    Можно сочинить собственную библиотеку, используя только .Def файл. К примеру, такой:
    Код (Text):
    1. LIBRARY Kernel32
    2. EXPORTS
    3.   ExitProcess@4 = ExitProcess   @183
    Его надо скормить утилите Lib:
    Код (Text):
    1. lib /machine:x86 /def:Kernel32.Def
    В результате имеем .Lib (и ненужный .Exp, но это уже издержки), при использовании которой ExitProcess() импортируется по ординалу.

    extrn используется для других целей (да и где там можно указать имя DLL?), хотя в процессе импорта участвует.

    В fasm всё действительно проще: там структуры PE, ответственные за импорт, формируются макросами.
     
  15. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    l_inc
    Вас чем-то не устраивает это самое место?

    К этому нужно относиться с пониманием. masm = Microsoft Assembler. Здесь не трудно тогадаться, что этот инструмент создавался уже довольно давно для ооочень массовой компиляции разных .exe`шек и .dll`лок, о компиляции PE файлав без стадии .obj даже и не помышляли. Их могло быть 50, 100 или больше.
    Слишком большое это удовольствие, если речь идёт о большом количестве модулей, которые друг из друга чего-то импортируют. В способе который я предложил информация об ординалах храниться в .lib файле, а ординал указывается только в .def файле и больше нигде.

    Scholium
    Когда писал ошибся.
    В .def файле можно ещё писать NONAME

    SHALWAPIBYORD @437 NONAME

    Но лучше почитай в msdn описание EXPORTS`A

    И прототип объявлять

    SHALWAPIBYORD PROTO

    baldr
    Можно. Но тогда загиб с invokaми может выйти.
     
  16. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    s_d_f
    :) Место устраивает. Только я его использую по назначению, а не компилирую через него. :)
    Всегда считал, что это означает "macro assembler". Но это не суть. К инструменту я отношусь с пониманием. К пользователям без особого.
    Это удовольствие опциональное. У fasm есть оптимальные решения для массового использования равно как и для единичной потребности в одной функции.

    Прошу прощения. Продолжать святую дискуссию не хочется.
     
  17. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    А вызывается функция по ординалу каким образом?

    Я, конечно, понимаю что, создав собственный lib-файл, можно выкрутиться из положения. Только для больших проектов этот путь не кажется таким уж привлекательным. По логике вещей, есть экспортируемая функция по имени или ординалу. Существуют простые способы импортирования этой функции по имени через директивы extrn (=extern) – стиль «Иды» или Ильфака Гильфанова, либо proto – стиль Iczelion’a. Логично предположить, что должны быть аналогичные способы импортирования по ординалу (без создания собственных lib-файлов). Если таких способов в MASM32 нет, это печально, но не смертельно. Тогда уже будем писать собственные lib-файлы (это что получается, мы должны будем делать работу за Microsoft :) ?).

    Читаем в документации:

    Про «другие цели» речь не идет. А имя указывается в директиве: includelib Lib\shlwapi.lib. Сами lib-файлы можно найти либо в бесплатном пакете MASM32 (сейчас уже v.10), либо в MS VS C++.

    У меня есть упаковки для шести различных видов ассемблеров, но из-за «Иды» меня устраивает только MASM32.
     
  18. Scholium

    Scholium New Member

    Публикаций:
    0
    Регистрация:
    5 мар 2009
    Сообщения:
    96
    MASM это действительно Macro Assembler! Когда Microsoft’у надо указать собственное имя они используют аббревиатуру MS. Хотя компилятор MASM’a создан в Microsoft’е.
     
  19. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Scholium
    Спасибо. Это была ирония. Мне не было нужно подтверждение. :)
     
  20. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    Во время ассемблирования без параметра /nologo первая строчка.
    Внимательно, что-то никогда не читал.