XCHG#
xchg A,B #指令用於將A和B的值互換
將鼠標放置到0x4013d8
,這裡有個xor eax,eax
指令,此處演示修改0x4013d8
地址出的指令,點擊菜單欄Edit-Patch program-Assemble
。
彈出如下窗口。
輸入需要修改的指令,即可修改
xchg eax,esi
寫入了新的指令後,原先的函數被打斷了。
將0xc0
改寫為NOP
,NOP(NO OPERATION)
指令什麼也不做,沒有任何操作,修改後內容如下:
上述例子中,只涉及了一個字節,原先的數據類型是 db,值為0xc0h
。
但修改後,此時函數處於打斷狀態,可以在函數起始地址 0x4013d8 右鍵Create function(創建函數)
。
函數創建完成後,內容如下:
IDA 將前置的loc_(表示普通指令)
改為sub_(表示函數起點)
。現在它已經重新識別為一個函數,按空格鍵切換到圖形視圖。
再重新關注這裡的 XCHG 指令。假設EAX = 12345678,ESI = 55
,那麼指令執行之後 EAX = 55 而 ESI = 12345678。
在PATCH
菜單上有一個PATCHED BYTES
功能,能夠顯示所有修改的字節,同時也可以將修改取消。
xchg 指令也可以對一個寄存器的值和另一個寄存器指向的內存存儲的值進行交換。
如指令:xchg eax,[esi]
如果 eax=55,esi=0x10000,程序會查找 0x10000 存儲的值,如果地址有寫入權限的話,就存入 55,原先 0x10000 的值寫入 eax。
4.3 堆棧操作指令#
Stack(堆棧)
是由於函數運行而臨時佔用的內存區域,內存存取模式是先進後出(FILO,FIRST IN LAST OUT)
,通過這種方式來存儲和讀取數據。
Stack(堆棧)
的特點就是,最晚入棧的幀最早出棧(因為最內層的函數調用,最先結束運行),這就叫做後進先出
的數據結構。
對於堆棧的數據操作有 2 個基本的操作命令
-
PUSH 將一個對象保存在堆棧的頂部 (壓棧)
-
POP 將最後存入堆棧頂的對象取出。
-
在任何時刻,只能對堆棧的頂部或者最後一個壓棧的對象進行操作。
POP 操作將最後存入堆棧頂的對象取出,然後這個對象下方 (倒數第二次壓棧操作存入) 的對象變成最後一次壓棧操作存入的對象,能夠進行後續操作。
Stack 是由內存區域的結束地址開始,從高位(地址)向低位(地址)分配。比如,內存區域的結束地址是 0x00001000 ,第一幀假定是 4 字節,那麼下一次分配的地址就會從 0x00000FFC 開始;如上面的圖所示 。
push#
通常在 32 位程序中,PUSH 用於在調用函數之前向堆棧傳遞參數,例如在上圖0x40104f
處的指令。PUSH 64
指令將 64 (dword) 壓入堆棧頂,PUSH EAX
將 EAX 的值壓棧,存儲在之前 64 (dword) 的上方,現在 EAX 的值在堆棧的最頂部。
PUSH 可以操作不同的對象,除常數外,還可以對地址進行操作。
可以發現字符串名稱前面的 OFFSET 關鍵詞,這條指令會將這個字符串或者字符數組的地址壓棧。
雙擊字符串名稱,跳轉到下圖中。
一般 C 語言源代碼中,字符數組是這樣定義的: Char mystring[] = "Hello"
按D鍵
會改變windowname
變量的數據類型,強制 IDA 不將它視作字符數組而作為 db 或者說字節。
修改之後,引用這個地址的一些指令會隨之改變,在下圖中,offset 關鍵字確保傳入的還是 0x004020e7 這個地址,但地址上存儲的不再是字符數組,而是字節。
整個指令變成了push offset byte_4020E7
,查找 `byte_4020E7`` 的內容,變成了 db 類型。
按A鍵
將其轉換為 ASCII 字符串,恢復到初始顯示。
當發現其他作為單獨字符顯示的字符串時,都可以進行該操作。找到字符串的起點按 “A” 鍵,使顯示更加易讀。
如下內容:
按 A 鍵之後,變成如下內容:
在0x402110
處按D鍵
, 將 amenu
字符串分成單個字節。再按A鍵
重新組合成字符串。
普通字符串示例
按X鍵
查找引用字符串的位置。
一般在向函數傳遞參數的時候,一般都會使用PUSH offset xxxxx
指令,將字符串的地址傳遞給函數。如果沒有 offset 關鍵詞,傳遞進去的就是0x402110
地址上存儲的內容,就是字符串的具體字節55 4E 45 4D
。但是 API 函數不是這樣運行的,他們一般接受指針或者字符串的起始地址作為參數。
在以上的指令中,DS: 這個標記表示程序將在數據區塊 (DS=DATA) 的內存上寫入。
pop 指令#
POP 指令會讀取堆棧頂上的值,並且將它傳輸到目標寄存器上。如下圖所示,POP EDI
將讀取堆棧頂上的第一個值並將其傳給 EDI 寄存器,並將 ESP 的值指向已讀取值下方的那個值,並把它作為堆棧的頂部。
通過 Text 搜索 pop,發現 pop 指令的使用方式沒什麼改變,而且沒有將值 pop 存入一個地址中。