Опыт дизассемблирования большой .com программы - Особенности и ошибки дизассемблера DisDoc 2.3

ОГЛАВЛЕНИЕ

 

Особенности и ошибки дизассемблера DisDoc 2.3

К сожалению, DisDoc 2.3 совершает ошибки, иногда регулярные, а иногда редкие, коварные и даже подлые. Самая противная ошибка - случайный пропуск данныхвстречается довольно редко. Начнем с того, что встречается очень часто.

1. EQU - кто тебя выдумал?

В коде, выданном дизассемблером, часто попадаются такие загадочные куски:

;<00465>
s12 proc near
d0046c equ 00046ch
cmp bx,5ah ;00465

Каков смысл присвоения d0046c equ 00046ch ? Чтобы выяснить это, нужно отыскать d0046c в тексте. В нашем случае элемент данных d0046c встречается очень далеко от своего первого появления - в подпрограмме s321

mov ax,0040h ;06257 ;<es = 0040> mov es,ax ;0625a mov al,BYTE PTR es:d0046c ;0625c sti ;06260 ;Turn ON Interrupts b06261: cmp al,BYTE PTR es:d0046c ;06261 jz b06261 ;06266 ;Jump if equal (ZF=1) mov al,BYTE PTR es:d0046c ;06268 dec cx ;0626c jnz b06261 ;0626d ;Jump not equal(ZF=0) pop ax ;0626f out 61h,al ;06270 ;060-067:8024 keybrd contrlr ;<es = 0000> pop es ;06272 ret ;06273 s321 endp Рис.6

При виде этого текста возникает догадка, что здесь идет зваимодействие с областью данных BIOSa . Действительно, в регистр es засылается число 40, т.е. es будет указывать на адрес 400 - начало этой области. Тогда следующий вопрос - каков смысл адреса 046сh? Легко выяснить, что по этому адресу находится счетчик прерываний от таймера. Если это так, то фрагмент, приведенный на рис.6, обретает смысл - он дает задержку на число прерываний от таймера, заданное в регистре cx. Но если все сказанное верно, то d0046c должно быть равно не 46сh, а просто 6сh! И действительно, если посмотреть подпрограмму s321 отладчиком, то станет ясно, что вместо mov al,BYTE PTR es:d0046c в тексте должно стоять mov al,6ch.

Итак, чтобы исправить эту ошибку, необходимо:

  • Удалить из начала подпрограммы s12 присвоение d0046c equ 00046ch
  • Переписать приведенный на рис.6 фрагмент s321 следующим образом: mov ax,0040h ;06257 ;<es = 0040> mov es,ax ;0625a mov al,BYTE PTR es:006ch ;0625c sti ;06260 ;Turn ON Interrupts b06261: cmp al,BYTE PTR es:006ch ;06261 jz b06261 ;06266 ;Jump if equal (ZF=1) mov al,BYTE PTR es:006ch ;06268 dec cx ;0626c jnz b06261 ;0626d ;Jump not equal(ZF=0) pop ax ;0626f out 61h,al ;06270 ;060-067:8024 keybrd contrlr ;<es = 0000> pop es ;06272 ret ;06273 s321 endp

    Рассмотрим второй пример. В коде, выданном дизассемблером, встретился такой кусок:

    ;<0074e> 
    s22 proc near
    d0076a equ 00076ah
    d00771 equ 000771h
    call s24 ;<00791> ;0074e
    ...............
    b0076a: push cx ;0076a
    call s25 ;<0086b> ;0076b
    call s23 ;<00776> ;0076e
    pop cx ;00771
    dec bx ;00772

    Поиск элемента данных d0076a окончился неудачей. А d00771 встретился в таком фрагменте:

    	..................................... 
    mov BYTE PTR ds:b0076a,51h ;0080b
    mov BYTE PTR ds:d00771,59h ;00810
    ......................................

    Здесь явно идет модификация кода подпрограммы s22. Значит, необходимо заменить d00771 на b00771, пометить этой меткой соответствующую инструкцию в s22 и удалить присвоения

      d0076a equ	00076ah
    d00771 equ 000771h

    Исправленный фрагмент s22 будет выглядеть так:

    ;<0074e> 
    s22 proc near
    call s24 ;<00791> ;0074e
    ......................................................
    b0076a: push cx ;0076a
    call s25 ;<0086b> ;0076b
    call s23 ;<00776> ;0076e
    b00771: pop cx ;00771
    dec bx ;00772
    ..............................................
    mov BYTE PTR ds:b0076a,51h ;0080b
    mov BYTE PTR ds:b00771,59h ;00810
    ................................................

    Рассмотрим еще один пример. В начале s32 встретились уже знакомые псевдооператоры:

    ;<00bf7> 
    s32 proc near
    d00c1c equ 000c1ch
    d00c1e equ 000c1eh

    Если посмотреть в область со смещениями, близкими к с1с, то там окажется кусок повисшего кода, который может быть только данными:

    	....................................... 
    or al,BYTE PTR [bp+di] ;00c14
    add WORD PTR [bx+di],ax ;00c16
    add BYTE PTR [bx+si],al ;00c18
    add BYTE PTR [bx+si],al ;00c1a
    mov di,1306h ;00c1c
    add ax,06c0h ;00c1f
    ......................................

    Теперь нужно поискать идентификаторы d00c1c и d00c1e в тексте, выданном дизассемблером. Очень быстро можно найти фрагменты типа: mov WORD PTR ds:d00c1c,ax, mov WORD PTR ds:d00c1e,ax. Значит, ошибка дизассемблера состоит в том, что он перепутал данные и команды и на этой почве сделал два неправильных присваивания, equ, попавших в начало подпрограммы s32.

    Исправления будут заключаться в следующем:

  • Убрать из начала подпрограммы s32 два псевдооператора equ.
  • Переписать коды на рисунке 7 следующим образом:
    d00c14	db 0a,03,01,01,00,00,00,00  ;00c14 
    d00c1c db 0bf,06 ;00c1c
    d00c1e db 13,05,0c0,06 ;00c1e

    В заключение рассмотрим совсем простенький фрагмент кода:

    ;<01252> 
    s39 proc near
    d0125d equ 00125dh
    d0125f equ 00125fh
    dec bh ;01252
    jz b0124f ;01254 ;Jump if equal (ZF=1)
    xor ah,ah ;01256
    shl al,1 ;01258 ;Multiply by 2's
    rcl ah,1 ;0125a ;CF<--[HI .. LO]<--CF
    ret ;0125c
    ;-----------------------------------------------------
    add BYTE PTR [bx+si],al ;0125d
    add BYTE PTR [bx+si],al ;0125f
    s39 endp

    Укажем без комментариев, что подпрогромма s39 должна выглядеть так:

    ;<01252> 
    s39 proc near
    dec bh ;01252
    jz b0124f ;01254 ;Jump if equal (ZF=1)
    xor ah,ah ;01256
    shl al,1 ;01258 ;Multiply by 2's
    rcl ah,1 ;0125a ;CF<--[HI .. LO]<--CF
    ret ;0125c
    ;-----------------------------------------------------
    d0125d db 00,00 ;0125d
    d0125f db 00,00 ;0125f
    s39 endp

    В заключение этого пункта подведем итоги. Значки equ называют всевдооператорами. Если говорить о дизассемблере DisDoc 2.3, то это название удивительно точное. Если в тексте встретится equ - то ошибка рядом. Между тем, иногда DisDoc 2.3 употребляет equ вполне корректно. Так что будьте бдительны и не дайте себя обмануть.