add.c
- Build:
gcc -fno-pie -no-pie -o add add.c
- Code
#include <stdio.h> int add(int a, int b) { return a + b; } int main() { printf("2 + 3 = %d\n", add(2, 3)); return 0; }
DynmaicRIO
Thanks, 🥔
https://github.com/DynamoRIO/dynamorio
- 정적 바이너리에도 적용할 수 있어 좋음
Environment: Ubuntu 20.04 LTS
- Add func address:
0x0000000000401cb5
ubuntu@64a54bd8db27:~/study/DynamoRIO-Linux-11.3.0-1$ nm add | grep add ... 0000000000401cb5 T add ...
- test.c
#include "dr_api.h" #include "drwrap.h" #include "drsyms.h" // 전처리 콜백 static void pre(void *wrapcxt, DR_PARAM_OUT void **user_data) { void *arg0 = drwrap_get_arg(wrapcxt, 0); void *arg1 = drwrap_get_arg(wrapcxt, 1); dr_fprintf(STDERR, "Calling add func with arg0=%p, arg1: %p\n", arg0, arg1); } // 후처리 콜백 static void post(void *wrapcxt, void *user_data) { void *ret = drwrap_get_retval(wrapcxt); dr_fprintf(STDERR, "add func returned %p\n", ret); } static void event_exit(void) { drwrap_exit(); } static void module_load_event(void *drcontext, const module_data_t *mod, bool loaded) { app_pc addr = (app_pc)0x000000000401cb5; if (drwrap_wrap(addr, pre, post)) dr_printf("Wrapped add at %p\n", addr); } DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { drwrap_init(); drsym_init(0); dr_register_exit_event(event_exit); dr_register_module_load_event(module_load_event); }
- build.sh
ubuntu@64a54bd8db27:~/study/DynamoRIO-Linux-11.3.0-1$ g++ -shared -fPIC -o test.so test.c -I ./include -I./ext/include -L ./lib64 -L ./lib64/release -L ./ext/lib64/release -DLINUX -DX86_64 -DUNIX -ldynamorio -ldrwrap -ldrsyms
- Result
ubuntu@64a54bd8db27:~/study/DynamoRIO-Linux-11.3.0-1$ ./bin64/drrun -c ./test.so -- ./add Wrapped add at 0x0000000000401cb5 Wrapped add at 0x0000000000401cb5 Wrapped add at 0x0000000000401cb5 Wrapped add at 0x0000000000401cb5 Calling add func with arg0=0x0000000000000002, arg1: 0x0000000000000003 add func returned 0x0000000000000005 2 + 3 = 5
Patchrex2
https://github.com/purseclab/Patcherex2
- 바이너리 패치를 통해 매개변수를 살펴볼 수 있음.
- 단, (거의) 함수 프롤로그 위주에만 적용 가능한 것 같음. 리턴값은 보기 불가능한듯.
Environment: Ubuntu 22.04 LTS
- Configure
sudo su wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" | tee /etc/apt/sources.list.d/llvm.list apt-get update && apt-get install -y clang-19 lld-19 exit; pip3 install patcherex2 git clone
- Add func address:
0x0000000000001149
ubuntu@wh1te4ever-main:~/Patcherex2/examples/insert_instruction_patch_c$ nm ./add | grep add 0000000000001149 T add ubuntu@wh1te4ever-main:~/Patcherex2/examples/insert_instruction_patch_c$ objdump -d ./add 0000000000001149 <add>: 1149: f3 0f 1e fa endbr64 114d: 55 push %rbp 114e: 48 89 e5 mov %rsp,%rbp 1151: 89 7d fc mov %edi,-0x4(%rbp) 1154: 89 75 f8 mov %esi,-0x8(%rbp) 1157: 8b 55 fc mov -0x4(%rbp),%edx 115a: 8b 45 f8 mov -0x8(%rbp),%eax 115d: 01 d0 add %edx,%eax 115f: 5d pop %rbp 1160: c3 ret
from patcherex2 import * import logging logger = logging.getLogger("patcherex2.patches.instruction_patches") logger.setLevel(logging.INFO) p = Patcherex("add", target_opts={"compiler": "clang19"}) c_forward_header = """ // This string will be inserted outside the micropatch function. It will be inserted before your code. // This is how you can define types and function forward declarations used by your C micropatch #include <stdio.h> """ # The asm_header is inserted in the main body of the patch before the C code. This header is primarily # useful for gaining access to the stack pointer, which is a register that is unavailable in our C # code. In this example we have moved rsp to the r12 register, which is a register that is accessible. # This means that inside the C code we can access variables on the stack by using the r10 variable. # There is also an asm_footer asm_header = "mov r12, rsp" # We can access assembly registers directly by using their name, while still using high level C constructs # as well as intermediate variables. Note that you can use a return statement anywhere in your C micropatch # to jump back to the next instruction after the micropatch insertion point. c_str = """ printf("add called, rsp: %p\\n", (void *) r12); printf("add called, rdi: 0x%llu, rsi: 0x%llu\\n", rdi, rsi); """ # It is generally a good idea to mark some registers as scratch to give the compiler # breathing room for allocating registers to use for intermediate variables in your micropatch # All of the registers that we mark as scratch can be freely clobbered by the compiler # Note that you can still read from scratch registers stored in the variables. What the scratch # register denotation will indicate however is that the register can be re-used after the variable # is no longer live. c_scratch_regs = [ 'r8', 'r9', 'r10', 'r11', 'r13', 'r14', 'r15' 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', 'xmm6', 'xmm7', 'xmm9', 'xmm10', 'xmm11', 'xmm12', 'xmm13', 'xmm14', 'xmm15' ] # By default floating point registers will have the 'float' type. We can use c_regs_sort to override # certain registers so they hold different types. In this example we denote that xmm8 is of type double c_regs_sort = [('xmm8', 'double')] config = InsertInstructionPatch.CConfig( c_forward_header = c_forward_header, scratch_regs=c_scratch_regs, regs_sort=c_regs_sort, asm_header=asm_header ) p.patches.append(InsertInstructionPatch(0x114d, c_str, language="C", c_config=config)) p.apply_patches() p.binfmt_tool.save_binary()
- Result
ubuntu@wh1te4ever-main:~/Patcherex2/examples/insert_instruction_patch_c$ python3 patch.py INFO | 2025-04-22 19:42:21,261 | patcherex2.patches.instruction_patches | InsertInstructionPatch generated C code: ... ubuntu@wh1te4ever-main:~/Patcherex2/examples/insert_instruction_patch_c$ ./add.patched add called, rsp: 0x7ffc1cfc1ef8 add called, rdi: 0x2, rsi: 0x3 2 + 3 = 5
Dobby
https://github.com/jmpews/Dobby
- 바이너리에서 어셈블리어 실행 중에 레지스터 후크할때 유용
- 정적 바이너리 경우, https://github.com/pfalcon/foreign-dlopen 병행해서 사용하면 가능.
- Configure
git clone https://github.com/jmpews/Dobby.git cd Dobby git checkout d353eea7594d1ac85db84065aeb4d1300d9f3248 nano ~/Dobby/source/MemoryAllocator/NearMemoryAllocator.cc
MemBlock *NearMemoryAllocator::allocateNearBlockFromDefaultAllocator
,
**MemBlock *NearMemoryAllocator::allocateNearBlockFromUnusedRegion
함수에 있는 코드 수정.
min_valid_addr = pos - search_range; // if pos < search_range, and min_valid_addr will be a large value,this maybe a bug max_valid_addr = pos + search_range;
위 내용 다음에 아래 내용 추가
min_valid_addr = pos >= search_range ? (pos - search_range) : 0;
설치
mkdir build_for_host && cd build_for_host cmake .. make -j8 sudo cp libdobby.so /usr/local/lib sudo ldconfig sudo cp ~/Dobby/include/dobby.h /usr/local/include
- build.sh
#!/bin/bash gcc -shared -fPIC -o hook.so hook.c -ldl -L. -ldobby
- hook.c
- Add func address:
0x0000000000401139
#define _GNU_SOURCE #include <link.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <signal.h> #include <sys/mman.h> #include <dobby.h> void add_handler(void *address, DobbyRegisterContext *reg_ctx) { uint64_t rdi = (uint64_t)(reg_ctx->general.regs.rdi); uint64_t rsi = (uint64_t)(reg_ctx->general.regs.rsi); printf("[hook.so] Called add_handler: rdi = 0x%lx, rsi = 0x%lx\n", rdi, rsi); } __attribute__((constructor)) void initialize() { printf("[hook.so] Successfully Loaded!\n"); dobby_enable_near_branch_trampoline(); DobbyInstrument((void *)(0x0000000000401136), (dobby_instrument_callback_t)add_handler); dobby_disable_near_branch_trampoline(); // DobbyHook((void*)(0x7165e), (void*)hook_main, (void**)&orig_main); }
- Result (hook.c)
ubuntu@64a54bd8db27:~/study/DynamoRIO-Linux-11.3.0-1$ LD_PRELOAD=./hook.so ./add [hook.so] Successfully Loaded! [hook.so] Called add_handler: rdi = 0x2, rsi = 0x3 2 + 3 = 5
- hook2.c
#define _GNU_SOURCE #include <link.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <signal.h> #include <sys/mman.h> #include <dobby.h> static int (*orig_add)(int a, int b); static int hook_add(int a, int b) { printf("[hook.so] hook_add a: %d, b: %d\n", a, b); return orig_add(a, b); } __attribute__((constructor)) void initialize() { printf("[hook.so] Successfully Loaded!\n"); DobbyHook((void*)(0x401136), (void*)hook_add, (void**)&orig_add); }
- Result (hook2.c)
ubuntu@64a54bd8db27:~/study/DynamoRIO-Linux-11.3.0-1$ LD_PRELOAD=./hook.so ./add [hook.so] Successfully Loaded! [hook.so] hook_add a: 2, b: 3 2 + 3 = 5