《C++反匯編與逆向分析技術揭秘》--錢林松,趙海旭 著


1 熟悉工作環境和相關工具

1.1 調試工具Microsoft Visual C++6.0和OllyDBG

1.2 反匯編靜態分析工具IDA

1.3 反匯編引擎的工作原理


2 基本數據類型的表現形式

2.1 整數類型

2.2 浮點數類型

2.3 字符和字符串

2.4 布爾類型

2.5 地址、指針和引用

2.6 常量


3 認識啟動函數,找到用戶入口

3.1 程序的真正入口

3.2 了解VC++6.0的啟動函數

3.3 main函數的識別


4 觀察各種表達式的求值過程

4.1 算術運算和賦值

4.2 關系運算和邏輯運算

4.3 位運算

4.4 編譯器使用的優化技巧

4.5 一次算法逆向之旅


5 流程控制語句的識別

5.1 if語句

  • if語句只能判斷兩種情況,0和非0

  • 5-1 if語句構成

#include<stdio.h>
int main()
{
	int a=2;
	if (a==2)//注意與匯編位置對比
	{
		printf("%d",a);
	}
	return 0;
}

image-20211003092942039

image-20211003093502615

  • 使用相反的條件跳轉指令

  • 表達式短路和if語句的反匯編一樣,不容易區分

    總結:
    ;先執行各類影響標志位的指令
    ;其后各種條件跳轉指令
    JCC xxxx
    
  • 循環結構中也會出現類似代碼,分析過程還需要結合上下文

5.2 if...else...語句

  • 取相反的條件跳轉指令
  • if語句條件成立執行后有個jmp指令,跳過else的代碼塊
  • 條件表達式與if-else的判斷比較難
  • 5-3 if-else組合
#include<stdio.h>
int main()
{
	int a=2;
	if (a==2)
	{
		printf("a等於2");
	}
	else{
		printf("a不等於2");
	}
	return 0;
}

image-20211003094550882

  • 利用兩個跳轉(jne,jmp)來區別if塊和else塊的邊界
總結:
;先執行影響標志位的指令
jcc ELSE_BEGIN  ;重點
IF_BEGIN:
...
IF_END:
jmp ELSE_END    ;重點
ELSE_BEGIN:
...
ELSE_END:
  • 在沒有高級源碼的情況下,分析者需要先定位語句塊的邊界,然后根據跳轉目標和邏輯依賴慢慢推出高級代碼

5.3 用if構成的多分支流程

#include<stdio.h>
int main()
{
	int a=2;
	if (a<0)
	{
		printf("a小於2");
	}
	else if(a==2)
	{
		printf("a等於2");
	}
	else
	{
		printf("a為其他");
	}
	return 0;
}

image-20211003101243644

image-20211003101433819

  • 每一個if語句由cmp與jcc組成,而else代碼之前沒有cmp判斷,直接跳到最后
總結:
;會影響標志位的指令 
JCC ELSE_IF_BEGIN   //if條件不成立,跳轉到下一條if_else首地址
IF_BEGIN:
...            //if條件成立執行代碼塊
IF_END: 
jmp END      //執行if后跳出到結束地址

ELSE_IF_BEGIN:      //else_if條件不成立,跳轉到else首地址
;會影響標志位的指令
JCC ELSE_BEGIN
...             //else_if條件成立,執行代碼塊
ELSE_IF_END:
jmp END    //執行else_if后跳出到結束地址

ELSE_BEGIN:   //if和else_if條件都不成立,執行else代碼
...
ELSE_END   //執行完就結束了,不用再跳轉了
...
END:
...
  • 編譯器自動優化問題,空間與效率的問題

5.4 switch的真相

  • switch是常用的多分支結構,效率上高於if else_if語句
#include<stdio.h>
int main()
{
	int nIndex = 1;
	scanf_s("%d", &nIndex);
	switch (nIndex)
	{
	case 1:
		printf("nIndex==1");
	case 2:
		printf("nIndex==2");
	case 100:
		printf("nIndex==100");
	}
	return 0;
}

image-20211003105522799

image-20211003105622944

