呵,好久沒寫
csdn
文章了,來湊個熱鬧。最近我閱讀了楚狂人
,wowocock
寫的《天書夜讀》試讀本,對
c反彙編感觸頗深,書中有一例演算法反彙編,其對彙編的閱讀確實富有挑戰,而該書中也未詳解,在此,我謹將此例詳細分析如下,幫助大家更好理解
c反彙編**,若有任何錯誤,請大家批評指正!
該例的要求是求兩個
3x3矩陣的乘積,其
c源**如下:
int myfunction(int a[3][3], int b[3][3], int c[3][3])
return 0;
}相當簡單吧?呵呵,那麼希望你看過了下面的反彙編**後,還能這樣樂觀。
如下的反彙編**,據原書作者,在
vc2003
,debug
模式下得到,同時我在
vc2008pro
下得到彙編**完全相同,可以通用,但此處引用原書中的**。
00411a3e mov dword ptr [i],0
00411a45 jmp myfunction+30h (411a50h)
00411a47 mov eax,dword ptr [i]
00411a4a add eax,1
00411a4d mov dword ptr [i],eax
00411a50 cmp dword ptr [i],3
00411a54 jge myfunction+0aeh (411aceh)
00411a56 mov dword ptr [j],0
00411a5d jmp myfunction+48h (411a68h)
00411a5f mov eax,dword ptr [j]
00411a62 add eax,1
00411a65 mov dword ptr [j],eax
00411a68 cmp dword ptr [j],3
00411a6c jge myfunction+0a9h (411ac9h)
00411a6e mov eax,dword ptr [i]
00411a71 imul eax,eax,0ch
00411a74 mov ecx,dword ptr [a]
00411a77 mov edx,dword ptr [j]
00411a7a mov esi,dword ptr [b]
00411a7d mov eax,dword ptr [ecx+eax]
00411a80 imul eax,dword ptr [esi+edx*4]
00411a84 mov ecx,dword ptr [i]
00411a87 imul ecx,ecx,0ch
00411a8a mov edx,dword ptr [a]
00411a8d mov esi,dword ptr [j]
00411a90 mov edi,dword ptr [b]
00411a93 mov ecx,dword ptr [edx+ecx+4]
00411a97 imul ecx,dword ptr [edi+esi*4+0ch]
00411a9c add eax,ecx
00411a9e mov edx,dword ptr [i]
00411aa1 imul edx,edx,0ch
00411aa4 mov ecx,dword ptr [a]
00411aa7 mov esi,dword ptr [j]
00411aaa mov edi,dword ptr [b]
00411aad mov edx,dword ptr [ecx+edx+8]
00411ab1 imul edx,dword ptr [edi+esi*4+18h]
00411ab6 add eax,edx
00411ab8 mov ecx,dword ptr [i]
00411abb imul ecx,ecx,0ch
00411abe add ecx,dword ptr [c]
00411ac1 mov edx,dword ptr [j]
00411ac4 mov dword ptr [ecx+edx*4],eax
00411ac7 jmp myfunction+3fh (411a5fh)
00411ac9 jmp myfunction+27h (411a47h)
暈了?呵呵,如果你第一遍看就可以完全讀通,那我只好對您orz了= =,希望你們這些天才別來砸我場子啊^_^
首先先簡單提一下,c語言中多維陣列的儲存方式。其儲存的規則是,列優先於行,也就是:若有a[3][3],則順序是a[0][0], a[0][1], a[0][2], a[1][0], a[1][1], a[1][2],a[2][0], a[2][1], a[2][2]。其實所謂的多維陣列這樣高階語言才有的資料結構,在底層實現中,無非就是一片連續分配的一維記憶體空間而已,大家千萬別看得太過神秘了。
接著要提一下c語言中指標在彙編實現時的情況。彙編可以說把高階語言中幾乎所有現象的本質都暴露了出來。現觀察一下以下情況:對於int a[3][3]定義的多維陣列變數a,a本身表示的是該矩陣首行的位址,而a[0](或者*a)是首行首列元素的位址,兩者是不同的指標型別,然而很容易知道在位址數值上兩者是相等的,我們引申開來,比如我們要訪問a[m][n],我們還可以寫作*(*(a + m) + n),但由上段所述,實際上就是把a所含的位址再往後移m * 3 * 4(int佔4個位元組) + n * 4個位元組(此注:a + m不是a所在處後移m個位元組!可以把a理解為int[3]這種型別的指標,所以加了m,其實是加了12* m個bytes)就是所要訪問的記憶體,換句話說任何多維陣列總可以用乙個一維普通指標(比如使用指標型別強制轉換)完全訪問到,儘管在高階語言中這樣的做法是非常不明智且危險的。但彙編卻正是這麼做的!比如我們有c語句:a[i][j] = 2;,用c還可以寫作*(*(a + i) + j) = 2,而用彙編則可能是這樣,
mov eax, dword ptr [i] ;
把i的值讀入eax
imul eax, eax, 0ch ;
把eax乘以12,因為一行有三個int,3 * 4 = 12
lea ecx, a[eax] ;
相當於將a所含的位址與eax相加後存入ecx
mov edx, dword ptr [j] ;
把j的值讀入edx
mov dword ptr [ecx + edx * 4], 2 ; ecx + edx
就是a[i][j]的位址
我們嘗試省略暫存器的中間步驟、並逐式代入的話,就直接可以推出這個式子:
movdword ptr [a + i * 12 + j * 4], 2
;這個式子和上述的完全一致。
最後提到的就是c中for語句的彙編實現。
for (init; condition; expr)
loop-body
大家都應當很清楚,執行的順序是:先init初始化迴圈變數,接著判斷condition,若滿足則執行迴圈體(loop-body),再執行expr,判斷條件是否滿足,滿足則執行loop-body……彙編**是非常機械地對應著上述過程,例:
for (i = 0; i < 5; ++i)
loop-body
則相應彙編**:
; 初始化**
mov dword ptr [i], 0
jmp(*); 無條件跳轉到(*)所在**,這裡不列出具體的**位址了
; 這段就是expr,類似用乙個中間變數(只不過這裡是暫存器)的辦法來給i加一
(#)mov eax, dword ptr [i]
add eax, 1
mov dword ptr [i], eax ;
這段則是判斷條件,condition
(*)cmp dword ptr [i], 5 ;
比較i和5
jge(**); jge
表示jump to(**)when i is greater than or equal to 5,大於等於5
; 跳轉,(**)指向的是for迴圈後面接著的**,也就是跳出迴圈
; 以下段省略,是迴圈體loop-body…
jmp(#);
無條件跳轉到expr,並進而判斷條件
; 以下是for迴圈以外的**
(**)…
C反彙編例項(詳細註解版)(三)
上次我分析了一下,debug 模式下反彙編後的演算法部分 天才的您可能覺得不算糟,想再搞點花樣,那麼本文就能滿足你的需求。天書夜讀上其實還貼出來了 release 模式下的 它經過 vc編譯器 o2的優化,我初次看到反彙編 時,還真汗了一把。不過定下心來細細品位還是可以看懂的,儘管正如原書所說,連語...
C反彙編例項(詳細註解版)(三)
上次我分析了一下,debug 模式下反彙編後的演算法部分 天才的您可能覺得不算糟,想再搞點花樣,那麼本文就能滿足你的需求。天書夜讀上其實還貼出來了 release 模式下的 它經過 vc編譯器 o2的優化,我初次看到反彙編 時,還真汗了一把。不過定下心來細細品位還是可以看懂的,儘管正如原書所說,連語...
C反彙編例項(詳細註解版)(二)
那麼有了上述三方面的基礎,我們就可以來逐一解讀那段 傳奇 的彙編 了。初始化i 00411a3e mov dword ptr i 0 跳轉至條件判斷 00411a45 jmp myfunction 30h 411a50h 迴圈表示式,對i每輪加1 00411a47 mov eax,dword ptr...