1.啓動函數
功能:檢索新進程命令指針,環境指針,全局變量初始化,內存堆棧初始化
比如:
GetCommandLineA 命令指針
GetStartupInfoA 啓動信息
GetModuleHandleA 執行文件基地址
編譯器自動加入的代碼:
00401020 >/$ 55 push ebp
00401021 |. 8BEC mov ebp, esp
00401023 |. 6A FF push -1
00401025 |. 68 A0504000 push 004050A0
0040102A |. 68 7C1C4000 push 00401C7C ; SE 處理程序安裝
0040102F |. 64:A1 0000000>mov eax, dword ptr fs:[0]
00401035 |. 50 push eax
00401036 |. 64:8925 00000>mov dword ptr fs:[0], esp
0040103D |. 83EC 10 sub esp, 10
00401040 |. 53 push ebx
00401041 |. 56 push esi
2.函數及其參數
反編譯分析時,將注意力集中在函數的識別以及參數的傳遞上
函數多以call(保存函數返回信息)地址爲跳轉 也有動態計算或者寄存器傳地址
函數的參數傳遞方式有三種:
堆棧方式
寄存器方式
全局變量
3.函數的返回值
1. return操作符返回值
od遇到了一個bug 就是不是每個指令都會下到int3斷點 從而debug時F8單步有一些代碼是一起執行的。(這讓想起了原子操作)
一般的返回值時存放在eax寄存器中,如果超過了容量則會放到edx寄存器中。
2.通過參數按照傳引用的方式返回
注意ds和ss的區別 然後一般通過引用來傳值需要使用變量的地址,在調用某個函數時再把變量的地址傳給函數。
3.通過全局變量返回
4.數據結構
逆向算法和數據結構
1.局部變量
1.堆棧分配局部變量
在堆棧中進行分配,分配完在釋放。或者直接存放在寄存器中。
分配[ebp-xxx]
訪問[ebp+xxx]
初始化局部變量的兩種方法:
mov [ebp-xxx],5
push 5
2.利用寄存器存放局部變量
堆棧佔用兩個寄存器,編譯器會利用剩下的6個通用寄存器儘可能的有效存放局部變量。寄存器不夠時纔會使用堆棧,且局部變量的生命週期比較短,需要確定時那個寄存器存放的那個變量。
2.全局變量
全局變量存放在內存區,靜態pe的.data的可讀寫區域的固定地址上。訪問時即訪問硬編碼的特定地址即可。
(靜態變量 static 作用範圍有限)
如果是常量 則在只可讀區域內。
3.數組
數組保存在.data段中 使用基地址+偏移來尋址
4.虛函數
虛函數實在運行時刻定義的函數,地址無法在編譯時確定(在即將調用時確定)
對所有虛函數的引用常放在一個專用的數組虛函數表VTBL裏,
這裏使用4050A0存放着虛表(虛表裏有兩個方法的地址 利用兩次間接尋址找到虛函數的正確地址)
每太搞懂右邊的流程,爲什麼有個失敗的流程 xor esi,esi
還有爲什麼每次任然需要新建一個this指針( mov ecx,esi )ecx來傳遞參數給成員函數
(更好奇的是如何復原ida裏面的函數流程)
5.控制語句
1.if-then-else的循環控制語句
整數比較實用cmp 浮點數使用fcom fcomp
不太懂這個初始化的時候的值。書上說是指向局部變量空間
這裏使用test替換cmp指令 如果爲0則zf=1 不跳轉繼續執行
2.Switch-case語句
未優化前的代碼都含有printf函數
編譯優化後使用dec eax 來代替cmp指令 指令更短執行更快速
使用調轉表
cmp 和ja 無符號大於則跳轉 jmp table
3.轉移指令的機器碼的計算
短轉:無條件和條件轉移的機器碼都是兩個字節
長轉:無條件轉移是5個字節 條件轉移是6個字節
子程序調用:call
6.循環語句
受用Maximize Speed優化後的代碼使用寄存器來傳值
7.數學運算符
1.一般使用add和sub指令,優化後比較喜歡使用lea指令來代替add和sub指令
這兩個變量是什麼時候初始化的
004007030 是%d
004701020 是printf函數
2.整數的乘法
mul和imul指令。
如果一個數是2的冪,會使用指令shl來實現乘法。
3.除法
div或者idiv指令
符號拓展指令cdq,作用是把eax寄存器中的數視爲有符號的數字。
編譯優化後爲:
用於將除法轉化爲乘法
8.文本字符串
1.字符串存儲格式
C \0 也成爲ASCII字符串 廣泛用於windows和unix中
DOS $
Pascal Y
Delphi Y
2.字符尋址指令
80x86支持直接和間接尋址等模式
mov eax,[40010h]
mov eax,[ecx]
load effective address
lea eax,[40040h]== mov eax,40040h
lea eax,[eax+8]== add eax,8
3.字符大小寫轉換
大寫41~5A 小寫 61~7A 大小寫轉換爲減去20h
或者將第五位置0
4.計算字符串的長度
strlen()的實現
repne scasb指令獲取字符串長度 strlen.c文件中的內聯彙編代碼如下:
#include <string.h>
size_t
strlen (const char *str)
{
int cnt;
asm("cld\n" /* Search forward. */
/* Some old versions of gas need `repne' instead of `repnz'. */
"repnz\n" /* Look for a zero byte. */
"scasb" /* %0, %1, %3 */ :
"=c" (cnt) : "D" (str), "0" (-1), "a" (0));
return -2 - cnt;
}
並沒有使用scasb指令,而是直接比對字節內容。