int main()
{
000C18B0  push        ebp  
000C18B1  mov         ebp,esp  
000C18B3  sub         esp,0D4h  
000C18B9  push        ebx  
000C18BA  push        esi  
000C18BB  push        edi  
000C18BC  lea         edi,[ebp-14h]  
000C18BF  mov         ecx,5  
000C18C4  mov         eax,0CCCCCCCCh  
000C18C9  rep stos    dword ptr es:[edi]  
000C18CB  mov         eax,dword ptr [__security_cookie (0CA024h)]  
000C18D0  xor         eax,ebp  
000C18D2  mov         dword ptr [ebp-4],eax  
000C18D5  mov         ecx,offset _A43DB3BE_源@cpp (0CC003h)  
000C18DA  call        @__CheckForDebuggerJustMyCode@4 (0C1316h)  

	int nIndex = 1;
000C18DF  mov         dword ptr [nIndex],1  
	scanf_s("%d", &nIndex);
000C18E6  lea         eax,[nIndex]  
000C18E9  push        eax  
000C18EA  push        offset string "%s" (0C7B30h)  
000C18EF  call        _scanf_s (0C13C0h)  
000C18F4  add         esp,8  
	switch (nIndex)
000C18F7  mov         eax,dword ptr [nIndex]  
000C18FA  mov         dword ptr [ebp-0D4h],eax  
000C1900  cmp         dword ptr [ebp-0D4h],1  
000C1907  je          __$EncStackInitStart+61h (0C191Dh)  
000C1909  cmp         dword ptr [ebp-0D4h],2  
000C1910  je          _printf+0Ah (0C192Ah)  
000C1912  cmp         dword ptr [ebp-0D4h],64h  
000C1919  je          _printf+17h (0C1937h)  
000C191B  jmp         _printf+24h (0C1944h)  
	{
	case 1:
		printf("nIndex==1");
000C191D  push        offset string "two" (0C7B34h)  
000C1922  call        _printf (0C10CDh)  
000C1927  add         esp,4  
	case 2:
		printf("nIndex==2");
000C192A  push        offset string "nIndex==2" (0C7BE4h)  
000C192F  call        _printf (0C10CDh)  
000C1934  add         esp,4  
	case 100:
		printf("nIndex==100");
000C1937  push        offset string "nIndex==100" (0C7BF0h)  
000C193C  call        _printf (0C10CDh)  
000C1941  add         esp,4  
	}
	return 0;
000C1944  xor         eax,eax  
}
  • if...else_if會在條件跳轉后緊跟語句塊,switch case則將所有的條件跳轉放到一起,把case語句塊也放到了一起

5.5 難以構成跳轉表的switch

5.6 降低判定樹的高度

5.7 do/while/for的比較

5.8 編譯器對循環結構的優化


6 函數的工作原理

6.1 棧幀的形參和關閉

6.2 各種調用方式的考察

6.3 使用ebp或esp尋址

6.4 函數的參數

6.5 函數的返回值

6.6 回顧


7 變量在內存中的位置和訪問方式

7.1 全局變量和局部變量的區別

7.2 局部靜態變量的工作方式

7.3 堆變量


8 數組和指針的尋址

8.1 數組在函數內

8.2 數組作為參數

8.3 數組作為返回值

8.4 下標尋址和指針尋址

8.5 多維數組

8.6 存放指針類型數據的數組

8.7 指向數組的指針變量

8.8 函數指針


9 結構體和類

9.1 對象的內存布局

9.2 this指針

9.3 靜態數據成員

9.4 對象作為函數參數

9.5 對象作為返回值


10 關於構造函數和析構函數

10.1 構造函數的出現時機

10.2 每個對象都有默認的構造函數嗎

10.3 析構函數的出現時機


11 關於虛函數

11.1 虛函數的機制

11.2 虛函數的識別


12 從內存角度看繼承和多重繼承

  • 父類指針指向子類對象

12.1 識別類和類之間的關系

  • 子類具備父類所有成員數據,成員函數

  • 父類聲明為私有的成員,子類對象無法直接訪問,但在子類對象的內存結構中,父類私有的成員數據依然存在

  • C++訪問權限僅限於編譯層面,不會影響對象的內存結構

  • 12-1 定義派生類和繼承類

    
    

12.2 多重繼承

12.3 抽象類

12.4 菱形繼承


13 異常處理

13.1 異常處理的相關知識

13.2 異常類型為基本數據類型的處理流程

13.3 異常類型為對象的處理流程

13.4 識別異常處理


14 PEiD的工作原理分析

14.1 開發環境的識別

14.2 開發環境的偽造


15 “熊貓燒香”病毒逆向分析

15.1 調試環境配置

15.2 病毒程序初步分析

15.3 “熊貓燒香”啟動過程分析

15.4 “熊貓燒香”的自我保護分析

15.5 “熊貓燒香”的感染過程分析


16 調試器OllyDBG的工作原理分析

16.1 INT3斷點

16.2內存斷點

16.3 硬件斷點

16.4 異常處理機制

16.5 加載調試程序


17 反匯編代碼的重建與編譯

17.1 重建反匯編代碼

17.2 編譯重建后的反匯編代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM