ctf中 可執行文件patch技術

前言

在CTF比賽中, 有時我們需要對可執行文件進行patch,或者在植入後門時,patch也是常用的手段。不過手工patch比較麻煩,下面介紹幾個工具。

博客地址:jinyu00.github.io/

ELF

Patchkit

地址:

github.com/lunixbochs/p

1. 由於鏈接器的原因暫時還不能使用 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

程序地址:github.com/lief-project

使用這個工具可以很方便的 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")

參考:

lief.quarkslab.com/doc/

github.com/lunixbochs/p

本文由看雪論壇 暗香沉浮 原創 轉載請註明來自看雪社區


推薦閱讀:

世界範圍內知名的 CTF 競賽有哪些?各有何不同?
打CTF比賽,對以後有什麼幫助嗎?含金量高嗎?
打CTF比賽可以代表哪方面的水平?對未來的職業生涯有何幫助?
國際國內良心的ctf比賽?
如何評價今年的0CTF?

TAG:CTFCaptureTheFlag | ELF |