【逆向】x64程序逆向基礎


主要區別

1. 所有地址指針都是64位。 2. 增加和擴展新的寄存器,並兼容原32位版本的通用寄存器。 3. 原指令指針寄存器EIP擴展為RIP。

寄存器

1. 64位寄存器兼容原32位寄存器。 2. 新增加8個XMM寄存器(XMM8-XMM15)。 3. 擴展原32位寄存器的64位版本,並增加8個新的64位寄存器(R8-R15)。

// 通用寄存器:RAX(64位),EAX(32位),AX(16位),AL(0-7位),AH(8-15位)
// 新增寄存器:R8(64位),R8D(32位),R8W(16位),R8B(8位)

調用約定

1. x86使用stdcall、cdecl、Fastcall等。 2. x64使用類似“Fastcall”的調用約定。 使用RCX、RDX、R8、R9寄存器傳遞前4個參數,其余參數從右往左依次保存在棧上。 3. 浮點參數使用XMM寄存器傳遞(XMM0-XMM3)。
4. 任何在函數開頭的mov指令都是在保存被傳遞到這個函數的參數,編譯器不會再其中插入做其它事情的mov指令。
1 mov dword ptr [rsp+28h] ,6     //參數6 保存在棧中
2 mov dword ptr [rsp+20h] ,5     //參數5 保存在棧中
3 mov r9d ,4                     //參數4 保存在寄存器中
4 mov r8d ,3                     //參數3 保存在寄存器中
5 mov edx ,2                     //參數2 保存在寄存器中
6 mov ecx ,1                     //參數1 保存在寄存器中
7 call Fun                       //調用函數

棧使用

1. 32位代碼在函數中使用push和pop等指令改變棧的大小。 2. 64位代碼在函數中從不改變棧的大小,棧在函數的開始增長,期間一直保持不變,直到函數末尾。 3. 當一個函數調用另一個函數時,調用函數會多申請32字節(0x20)的預留棧空間,當被調用函數寄存器不夠用時,可以將4個參數寄存器(RCX、RDX、R8、R9)中的值保存在申請的預留棧空間中。 預留棧空間由函數調用者提前申請,也由函數調用者負責平衡回收。
注意:如果一個函數有其他參數(>4個)或局部棧變量,函數會在0x20的基礎上增加預留棧空間的大小,有時增加大小后的值需要與16進行對齊

示例代碼

 1 #include "stdafx.h"
 2 
 3 // Add
 4 int Add(int nl, int n2, int n3, int n4, int n5, int n6)
 5 {
 6     return nl+n2+n3+n4+n5+n6;
 7 }
 8 
 9 // Main
10 int tmain(int argc, TCHAR* argv[])
11 {
12     printf("%d\r\n", Add(1,2,3,4,5,6));
13     return 0;
14 }

 Main函數反匯編

 1 // 保存Main函數參數到預留棧空間,此預留棧空間為其它函數調用Main函數時申請
 2 mov[rsp+10h], rdx            // 將參數2保存到預留棧空間中
 3 mov[rsp+8h], ecx             // 將參數1保存到預留棧空間中
 4 
 5 // Main函數作為調用者申請預留棧空間,用於保存Add函數的參數
 6 push rdi                     // 保存環境
 7 sub rsp, 30h                 // 申請預留棧空間(Add函數6個參數)(6*8=48 0x30)
 8 mov rdi, rsp                 // 將棧空間初始化為0xcC
 9 mov ecx, 0Ch
