iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Security

從零開始の Binary CTF:30 天系統化學習 Rev & Pwn系列 第 13 篇

Day13|Xref & Function Analysis

  • 分享至 

  • xImage
  •  

昨天我們介紹了 Control Flow,開始學著從:

Basic Block
Branch
CFG

去理解一個 Function 裡面的執行流程。
但實際的程式通常不會只有一個 Function。

例如:

main()
 ├── read_input()
 ├── check_password()
 └── print_flag()

這時候只盯著 main() 裡面的 Assembly 看,很快就會遇到另一個問題:

這個 Function 是誰呼叫的?
這個 String 又是在哪裡被使用的?

這就是今天要看的:

Xref
Function Analysis

0x00 準備

先來準備一個簡單的程式:

#include <stdio.h>
#include <string.h>

int check_password(char *input) {
    if (strcmp(input, "shark_is_cute") == 0) {
        return 1;
    }

    return 0;
}

void success() {
    puts("Correct!");
}

int main() {
    char input[32];

    printf("Password: ");
    scanf("%31s", input);

    if (check_password(input)) {
        success();
    } else {
        puts("Wrong!");
    }

    return 0;
}

Compile:

gcc chall.c -o chall

接著把它丟進 IDA 或 Ghidra。


0x01 Xref 是什麼?

Xref 全名是:

Cross Reference

可以先把它理解成:

誰有使用這個東西?

這個「東西」可以是:

Function
String
Global Variable
Address
Data

例如 Binary 裡面有:

Correct!

我們可以問:

哪裡使用了 "Correct!"?

工具就可能告訴我們:

success()

再查看 success() 的 Xref,又可以知道:

誰呼叫了 success()?

答案可能是:

main()

所以我們就可以一路反推:

"Correct!"
    ↑
 success()
    ↑
  main()

這就是 Xref 很重要的地方。


0x02 從 String 開始找

前面 Static Analysis 的時候有介紹過:

strings chall

可能會看到:

Password:
Correct!
Wrong!
shark_is_cute

其中:

Correct!

就是一個很值得注意的 String。

因為如果這是一題 Crackme,我們通常想知道的就是:

怎麼走到成功的地方?

這時不一定需要從 main() 一行一行往下看。

可以直接在 IDA / Ghidra 的 Strings View 找:

Correct!

接著查看它的 Xrefs。

可能會跳到:

void success()
{
    puts("Correct!");
}

於是我們知道:

Correct!
   ↑
success()

下一步再問:

誰呼叫 success()?

繼續查看 success() 的 Xrefs。

可能就會找到:

if (check_password(input)) {
    success();
}

到這邊,分析方向就很明顯了。


0x03 Function Xref

Xref 不只能用在 String。

Function 本身也可以查看 Cross Reference。

例如:

check_password()

我們想知道:

誰呼叫 check_password()?

查看 Xrefs 後可能得到:

main

Assembly 可能看到:

lea     rax, [rbp-20h]
mov     rdi, rax
call    check_password
test    eax, eax
je      wrong

前面已經學過:

RDI → 第一個 Argument
RAX → Return Value

因此可以推測:

input
  ↓
check_password(input)
  ↓
RAX
  ↓
判斷成功 / 失敗

這時候我們就不用把整個 main() 都看懂。

只需要順著我們關心的 Function 追下去。


0x04 Caller 與 Callee

分析 Function 時會很常遇到兩個詞:

Caller
Callee

假設:

main() {
    check_password(input);
}

那麼:

main            → Caller
check_password  → Callee

也就是:

Caller
   │
   │ call
   ▼
Callee

所以分析一個 Function 時,可以問兩個問題:

誰呼叫它?
它又呼叫誰?

例如:

main
 ├── printf
 ├── scanf
 ├── check_password
 │      └── strcmp
 │
 ├── success
 │      └── puts
 │
 └── puts

這樣其實就已經開始形成一個:

Call Graph

0x05 Function Analysis

當 Binary 稍微大一點之後,最常遇到的問題就是:

sub_401156
sub_4011A0
sub_401230
sub_4012F4
...

如果 Binary 被 Strip 過,原本像:

check_password
success
read_input

這些名稱可能全部消失。

IDA 可能只會幫它取名成:

sub_401156

這時候就要靠 Function Analysis 去判斷它在做什麼。

例如看到:

int sub_401156(char *a1)
{
    return strcmp(a1, "shark_is_cute") == 0;
}

那其實我們已經可以推測:

sub_401156

很可能是在做:

check_password

這時就可以直接 Rename:

sub_401156
    ↓
check_password

0x06 Rename 很重要

很多人剛開始 Reverse 的時候,會一直看:

v1
v2
v3
a1
sub_401156
sub_401220

看久之後很容易直接混亂。

所以分析時很重要的一個習慣就是:

看懂一個東西,就幫它重新命名。

例如原本:

int sub_401156(char *a1)
{
    int v2;

    v2 = strcmp(a1, "shark_is_cute");
    return v2 == 0;
}

理解之後可以整理成:

