Asm的魅力(二)

 Author:charme
Data:2009.9.4
Index:hi.baidu.com/charme000

廢話:
上一篇大牛牛們都說是出力不討好,呼呼!我還是一貫作風吧:一笑而過。
做個簡單的類比:
開發黑客軟件的人笑只會使用軟件入侵的菜菜膚淺,如果認可這個掛點的話,那研究asm本身實質的人就應該嘲笑利用asm搞逆向和破解的人了。

而實際上這是幾個不同的領域,所以沒有什麼喫力不討好的說法。
會寫軟件的人必然懂得入侵的原理,所以懂asm的人逆向只是個時間問題。

曾子曰:自作孽,不可活!
如果爲了湊篇精華,拼湊代碼科普出來,基本等於沒有創造!!!我不幹那樣的事情!!

還是相信女王大牛的教誨:這個世界永遠注重的是創造。

正文:
程序語言中的結構體是常見的訪問內存中存儲數據的一種方式,有點不必說了,大家接觸過結構的都明白。那麼asm中的結構用的當然也比較多。

結構可以嵌套,但是初學者必然的會有一些迷惑。

(一)明白結構訪問的本質
首先看一個C語言中的結構使用:

代碼:
Struct A
{
   Int charme;
}*a;

Struct B
{
  A c;
}*b

那麼我們常常見到這樣的訪問方法:b->c.charme
那麼->是個什麼東西?(到現在我都不知道這叫個什麼操作符,呼呼)爲什麼我們不寫成這樣的:b.c.charme?不就是訪問成員嘛!!!到這裏就是重點了,->操作符的本質是什麼?

再看一段程序:

代碼:
int  _ClientLogin( LPVOID _lpStruct)
{
  TranSocket  *stSock;
  ...
  int k = stSock->dwIndex;    //相當於取stSock.dwIndex 賦值給k
  ...
  return (0);
}

這是含笑大哥程序裏摘出來的一段,重點註釋部分。
看完這個我們可能就明白了,stSock只是一個指針,指向TranSocket結構的一個指針。這樣做只是爲TranSocket結構預留了一個空間,方便以後的初始化。
這就是->的本質!!!!!!!!!!!!!!!!!!這個必須搞清楚的,搞清楚了這個才能理解怎麼嵌套。

(二)asm中的結構嵌套的錯誤
那麼就之前的那個結構嵌套。很多人可能會這樣寫(我之前也是這麼寫的,呼呼):

代碼:
.386
.model flat,stdcall
option casemap:none

include windows.inc
include kernel32.inc
include user32.inc
includelib user32.lib
includelib kernel32.lib

aa struct
   charme dd ?
aa ends

bb struct
    charme1 dd ?
    cc aa <>
    
bb ends

.data?
p2 bb <>  ;;;;;;;注意
p1 aa <>  ;;;;;;;;注意

szEAX      db 8 dup('0'),0

strr1 dd ? 
.data
strr db "charme",0
.code
ExchangCode  proc w32BitCode:DWORD,lpCode:DWORD
    push  ebx
    push  ecx
    push  esi
    mov  esi,lpCode
    mov  eax,w32BitCode
    mov  ecx,8
    @@nextchar:  push  ecx
    rol  eax,4
    mov  ebx,eax
    and  ebx,0000000Fh
    cmp  bl,09
    jle  @@char0_9
    add  bl,07h
    @@char0_9:  add  bl,30h
    mov  BYTE PTR [esi],bl
    inc  esi
    pop  ecx
    loop  @@nextchar
    pop  esi
    pop  ecx
    pop  ebx
    ret
ExchangCode  endp 
main proc
    mov p1.charme,20h
    mov ebx, p2.cc.charme
        
    push offset szEAX
    push ebx
    call ExchangCode
    
    push 40h
    push offset strr
    push offset szEAX
    push 0
    call MessageBox
    
    
    ;push 03e8h
    ;call Sleep
    
    push 0
    call ExitProcess
main endp
end main

asm中沒有->這樣的操作符,那怎麼實現b->c.charme這樣的訪問呢?只好是像上面這樣寫了
結果顯示p2.cc.charme的值是0,意圖來看應該是顯示20h。
錯誤在哪裏?
OD調試看看,可以很清楚的看到,內存空間中:
P1.charme--------------------------0400xxxxh
Charme1
P2.cc.charme----------------------0400yyyyh
根本就是兩個變量,怎麼可能訪問到呢,,照這樣看來我們這樣修改下:
mov ebx, p2.cc.charme+4,就輸出20h,但實際上這只是投機,只是跳過了charme1的空間,訪問了下p1.charme,一點也不能通用。