10 mov eax, 0CCCCCCCCh
11 rep stosd
12 
13 // 調用Add函數,前4個參數使用寄存器,其余參數入棧
14 mov ecx, [rsp+40h]
15 mov dword ptr [rsp+28h], 6   // 參數6入棧
16 mov dword ptr [rsp+20h], 5   // 參數5入棧
17 mov r9d, 4                   // 參數4
18 mov r8d, 3                   // 參數3
19 mov edx, 2                   // 參數2
20 mov ecx, 1                   // 參數1
21 cal1 Add                     // 調用Add函數
22 
23 // 調用pirntf函數
24 mov edx, eax                 // 將返回值保存到edx中
25 lea rcx, Format              // "%d\r\n"
26 cal1 printf                  // 調用pirntf函數
27 xor eax, eax                 // 設置返回值
28 
29 // Main函數作為調用者釋放預留棧空間
30 add rsp, 30h                 // 釋放預留棧空間+2個參數的棧空間(Add參數5,6)
31 pop rdi                      // 恢復環境
32 retn                         // 函數返回

 Add函數反匯編

 1 // 保存Add函數前4個參數到預留棧空間,預留棧空間由Mian函數申請和釋放
 2 mov[rsp + 20h], r9d     // 參數4
 3 mov[rsp + 18h], r8d     // 參數3
 4 mov[rsp + 10h], edx     // 參數2
 5 mov[rsp + 08h], ecx     // 參數1
 6 
 7 // Add函數中沒有調用其它函數和局部變量,所以沒有申請預留棧空間
 8 push rdi               // 保存環境
 9 mov eax, [rsp + 18h]    // eax = 參數2
10 mov ecx, [rsp + 10h]    // ecx = 參數1
11 add ecx, eax           // ecx = 參數1+參數2
12 mov eax, ecx           // eax = ecx
13 
14 // 使用預留棧空間來獲取Add函數參數
15 add eax, [rsp + 20h]    // eax+參數3
16 add eax, [rsp + 28h]    // eax+參數4
17 add eax, [rsp + 30h]    // eax+參數5
18 add eax, [rsp + 38h]    // eax+參數6
19 
20 // 再次印證預留棧空間由調用函數(Main函數)釋放
21 pop rdi                // 恢復環境
22 retn                   // 函數返回

WOW64與重定位

1. 微軟開發的一個在Win64位系統上運行的Win32位子系統(wow64),用於允許32位程序在64位機器上運行。

2. 由於Wow64使用x64處理器的32位模式來執行指令,為了不同版本的程序能正確訪問系統核心組件(DLL、EXE),就需要對文件系統和注冊表進行重定位處理。

文件重定位:
// 32位程序重定位目錄到:“\SysWOW64”。
// 64位程序重定位目錄到:“\System32”。
// 32位程序通過訪問:"C:\Windows\Sysnative"目錄可以進入真實的\System32目錄,不管是否存在重定位。

注冊表重定位:
// 32位程序訪問注冊表:“HKEY_LOCAL MACHINE\Software”
// 也會被重定位到:“HKEY_LOCAL_MACHINE\Software\Wow6432Node”。
// 使用“RegCreateKeyEx”等函數可以通過參數的標志位來決定訪問注冊表的32位或64位版本。

3. 使用以下函數可以查詢當前程序是否運行在Wow64環境中。

1 // 確定指定的進程是否在WOW64下運行
2 BOOL WINAPI IsWow64Process(
3   _In_   HANDLE hProcess,        // 進程句柄
4   _Out_  PBOOL Wow64Process      // Wow64下運行返回TRUE,否則FALSE
5 );

4. 使用以下函數可以禁用或開啟當前線程的文件重定位。

1 Wow64DisableWow64FsRedirection   // 禁用調用線程的文件系統重定向(默認開啟)
2 Wow64RevertWow64FsRedirection    // 為調用線程恢復文件系統重定向
3 Wow64EnableWow64FsRedirection    // 為調用線程啟用或禁用文件系統重定向

指針與常量數據識別

一般由編譯器生成的代碼,一個整數最常見的大小是32位(特殊情況下也可能是64位),而一個指針數據的大小一定是64位所以當我們開始理解一個函數的功能時,這些信息對判斷一個函數的用途能起到關鍵作用。


免責聲明!

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



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