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