идентификация функции в дизассемблированном коде
Как можно идентифицировать название функции?
Есть простенькая программа, выводящая MessageBox, исходник на ассемблере:
Код:
.586P
.MODEL FLAT,STDCALL
includelib f:\masm32\lib\user32.lib
EXTERN MessageBoxA@16:NEAR
;сегмент данных
_DATA SEGMENT
TEXT1 DB 'No problem!',0
TEXT2 DB 'Message',0
_DATA ENDS
;сегмент кода
_TEXT SEGMENT
START:
PUSH OFFSET 0
PUSH OFFSET TEXT2
PUSH OFFSET TEXT1
PUSH 0
CALL MessageBoxA@16
RETN
_TEXT ENDS
END START
.MODEL FLAT,STDCALL
includelib f:\masm32\lib\user32.lib
EXTERN MessageBoxA@16:NEAR
;сегмент данных
_DATA SEGMENT
TEXT1 DB 'No problem!',0
TEXT2 DB 'Message',0
_DATA ENDS
;сегмент кода
_TEXT SEGMENT
START:
PUSH OFFSET 0
PUSH OFFSET TEXT2
PUSH OFFSET TEXT1
PUSH 0
CALL MessageBoxA@16
RETN
_TEXT ENDS
END START
Пытаюсь дизассмеблировать с помощью dumpbin.exe (та, что в VC идет), получаю код:
Код:
00401000: push 0
00401002: push 40300Ch
00401007: push 403000h
0040100C: push 0
0040100E: call 00401014
00401013: ret
00401014: jmp dword ptr ds:[00402000h]
00401002: push 40300Ch
00401007: push 403000h
0040100C: push 0
0040100E: call 00401014
00401013: ret
00401014: jmp dword ptr ds:[00402000h]
Как можно понять, что call в данном случае вызывает MessageBoxA?
по адресу 00401014 у нас идет jmp на 00402000, а где можно найти этот адрес?
Заранее большое спасибо
можно конечно сразу было в программе писать call 7E3A07EAh (для WinXP SP3), но на каждой системе адрес функций будет(может) отличаться поэтому импорты и придумали.
а call 00401014 + jmp это переходник, который делает компилятор
почитайте это или на русском
//а чтобы было сразу видно что и почем возьмите нормальный дизассемблер или в отладчике гоняйте
Смотрел через OllyDbg, но кое-что не понимаю, откуда она берет.
Чтобы лучше объяснить, приведу другой пример:
Код ассемблера:
Код:
.586P
.MODEL FLAT,STDCALL
includelib c:\masm32\lib\user32.lib
MessageBoxA PROTO ,:DWORD, :DWORD, :DWORD, :DWORD
MessageBoxW PROTO ,:DWORD, :DWORD, :DWORD, :DWORD
;сегмент данных
_DATA SEGMENT
TEXT1 DB 'TITLE',0
TEXT2 DB 'Hello World',0
_DATA ENDS
;сегмент констант
_CONST SEGMENT
NULL equ 0
MB_OK equ 0
_CONST ENDS
;сегмент кода
_TEXT SEGMENT
START:
INVOKE MessageBoxA, NULL, ADDR TEXT1, ADDR TEXT2, MB_OK
INVOKE MessageBoxW, NULL, ADDR TEXT1, ADDR TEXT2, MB_OK
RETN
_TEXT ENDS
END START
.MODEL FLAT,STDCALL
includelib c:\masm32\lib\user32.lib
MessageBoxA PROTO ,:DWORD, :DWORD, :DWORD, :DWORD
MessageBoxW PROTO ,:DWORD, :DWORD, :DWORD, :DWORD
;сегмент данных
_DATA SEGMENT
TEXT1 DB 'TITLE',0
TEXT2 DB 'Hello World',0
_DATA ENDS
;сегмент констант
_CONST SEGMENT
NULL equ 0
MB_OK equ 0
_CONST ENDS
;сегмент кода
_TEXT SEGMENT
START:
INVOKE MessageBoxA, NULL, ADDR TEXT1, ADDR TEXT2, MB_OK
INVOKE MessageBoxW, NULL, ADDR TEXT1, ADDR TEXT2, MB_OK
RETN
_TEXT ENDS
END START
После дизассемблирования через dumpbin.exe (использую эту программку, потому что мне нужен консольный вывод, чтобы парсить его своей программой):
Код:
00401000: push 0
00401002: push 403006h
00401007: push 403000h
0040100C: push 0
0040100E: call 00401028
00401013: push 0
00401015: push 403006h
0040101A: push 403000h
0040101F: push 0
00401021: call 0040102E
00401026: ret
00401027: int 3
00401028: jmp dword ptr ds:[00402004h]
0040102E: jmp dword ptr ds:[00402000h]
00401002: push 403006h
00401007: push 403000h
0040100C: push 0
0040100E: call 00401028
00401013: push 0
00401015: push 403006h
0040101A: push 403000h
0040101F: push 0
00401021: call 0040102E
00401026: ret
00401027: int 3
00401028: jmp dword ptr ds:[00402004h]
0040102E: jmp dword ptr ds:[00402000h]
Соответствия получаются
jmp dword ptr ds:[00402004h] -> MessageBoxA
jmp dword ptr ds:[00402000h] -> MessageBoxW
Как на основании 00402004 и 00402000 вычислять имя функции? Если заглянуть в .rdata, то увидим:
Код:
00402000: 4E 20 00 00 40 20 00 00 00 00 00 00 34 20 00 00 N ..@ ......4 ..
00402010: 00 00 00 00 00 00 00 00 5C 20 00 00 00 20 00 00 ........\ ... ..
00402020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00402030: 00 00 00 00 4E 20 00 00 40 20 00 00 00 00 00 00 ....N ..@ ......
00402040: B1 01 4D 65 73 73 61 67 65 42 6F 78 41 00 B8 01 ±.MessageBoxA.?.
00402050: 4D 65 73 73 61 67 65 42 6F 78 57 00 75 73 65 72 MessageBoxW.user
00402060: 33 32 2E 64 6C 6C 00 00 32.dll..
00402010: 00 00 00 00 00 00 00 00 5C 20 00 00 00 20 00 00 ........\ ... ..
00402020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00402030: 00 00 00 00 4E 20 00 00 40 20 00 00 00 00 00 00 ....N ..@ ......
00402040: B1 01 4D 65 73 73 61 67 65 42 6F 78 41 00 B8 01 ±.MessageBoxA.?.
00402050: 4D 65 73 73 61 67 65 42 6F 78 57 00 75 73 65 72 MessageBoxW.user
00402060: 33 32 2E 64 6C 6C 00 00 32.dll..
Если в импортируемые библиотеки:
Код:
user32.dll
402000 Import Address Table
402034 Import Name Table
0 time date stamp
0 Index of first forwarder reference
1B8 MessageBoxW
1B1 MessageBoxA
402000 Import Address Table
402034 Import Name Table
0 time date stamp
0 Index of first forwarder reference
1B8 MessageBoxW
1B1 MessageBoxA
Ни .rdata, не импортируемые функции не дают мне понять, что же такое 00402004 и 00402000. Куда мне тыкнуться с ними, чтобы увидеть на какие функции осуществляется переход?
Почитай про PE-формат и всё сразу станет понятно.Сам давно не занимался,поэтому ничего особо посоветовать не могу.Можно почитать Microsoft'овский оригинал
Вот и получается, то ли лыжи не едут, то ли я...
по адресам 00402000h и 00402004h лежит RVA (Relative Virtual Address), который указывает на ASCIIZ-строку с двухбайтовым префиксом (hint) и содержит имя функции. но не всегда так будет, зависит от компилятора\линкера, все по разному импорты строят.
в данном случае:
по адресу 00402000h лежит DWORD - 0000204Eh - это RVA.
если теперь к нему прибавить 400000h (ImageBase) получим адрес 0040204Eh и теперь + 2 (отбросим hint) = 00402050 - указатель на строку MessageBoxW
Цитата:
но не всегда так будет, зависит от компилятора\линкера, все по разному импорты строят.
А образ процесса формируется посредством черной магии, формат то неизвестен.
PS: Читаем про динамическое связывание и загрузку в антернетах
Вы это к чему написали? И, пожалуйста, если уже начали, то внятно закончите и выразите что хотели этим сказать.
всё дело в том, что ImportLookUp (OriginalFirstThunk) (IMAGE_IMPORT_DIRECTORY+00h) может указывать на ту же самую табличку что и AddresTableRva (FirstThunk) (IMAGE_IMPORT_DIRECTORY+10h) или может быть 0x00000000 (что является аналогом) -в таком случае берем имена функций из AddresTableRva (IAT)
(IMAGE_IMPORT_DIRECTORY в примере идет по адресу - 0040200Ch)
либо ImportLookUp может указывать на отдельную табличку и тогда уже из неё берутся имена\ординалы, но адреса кладутся всё также в AddresTableRva. часто в таком варианте IAT уже заполняется какими-нибудь адресами верными для системы на которой происходила компиляция, но в момент загрузки они конечно же перезаписываются
да, формат описан, но собирать файлы мы можем довольно вариабельно, часто до всяких извращений доходит
и еще, я не обратил внимания сначала в примере что выше был
Import Address Table (AddresTableRva) и Import Name Table (ImportLookUp) - это две разные таблички, но с одинаковыми данными - это 3 DWORD: 0000204E, 00002040, 00000000 так что тут и так и так можно рассматривать
и если допустим заменить 402034 Import Name Table на 0 Import Name Table то программка как работала так и будет работать потому что будет использована Import Address Table
Теперь всё понял :) Осталось только предусмотреть другие возможные варианты, знать бы только все возможные
написано уже, два по сути их варианта откуда брать имена функций. по умолчанию смотрим Import Name Table, а если он == 0 то берем из Import Address Table