int check_password(char *input)
{
    int result;

    result = strcmp(input, "shark_is_cute");

    return result == 0;
}

雖然 Binary 本身完全沒有改變,但可讀性差非常多。

Reverse Engineering 很大一部分其實就是:

未知資訊
   ↓
分析
   ↓
建立假設
   ↓
重新命名
   ↓
逐漸還原程式語意

0x07 不需要分析所有 Function

這也是我自己覺得 Reverse 很重要的一個觀念。

假設一個 Binary 裡面有:

300 Functions

是不是代表:

我要把 300 個 Function 全部看完?

通常不是。

我們真正需要的是:

找到與目標有關的 Function。

例如 Crackme 想找 Password,可以從:

Correct!
Wrong!
Password:
strcmp
memcmp

這些線索開始。

假設先找到:

Correct!

接著:

Correct!
   ↓ Xref
success()
   ↓ Xref
main()
   ↓
check_password()
   ↓
strcmp()

最後可能只分析了:

4 ~ 5 個 Function

就已經理解主要邏輯。

所以 Reverse 並不是:

從第一行看到最後一行

更像是在 Binary 裡面:

找線索
  ↓
追 Xref
  ↓
找到關鍵 Function
  ↓
建立程式邏輯

0x08 從 Library Function 反推

除了 String 以外,一些 Function 本身也是很好的線索。

例如看到:

strcmp
memcmp
strncmp
scanf
fgets
malloc
free
open
read
write

都可以思考:

為什麼程式會使用它?

例如 Crackme 裡看到:

strcmp

很可能代表程式正在比較:

Input
Password
Flag
Command

查看 strcmp() 的 Xrefs,就可以找到:

哪些地方正在做 String Comparison?

例如:

mov     rsi, offset secret
mov     rdi, rax
call    strcmp

很快就可以發現:

User Input
    ↓
 strcmp
    ↑
 secret

因此:

Xref 不一定要從自己寫的 Function 開始追,也可以從 Library Function 反推程式邏輯。


0x09 Data Xref

除了 Function 和 String,其實 Data 也可以有 Xref。

例如程式裡有:

int is_admin = 0;

某個 Function:

if (is_admin) {
    print_flag();
}

我們找到:

is_admin

之後,可以查看:

誰讀取 is_admin?
誰修改 is_admin?

最後可能找到:

login()
    ↓
修改 is_admin

admin_panel()
    ↓
讀取 is_admin

形成:

login()
   ↓
is_admin
   ↓
admin_panel()

這種分析方式之後在:

Reverse
Malware Analysis
Firmware
Vulnerability Research

裡面都會非常常見。


0x0A Xref + Control Flow

昨天介紹的是 Control Flow。

例如:

        check_password()
              │
          test eax
          /      \
         /        \
    Success      Failed

它主要是在分析:

一個 Function 裡面的程式怎麼走?

今天的 Xref 則比較像:

Function、String、Data 之間彼此有什麼關係?

可以簡單理解成:

Control Flow
    ↓
Function 內部怎麼走

Xref / Call Graph
    ↓
不同 Function / Data 之間怎麼連

兩個搭配起來,就可以慢慢建立整個 Binary 的程式結構。


0x0B 一個基本的 Reverse Workflow

到目前為止,我們的 Reverse Workflow 可以再稍微更新一下:

Binary
  ↓
file / strings
  ↓
IDA / Ghidra
  ↓
找 Interesting String / Function
  ↓
Xrefs
  ↓
找到相關 Function
  ↓
Decompiler / Assembly
  ↓
Control Flow
  ↓
Rename
  ↓
繼續追 Xrefs
  ↓
建立程式邏輯

而不是:

打開 Binary
  ↓
從 main 第一行開始看
  ↓
一路看到最後

這樣分析速度通常會快非常多。


0x0C 小結

今天主要介紹了:

Xref
Caller
Callee
Function Analysis
Call Graph
Rename
Data Reference

其中最重要的概念其實只有一個:

不要試圖一次看懂整個 Binary,而是順著關係找到真正重要的地方。

例如:

Interesting String
      ↓
    Xref
      ↓
Interesting Function
      ↓
 Caller / Callee
      ↓
Control Flow
      ↓
理解程式邏輯

當我們看到:

sub_401156
v3
a1
loc_401230

這些看起來很混亂的東西時,也不用急著全部理解。

先找到它們彼此之間的關係,再慢慢:

Rename
Comment
建立假設
驗證假設

Binary 原本看起來可能是一團:

sub_401156
sub_4011A0
sub_401230
sub_401300

分析到最後,就可能逐漸變成:

main
 ├── read_input
 ├── check_password
 │      └── strcmp
 │
 └── success
        └── puts

而這就是 Reverse Engineering 很核心的一個過程:

從不知道程式在做什麼,到逐步還原出程式真正的行為。


上一篇
Day12|Control Flow
下一篇
Day14|Stripped Binary
系列文
從零開始の Binary CTF:30 天系統化學習 Rev & Pwn 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言