ctf中 可執行文件patch技術
前言
在CTF比賽中, 有時我們需要對可執行文件進行patch,或者在植入後門時,patch也是常用的手段。不過手工patch比較麻煩,下面介紹幾個工具。
博客地址:https://jinyu00.github.io/
ELF
Patchkit
地址:
https://github.com/lunixbochs/patchkit.git1. 由於鏈接器的原因暫時還不能使用 libc 中的函數,所以所有要做的事情都需要我們自己實現。用 c 或者 asm
pt.patch(addr,jmp=jmp_addr) 用於修改程序流程。
pt.hook(addr, target) 用於劫持程序流程,進行參數過濾。
使用方式:./patch binary_file patch.py
過濾printf中 %n 的腳本。
def replace_free(pt):
printf_addr = 0x400548;// call printf 時的地址
new_printf = pt.inject(c=r
void fix_printf(char *fmt) {
for (int i = 0; fmt[i]; ++i)
{
if (fmt[i] == % && fmt[i+1] == n) {
//找到後,通過前移的方式刪除字元,每次刪掉一個。
int len=0;
int j;
while(fmt[len++]){
}
for(j=i;j<len-1;j++)
fmt[j] = fmt[j+1];
fmt[len-1] = x00;
len=0;
while(fmt[len++]){
}
for(j=i;j<len-1;j++)
fmt[j] = fmt[j+1];
fmt[len-1] = x00;
//i--;
}
}
}
)
pt.hook(printf_addr, new_printf);
64位程序,修改 malloc函數的參數為 0x20
def replace(pt):
malloc_addr = 0x040057A; //call malloc的位置
new_malloc = pt.inject(asm=r
mov rdi,0x20
ret
)
pt.hook(malloc_addr, new_malloc);
32位,由於與棧進行操作,要注意保存還原返回地址
def replace(pt):
malloc_addr = 0x08048454;
new_malloc = pt.inject(asm=r
pop eax
pop ebx
push 0x20
push eax
ret
)
pt.hook(malloc_addr,new_malloc);
或者
def replace(pt):
malloc_addr = 0x08048454;
new_malloc = pt.inject(asm=r
mov eax,0x20
mov [esp+4], eax
ret
)
pt.hook(malloc_addr,new_malloc);
LIEF
程序地址:https://github.com/lief-project/LIEF
使用這個工具可以很方便的 patch elf, pe,MachO 文件。本文以elf 為例。
通過交換導入導出符號
首先看第一個測試程序:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
puts("/bin/sh");
return EXIT_SUCCESS;
}
我們的目標是讓他調用 puts 變成調用 system
方案一
修改 libc 中的相關符號,然後使用 LD_LIBRARY_PATH 載入我們修改後的庫。
import lief
hashme = lief.parse("hashme")
libc = lief.parse("/lib/x86_64-linux-gnu/libc-2.23.so")
# get puts, system symbol
puts_sym = filter(lambda e: e.name == "puts", libc.dynamic_symbols)[0]
system_sym = filter(lambda e: e.name == "system", libc.dynamic_symbols)[0]
# swap them
puts_sym.name = "system"
system_sym.name = "puts"
libc.write("libc.so.6")
print("done")
首先拿到 puts 和 system 符號對象,然後交換他們的名稱。
成功
方案二
直接修改目標文件的導入符號,代碼如下
import lief
hashme = lief.parse("hashme")
# get puts, system symbol
puts_sym = filter(lambda e: e.name == "puts", hashme.imported_symbols)[0]
# set puts to system
puts_sym.name = "system"
hashme.write("hashme.patch")
print("done")
直接增加代碼進行patch
修改庫函數
測試程序:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <a>
", argv[0]);
exit(-1);
}
int a = atoi(argv[1]);
printf("exp(%d) = %f
", a, exp(a));
return 0;
}
目標是hook exp 函數,直接增加一個 segments , 然後劫持函數指針到這裡。首先編譯一個 lib 用來提供用於 hook 的代碼。
gcc -Os -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook
hook.c 的內容:
double hook(double x) {
return x + 100;
}
然後看腳本內容,很清晰。
import lief
libm = lief.parse("/lib/x86_64-linux-gnu/libm-2.23.so")
hook = lief.parse("hook")
segment_added = libm.add(hook.segments[0])
print("Hook inserted at VA: 0x{:06x}".format(segment_added.virtual_address))
exp_symbol = libm.get_symbol("exp")
hook_symbol = hook.get_symbol("hook")
exp_symbol.value = segment_added.virtual_address + hook_symbol.value
libm.write("libm.so.6")
通過 got/plt 表 直接劫持程序
測試程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Damn_YoU_Got_The_Flag
char password[] = "x18x3dx31x32x03x05x33x09x03x1bx33x28x03x08x34x39x03x1ax30x3dx3b";
inline int check(char* input);
int check(char* input) {
for (int i = 0; i < sizeof(password) - 1; ++i) {
password[i] ^= 0x5c;
}
return memcmp(password, input, sizeof(password) - 1);
}
int main(int argc, char **argv) {
if (check(argv[1]) == 0) {
puts("You got it !!");
return EXIT_SUCCESS;
}
puts("Wrong");
return EXIT_FAILURE;
}
hook.c 內容,hook memcpy, 列印內容。
#include "arch/x86_64/syscall.c"
#define stdout 1
//gcc -nostdlib -nodefaultlibs -fPIC -Wl,-shared hook.c -o hook
int my_memcmp(const void* lhs, const void* rhs, int n) {
const char msg[] = "Hook add
";
_write(stdout, msg, sizeof(msg));
_write(stdout, (const char*)lhs, n);
_write(stdout, "
", 2);
_write(stdout, (const char*)rhs, n);
_write(stdout, "
", 2);
return 0;
}
hook 腳本
import lief
crackme = lief.parse("crackme.bin")
hook = lief.parse("hook")
segment_added = crackme.add(hook.segments[0])
my_memcmp = hook.get_symbol("my_memcmp")
my_memcmp_addr = segment_added.virtual_address + my_memcmp.value
crackme.patch_pltgot(memcmp, my_memcmp_addr)
crackme.write("crackme.hooked")
參考:
https://lief.quarkslab.com/doc/tutorials/
https://github.com/lunixbochs/patchkit
本文由看雪論壇 暗香沉浮 原創 轉載請註明來自看雪社區
推薦閱讀:
※世界範圍內知名的 CTF 競賽有哪些?各有何不同?
※打CTF比賽,對以後有什麼幫助嗎?含金量高嗎?
※打CTF比賽可以代表哪方面的水平?對未來的職業生涯有何幫助?
※國際國內良心的ctf比賽?
※如何評價今年的0CTF?
TAG:CTFCaptureTheFlag | ELF |