Опыт дизассемблирования большой .com программы
Страница 2. Проблема OFFSET


 

Фундаментальные проблемы

1. Проблема OFFSET'a

Предположим, что в тексте, который выдал дизаccемблер есть такой фрагмент:

movax,bx;1 
shlax,1 ;004bc;2
movsi,8429h;3
addsi,ax;4
pushWORD PTR [si];5

Что засылается в регистр si в третьей строчке - число 8429h или смещение некой метки? На этот вопрос позволяет ответить пятая строчка, из которой видно, что регистр si используется для косвенной адресации. Значит, исправленный фрагмент должен выглядеть следующим образом:

movax,bx;1 
shlax,1 ;004bc;2
movsi,OFFSET d08429 ;3
addsi,ax;4
pushWORD PTR [si];5

................................

d08429 db 0ff,0ff,0f6 ;8429
db0ff,0d8,0ff,0a6,0ff,60;0842c .....`

Возможно, здесь у многих возникнет сомнение - нужно ли заменять число на соответствующий OFFSET - ведь, казалось бы, в заново ассемблированной программе данные будут иметь то же смещение? К сожалению, это не так. Во первых, мы,как правило, не знаем, какой ассемблер применялся при транслировании оригинального текста, а коды, полученные с помощью разных ассемблеров будут иметь разную длину, что приведет к изменению смещений. Например, команда AND CX,0007h транслируется MASMом 5.1 и TASMом 1.01 как 83E107 и занимает 3 байтa. Но эта же команда может быть транслирована как 81E10700 и занимать 4 байта. Во-вторых, даже если смещение сохранится, программа не поддастся модификации, так как при вставке какого-либо фрагмента кода изменятся смещения и все "развалится". Итак, OFFSETы позволяют склеить программу, делают ее пригодной для модификации. Разобранный пример достаточно примитивен. Попробуем рассмотреть более сложные ситуации и первым делом исследуем фрагмент текста, выданный дизассемблером:

movbx,9006h;08f66 
b08f75:movWORD PTR ds:d087d0,bx;08f75
.................................
callWORD PTR cs:d087d0;08fc3
......................................
;-----------------------------------------------------
pushdx;09006
calls419 ;<099a3>;09007
moval,BYTE PTR [si];0900a
movBYTE PTR [si],0ffh;0900c
popdx;0900f
ret;09010
;-----------------------------------------------------

Здесь возникает тот-же вопрос - что такое 9006h в первой строчке фрагмента - смещение или просто число? Ответить на этот вопрос помогает информация, помещенная дизассемблером в поле комментариев. Мы уже говорили о том что числа, помещенные в этом поле, представляют собой смещения, которые имела инструкция в исходной программе, подвергаемой дизассемблированию. Нетрудно догадаться, что в приведенном фрагменте осуществляется косвенный вызов подпрограммы, и, следовательно, 9006h - это смещение, а не число. Фрагмент должен быть исправлен так:

movbx,OFFSET d09006 ;08f66 
......................................
;-----------------------------------------------------
d09006:pushdx;09006
......................................
ret;09010

Рассмотрим еще один пример косвенного вызова подпрограммы, в котором OFFSET попадает в область данных.

 
s390 proc near
..........................................................
movax,WORD PTR [bx+8792h];092c7
movWORD PTR ds:d087d2,ax;092cb
...........................................................
callWORD PTR cs:d087d2;093c8
ret;093d4
;-----------------------------------------------------
rorah,1 ;093d5;LO]-->[HI..LO]-->[HI
jbb093da ;093d7;Jump if < (no sign)
ret;093d9
b093da:incsi;093da
ret;093db
............................................................

Чтобы выяснить, что представляет собой 8792h, нужно посмотреть в область со смещениями, близкими к этому числу. Приведем соответствующий фрагмент, выданный дизассемблером:

 
d08790db00,00,0d5,93 ;08790 ......
.............................................................

Видно, что смещению 08792 соответствует слово 0d5,93. Теперь остается заметить, что со смещения 093d5 в исходной программе начинается фрагмент повисшего кода

 
rorah,1 ;093d5 !!!!!!;LO]-->[HI..LO]-->[HI
jbb093da ;093d7;Jump if < (no sign)
ret;093d9
b093da:incsi;093da
ret;093db

Следовательно,весь разобранный пример - это хитроумный косвенный вызов подпрограммы. Исправленный фрагмент должен выглядеть так:

 
s390proc near
..........................................................
movax,WORD PTR [bx+OFFSET d08792];092c7
movWORD PTR ds:d087d2,ax;092cb
...........................................................
callWORD PTR cs:d087d2;093c8
ret;093d4
;-----------------------------------------------------
d093d5:rorah,1 ;093d5;LO]-->[HI..LO]-->[HI
jbb093da ;093d7;Jump if < (no sign)
ret;093d9
b093da:incsi;093da
ret;093db
............................................................

d08790db00,00 ;08790 ......
d08792 dw OFFSET d093d5 ;08792

Здесь я предвижу большие возражения. Мне скажут, что все это можно интерпретировать иначе, что мои доказательства неубедительны и т.д. С этим я совершенно согласен. Более того, эти доказательства неубедительны и для меня. Гораздо сильнее убеждает то, что программа после ассемблирования работает! Дизассемблирование, как и отладка программ - процесс интуитивный. Опытный человек испытывает особое удовольствие от того, что его немотивированные догадки впоследствии подтверждаются. Как часто мысль, пришедшая в автобусе, во сне, в компании, в самой неподходящей обстановке - оказывается верной! Завершим этот пункт еще одним достаточно хитрым примером. В тексте, который выдал дизассемблер, встретился такой фрагмент:

movbx,4f71h ;0522b 
b0522e:popax;0522e
cmpax,bx;0522f
jnzb0522e ;05231;Jump not equal(ZF=0)
movBYTE PTR ds:d041f4,00;05233
pushax;05238
ret;05239
.................................
calls229 ;<04fc4>;04f71

Возникает все тот же вопрос - что такое 4f71h - число или смещение? Чтобы ответить на этот вопрос, нужно понять, что делает этот участок программы. Давайте попробуем в этом разобраться. Очевидно, из стека выталкивается число, сравнивается с 4f71h и если нет равенства, выталкивается следующее число. Если число равно 4f71h, то оно снова заталкивается в стек и происходит возврат из подпрограммы. Но куда? Ясно, что в то место, смещение которого было в исходной программе равно 4f71h. Как видно из текста, в этом месте стоял вызов подпрограммы s229. Значит, таким странным образом вызывается подпрограмма и 4f71h - это смещение! Исправленный фрагмент должен выглядеть так:

movbx, OFFSET d04f71 ;0522b 
b0522e:popax;0522e
cmpax,bx;0522f
jnzb0522e ;05231;Jump not equal(ZF=0)
movBYTE PTR ds:d041f4,00;05233
pushax;05238
ret;05239
.................................
d04f71:calls229 ;<04fc4>;04f71

 
« Предыдущая статья   Следующая статья »