(三)重寫asm結構嵌套
之所以出現上面的錯誤,實際上還是因爲沒有真正的理解了結構訪問成員的本質。Asm的魅力在於最大限度的接近人的思維,所以他很靈活,但是靈活的話就需要你掌握本質。否則靈活的旁邊就是混亂。(我也搞不清楚這倆詞到底有啥區別,呼呼)

那麼在來看看

代碼:
.386
.model flat,stdcall
option casemap:none

include D:/MASMPlus/Include/windows.inc
include D:/MASMPlus/Include/kernel32.inc
include D:/MASMPlus/Include/user32.inc
includelib D:/MASMPlus/Lib/kernel32.lib
includelib D:/MASMPlus/Lib/user32.lib


X struct
  a  dd  10h
  b  db  10 dup(?)
X ends

Y struct
  cc  dd  ?
  d  dd  ?
  e  db  ?
Y ends

M struct
  p  X  <>
  q  Y  <>
M ends

.data
stMy  M  <>

szEAX      db 8 dup('0'),0
szTitle db "charme",0

.code
ExchangCode  proc w32BitCode:DWORD,lpCode:DWORD
    push  ebx
    push  ecx
    push  esi
    mov  esi,lpCode
    mov  eax,w32BitCode
    mov  ecx,8
    @@nextchar:  push  ecx
    rol  eax,4
    mov  ebx,eax
    and  ebx,0000000Fh
    cmp  bl,09
    jle  @@char0_9
    add  bl,07h
    @@char0_9:  add  bl,30h
    mov  BYTE PTR [esi],bl
    inc  esi
    pop  ecx
    loop  @@nextchar
    pop  esi
    pop  ecx
    pop  ebx
    ret
ExchangCode  endp
start:
  ;;正確做法一
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;lea   ebx, stMy ;;指向M結構的一個指針

  ;mov eax,(M ptr [ebx]).p.a;;必須加上M ptr因爲C裏面我們也看到了,實際上這個指向M結構的指針已idngyao確定是某個結構的,,因爲你lea   ebx, stMy只是初始化了一個指針,並沒有給你要初始化的結構分配空間
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;;正確在做法二
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;;mov ebx,sizeof M
  ;;mov eax,stMy[ebx].p.a
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;;正確做法三
  lea esi,stMy.p
  mov ebx,(X ptr [esi]).a
  
  
  
  push offset szEAX
  push ebx
 
  call ExchangCode
  
  push 40h
  push offset szTitle
  push offset szEAX
  push 0
  call MessageBox
  
  ret

end start

上面我用了三種方法,實際上本質是一樣的。

看看方法一和方法三,你可以對比出一個規律來:就是我們確定了指針,一定要明確他是要初始化一個什麼樣的結構。

方法二是可擴展的。
比如有一個叫做AA的結構,先假設他有兩個成員
我們定義一個結構數組:aa AA 3 dup(<0,0>)
那麼我們可以循環賦值,下面給一個示例:

代碼:
AA struct
   Ch1 dd ?
Ch2 dd ?
AA ends

.data
Align word
aa AA 3 dup(<0,0>)

.code
Start:
Mov edi,0
Mov ecx,3
Mov eax,1
Loopp:
Mov (AA ptr aa[edi]).ch1,eax
Mov (AA ptr aa[edi]).ch2,eax
Add edi,TYPE AA  ;;或者是SIZEOF AA,這裏獲得的大小就是結構數組裏單個數組的大小
Inc eax
Loop loopp

Push 0
Call ExitProcess
End start

當然我們也可以每個ch1 ch2初始化不同的值,含笑大哥提到了有兩種方法:
接受鍵盤輸入和預設初值爲一個數組

方法一和C語言裏的訪問形式就很接近了,但是很多人這樣寫:

代碼:
lea   ebx, stMy

mov eax, [ebx].p.a

這樣是不對的,,,[EBX]並不足以確定一個他屬於什麼樣的結構,當然結構裏的成員無從得知了。


不管怎麼樣吧!asm裏使用結構對程序的組織和效率是很有幫助的。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章