commit aa66855054691437330480603087aff6e5cf5dbf Author: Yi-Ting Shih Date: Sat Apr 12 08:26:23 2025 +0800 Initial commit diff --git a/.gdb_history b/.gdb_history new file mode 100644 index 0000000..b1e6163 --- /dev/null +++ b/.gdb_history @@ -0,0 +1,3 @@ +exit +exit +exit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0aaf326 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +sdb +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a464ace --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +.PHONY: all debug clean test + +all: sdb + +sdb: $(patsubst %.c,%.o,$(wildcard *.c)) + gcc -o $@ $^ -lcapstone -std=gnu17 -g -fsanitize=address,undefined -fanalyzer + +%.o: %.c + gcc -o $@ -c $^ -std=gnu17 -Wall -Wextra \ + -g -fsanitize=address,undefined -fanalyzer \ + -Wno-unused-value #-DDEBUG + +clean: + - rm -f sdb *.o + make -C test clean + +test: sdb + make -C test_case diff --git a/hw3_testcase.zip b/hw3_testcase.zip new file mode 100644 index 0000000..4bfd38c Binary files /dev/null and b/hw3_testcase.zip differ diff --git a/logger.h b/logger.h new file mode 100644 index 0000000..91a9763 --- /dev/null +++ b/logger.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#define ANSI_ESC_RED "\x1B[31m" +#define ANSI_ESC_GREEN "\x1B[32m" +#define ANSI_ESC_YELLOW "\x1B[33m" +#define ANSI_ESC_BLUE "\x1B[34m" +#define ANSI_ESC_MAGENTA "\x1B[35m" +#define ANSI_ESC_CYAN "\x1B[36m" +#define ANSI_ESC_COLOR_RESET "\x1B[m" + +#define ANSI_ESC_CLEAR "\x1B[2J" +#define ANSI_ESC_CURSOR_RESET "\x1B[;H" + +#ifdef DEBUG +#define PRINT(file, color, prompt, format, ...) \ + fprintf(file, "%s%s", color, prompt), \ + fprintf(file, format, ##__VA_ARGS__), \ + fprintf(file, "%s", ANSI_ESC_COLOR_RESET) +#define ERROR(format, ...) \ + PRINT(stderr, ANSI_ESC_RED, "[ERROR] ", format, ##__VA_ARGS__) +#define DEBUG(format, ...) \ + PRINT(stderr, ANSI_ESC_YELLOW, "[DEBUG] ", format, ##__VA_ARGS__) +#else +#define PRINT(file, color, prompt, format, ...) \ + fprintf(file, "%s", prompt), \ + fprintf(file, format, ##__VA_ARGS__) +#define ERROR(format, ...) +#define DEBUG(format, ...) +#endif + +#define INFO(format, ...) \ + PRINT(stdout, ANSI_ESC_CYAN, "** ", format, ##__VA_ARGS__) +#define OUTPUT(format, ...) \ + PRINT(stdout, ANSI_ESC_COLOR_RESET, "", format, ##__VA_ARGS__) diff --git a/main.c b/main.c new file mode 100644 index 0000000..6767d8f --- /dev/null +++ b/main.c @@ -0,0 +1,116 @@ +#include "sdb.h" +#include "vector.h" +#include "logger.h" + +#include +#include +#include +#include +#include +#include + +static inline enum instruction prompt(void) +{ + static char *input; + static size_t input_len; + + OUTPUT("(sdb) "); + getline(&input, &input_len, stdin); + + char *op = strtok(input, " \t\n"); + if (op == NULL) return INST_NOP; + + if (strcmp(op, "exit") == 0) return INST_EXIT; + if (strcmp(op, "load") == 0) return INST_LOAD; + if (strcmp(op, "show") == 0) return INST_SHOW; + if (strcmp(op, "info") == 0) return INST_INFO; + if (strcmp(op, "break") == 0) return INST_BREAK; + if (strcmp(op, "delete") == 0) return INST_DELETE; + if (strcmp(op, "patch") == 0) return INST_PATCH; + if (strcmp(op, "si") == 0) return INST_SI; + if (strcmp(op, "cont") == 0) return INST_CONT; + if (strcmp(op, "syscall") == 0) return INST_SYSCALL; + + return INST_INVALID; +} + +int main(int argc, char *argv[]) +{ + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + char *filename = ""; + if (argc > 1) { + filename = malloc(strlen(argv[1]) + 1); + strcpy(filename, argv[1]); + if (access(filename, F_OK) != 0) { + perror("main file open"); + exit(1); + } + } + + if (access(filename, F_OK) != 0) { +load_retry: + enum instruction op = prompt(); + + switch (op) { + case INST_NOP: + goto load_retry; + case INST_EXIT: + goto exit; + + case INST_LOAD: + inst_load(&filename); + if (access(filename, F_OK) == 0) + break; + else { + INFO("file invalid\n"); + goto load_retry; + } + + default: + INFO("please load a program first.\n"); + goto load_retry; + } + } + + DEBUG("filename = %s\n", filename); + run(filename); + + sync_regs(); + disassemble_addr = regs.rip; + disassemble(); + + for (;;) { + enum instruction op = prompt(); + DEBUG("op = 0x%x\n", (int)op); + + switch (op) { + case INST_NOP: break; + case INST_EXIT: goto exit; + case INST_SHOW: disassemble(); break; + case INST_INFO: inst_info(); break; + + case INST_BREAK: inst_break(); break; + case INST_DELETE: inst_delete(); break; + case INST_PATCH: inst_patch(); break; + + case INST_SI: inst_si(); disassemble(); break; + case INST_CONT: inst_cont(); disassemble(); break; + case INST_SYSCALL: inst_syscall(); disassemble(); break; + + default: + INFO("invalid command\n"); + } + + DEBUG("disassemble_addr = %p\n", (void *)disassemble_addr); + if (disassemble_addr == 0x00) + goto exit; + } + +exit: + free(filename); + free(bps); + return 0; +} diff --git a/sdb.c b/sdb.c new file mode 100644 index 0000000..352c49a --- /dev/null +++ b/sdb.c @@ -0,0 +1,410 @@ +#include "sdb.h" +#include "logger.h" +#include "vector.h" + +#include +#include +#include +#include +#include +#include + +int child; +int status; + +struct user_regs_struct regs; +uint64_t disassemble_addr; + +long syscall_nr = 0xffff; + +void sync_regs(void) +{ + if (ptrace(PTRACE_GETREGS, child, NULL, ®s) != 0) { + ERROR("sync_regs ptrace getregs\n"); + perror("sync_regs"); + exit(1); + } +} + +uint8_t poke(uint64_t addr, uint8_t data) +{ + uint64_t ret = ptrace(PTRACE_PEEKTEXT, child, addr, NULL); + ptrace(PTRACE_POKETEXT, child, addr, (ret & ~0xff) | data); + return ret & 0xff; +} + +void run(const char *filename) +{ + child = fork(); + if (child < 0) { + ERROR("run fork failed\n"); + perror("run fork"); + exit(1); + } + + if (child == 0) { + DEBUG("forked\n"); + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) { + perror("run traceme"); + exit(1); + } + execl(filename, filename, NULL); + perror("run execl"); + } + + if (waitpid(child, &status, 0) < 0) { + perror("run waitpid"); + exit(1); + } + if (WIFSTOPPED(status) == 0) { + perror("run stopped"); + exit(1); + } + ptrace(PTRACE_SETOPTIONS, child, 0, + PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD); + + sync_regs(); + INFO("program '%s' loaded. entry point %p.\n", filename, (void *)regs.rip); +} + +void disassemble() +{ + if (disassemble_addr == 0x00) return; + const uint64_t rip = disassemble_addr; + long ret; + uint8_t code[64], *ptr = (uint8_t *)&ret; + + for (int i = 0; i < 8; i++) { + ret = ptrace(PTRACE_PEEKTEXT, child, rip + (i << 3), NULL); + for (int j = 0; j < 8; j++) + code[i << 3 | j] = ptr[j]; + // DEBUG("\n"); + } + + for (int i = 0; i < bps_len; i++) + if (bps[i].addr && rip <= bps[i].addr && bps[i].addr < rip + 64) + code[bps[i].addr - rip] = bps[i].data; + + csh handle; + if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) { + perror("disassemble cs_open"); + exit(1); + } + + cs_insn *insn; + int count = cs_disasm(handle, code, sizeof(code) - 1, rip, 0, &insn); + if (count > 0) { + for (int i = 0; i < INSTRUCTION_COUNT; i++) { + if (insn[i].size == 2 && + insn[i].bytes[0] == 0 && + insn[i].bytes[1] == 0) { + INFO("the address is out of the range of the text section.\n"); + break; + } + OUTPUT("%12lx: ", insn[i].address); + char insn_buf[25] = "", *insn_buf_ptr = insn_buf; + for (int j = 0; j < insn[i].size; j++) + insn_buf_ptr += sprintf(insn_buf_ptr, "%02x ", + (uint8_t)insn[i].bytes[j]); + OUTPUT("%-24s%-10s %s\n", insn_buf, insn[i].mnemonic, insn[i].op_str); + } + cs_free(insn, count); + } else { + perror("disassemble"); + exit(1); + } + cs_close(&handle); +} + +void inst_load(char **filename) +{ + char *token = strtok(NULL, " \t\n"); + if (token == NULL) { + perror("inst_load"); + disassemble_addr = 0x00; + return; + } + *filename = malloc(strlen(token) + 1); + strcpy(*filename, token); +} + +void inst_si(void) +{ + if (!WIFSTOPPED(status)) { + ERROR("program not running.\n"); + perror("inst_si not running"); + disassemble_addr = 0x00; + return; + } + + syscall_nr = 0xffff; + + sync_regs(); + uint64_t rip = regs.rip; + DEBUG("rip = %lx\n", rip); + + const struct bps_node *bp = find(rip); + if (bp) + poke(bp->addr, bp->data); + + ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); + waitpid(child, &status, 0); + + if (bp) + poke(bp->addr, 0xcc); + + sync_regs(); + rip = regs.rip, bp = find(rip); + if (bp) + INFO("hit a breakpoint at %p.\n", (void *)rip); + + if (!WIFSTOPPED(status)) { + INFO("the target program terminated.\n"); + disassemble_addr = 0x00; + } else { + sync_regs(); + disassemble_addr = regs.rip; + } +} + +void inst_cont(void) +{ + if (!WIFSTOPPED(status)) { + ERROR("program not running.\n"); + perror("inst_cont not running"); + exit(1); + } + + syscall_nr = 0xffff; + + sync_regs(); + uint64_t rip = regs.rip; + DEBUG("rip = %lx\n", rip); + + const struct bps_node *bp = find(rip); + if (bp) { + poke(bp->addr, bp->data); + ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); + waitpid(child, &status, 0); + poke(bp->addr, 0xcc); + } + + ptrace(PTRACE_CONT, child, NULL, NULL); + waitpid(child, &status, 0); + + if (!WIFSTOPPED(status)) { + INFO("the target program terminated.\n"); + disassemble_addr = 0x00; + } else { + sync_regs(); + rip = regs.rip; + bp = find(rip - 1); + + if (bp == NULL) { + perror("inst_cont bp = NULL\n"); + exit(1); + } + INFO("hit a breakpoint at %p.\n", (void *)(rip - 1)); + + poke(bp->addr, bp->data); + regs.rip -= 1; + ptrace(PTRACE_SETREGS, child, 0, ®s); + poke(bp->addr, 0xcc); + + sync_regs(); + disassemble_addr = regs.rip; + } +} + +void inst_info(void) +{ + char *op = strtok(NULL, " \t\n"); + if (op == NULL) + goto inst_info_invalid; + + if (strcmp(op, "reg") == 0) + inst_info_reg(); + else if (strcmp(op, "break") == 0) + inst_info_break(); + else + goto inst_info_invalid; + + return; + +inst_info_invalid: + ERROR("invalid command\n"); + INFO("Command: info [reg | break]\n"); +} + +void inst_info_reg(void) +{ + sync_regs(); +#define OUTPUT_REGS(a, b, c) \ + OUTPUT("$%-7s 0x%016lx\t$%-7s 0x%016lx\t$%-7s 0x%016lx\n", \ + #a, (unsigned long)regs.a, \ + #b, (unsigned long)regs.b, \ + #c, (unsigned long)regs.c) + OUTPUT_REGS(rax, rbx, rcx); + OUTPUT_REGS(rdx, rsi, rdi); + OUTPUT_REGS(rbp, rsp, r8); + OUTPUT_REGS( r9, r10, r11); + OUTPUT_REGS(r12, r13, r14); + OUTPUT_REGS(r15, rip, eflags); +#undef OUTPUT_REGS +} + +void inst_info_break(void) +{ + if (bps_cnt == 0) { + INFO("no breakpoints.\n"); + return; + } + + OUTPUT("%-6s\t%-12s\n", "Num", "Address"); + for (int i = 0; i < bps_len; i++) + if (bps[i].addr) + OUTPUT("%-6d\t0x%lx\n", i, bps[i].addr); +} + +void inst_break(void) +{ + char *token = strtok(NULL, " \t\n"); + if (token == NULL) { + ERROR("invalid command.\n"); + INFO("Command: break [hex address]\n"); + return; + } + uint64_t addr; + sscanf(token, "%lx", &addr); + + bps_push(addr, poke(addr, 0xcc)); + INFO("set a breakpoint at %p.\n", (void *)addr); +} + +void inst_delete(void) +{ + char *token = strtok(NULL, " \t\n"); + if (token == NULL) { + ERROR("invalid command.\n"); + INFO("Command: delete [id]\n"); + return; + } + + int id; + sscanf(token, "%d", &id); + + if (bps_len <= id || bps[id].addr == 0x00) + INFO("breakpoint %d does not exist.\n", id); + else { + poke(bps[id].addr, bps[id].data); + bps[id].addr = 0x00, bps_cnt--; + INFO("delete breakpoint %d.\n", id); + } +} + +void inst_patch(void) +{ + char *token = strtok(NULL, " \t\n"); + if (token == NULL) goto inst_patch_invalid; + uint64_t addr; + sscanf(token, "%lx", &addr); + + token = strtok(NULL, " \t\n"); + if (token == NULL) goto inst_patch_invalid; + uint64_t data; + sscanf(token, "%lx", &data); + + token = strtok(NULL, " \t\n"); + if (token == NULL) goto inst_patch_invalid; + int len; + sscanf(token, "%d", &len); + + uint64_t val = ptrace(PTRACE_PEEKTEXT, child, addr, NULL); + DEBUG("original val = %lx\n", val); + + uint64_t mask = ((uint64_t)1 << (len << 3)) - 1; + DEBUG("mask = %lx\n", mask); + val = (val & ~mask) | (data & mask); + DEBUG("modified val = %lx\n", val); + + for (int i = 0; i < bps_len; i++) + if (bps[i].addr && addr <= bps[i].addr && bps[i].addr < addr + len) { + int j = bps[i].addr - addr; + bps[i].data = (data >> j) & 0xff; + } + + if (ptrace(PTRACE_POKETEXT, child, addr, val) != 0) { + ERROR("inst_patch POKETEXT\n"); + perror("inst_patch ptrace"); + exit(1); + } + INFO("patch memory at address 0x%lx.\n", addr); + + return; + +inst_patch_invalid: + ERROR("invalid command.\n"); + INFO("Command: patch [hex address] [hex value] [len]\n"); +} + +void inst_syscall(void) +{ + if (!WIFSTOPPED(status)) { + ERROR("program not running.\n"); + perror("inst_syscall"); + disassemble_addr = 0x00; + return; + } + + sync_regs(); + uint64_t rip = regs.rip; + const struct bps_node *bp = find(rip); + + if (bp) { + poke(bp->addr, bp->data); + ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); + waitpid(child, &status, 0); + poke(bp->addr, 0xcc); + } + + ptrace(PTRACE_SYSCALL, child, NULL, NULL); + waitpid(child, &status, 0); + + if (!WIFSTOPPED(status)) { + INFO("the target program terminated.\n"); + disassemble_addr = 0x00; + } else { + sync_regs(); + rip = regs.rip; + DEBUG("rip = %p\n", (void *)rip); + + if (WSTOPSIG(status) & 0x80) { + if (syscall_nr == 0xffff) { + syscall_nr = regs.orig_rax; + INFO("enter a syscall(%ld) at %p.\n", + syscall_nr, (void *)(rip - 2)); + } else { + INFO("leave a syscall(%ld) = %llu at %p.\n", + syscall_nr, regs.rax, (void *)(rip - 2)); + syscall_nr = 0xffff; + } + disassemble_addr = regs.rip - 2; + return; + } + + bp = find(rip - 1); + if (bp == NULL) { + ERROR("inst_syscall bp = NULL\n"); + perror("inst_syscall"); + exit(1); + } + INFO("hit a breakpoint at %p.\n", (void *)(rip - 1)); + + poke(bp->addr, bp->data); + regs.rip -= 1; + ptrace(PTRACE_SETREGS, child, 0, ®s); + poke(bp->addr, 0xcc); + + disassemble_addr = regs.rip; + } +} diff --git a/sdb.h b/sdb.h new file mode 100644 index 0000000..58217d9 --- /dev/null +++ b/sdb.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#define INSTRUCTION_COUNT 5 + +enum instruction { + INST_NOP = 0x00, + INST_EXIT = 0x01, + INST_LOAD = 0x02, + + INST_SHOW = 0x10, + INST_INFO = 0x11, + + INST_BREAK = 0x20, + INST_DELETE = 0x21, + INST_PATCH = 0x22, + + INST_SI = 0x30, + INST_CONT = 0x31, + INST_SYSCALL = 0x32, + + INST_INVALID = 0xff, +}; + +void sync_regs(void); +uint8_t poke(uint64_t addr, uint8_t data); + +void run(const char *filename); +void disassemble(); + +void inst_load(char **filename); +void inst_si(void); +void inst_cont(void); +void inst_info(void); +void inst_info_reg(void); +void inst_info_break(void); +void inst_break(void); +void inst_delete(void); +void inst_patch(void); +void inst_syscall(void); + +extern int child; +extern int status; + +extern struct user_regs_struct regs; +extern uint64_t disassemble_addr; + +extern long syscall_nr; diff --git a/test.zip b/test.zip new file mode 100644 index 0000000..7898b6f Binary files /dev/null and b/test.zip differ diff --git a/test/1.ans b/test/1.ans new file mode 100644 index 0000000..e2fc239 --- /dev/null +++ b/test/1.ans @@ -0,0 +1,24 @@ +(sdb) si +** please load a program first. +(sdb) load ./hello +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +(sdb) si + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax +(sdb) si + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +(sdb) cont +hello world! +** the target program terminated. diff --git a/test/1.in b/test/1.in new file mode 100644 index 0000000..a86ae27 --- /dev/null +++ b/test/1.in @@ -0,0 +1,6 @@ +./sdb +si +load ./hello +si +si +cont diff --git a/test/2.ans b/test/2.ans new file mode 100644 index 0000000..2e77f3d --- /dev/null +++ b/test/2.ans @@ -0,0 +1,44 @@ +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +(sdb) break 0x401005 +** set a breakpoint at 0x401005. +(sdb) break 40102b +** set a breakpoint at 0x40102b. +(sdb) info break +Num Address +0 0x401005 +1 0x40102b +(sdb) si + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax +(sdb) si +** hit a breakpoint at 0x401005. + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +(sdb) cont +** hit a breakpoint at 0x40102b. + 40102b: b8 01 00 00 00 mov eax, 1 + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall +(sdb) info reg +$rax 0x0000000000402000 $rbx 0x0000000000000000 $rcx 0x0000000000000000 +$rdx 0x000000000000000e $rsi 0x0000000000402000 $rdi 0x0000000000000001 +$rbp 0x00007ffe0e5cd5b8 $rsp 0x00007ffe0e5cd5b0 $r8 0x0000000000000000 +$r9 0x0000000000000000 $r10 0x0000000000000000 $r11 0x0000000000000000 +$r12 0x0000000000000000 $r13 0x0000000000000000 $r14 0x0000000000000000 +$r15 0x0000000000000000 $rip 0x000000000040102b $eflags 0x0000000000000202 +(sdb) cont +hello world! +** the target program terminated. diff --git a/test/2.in b/test/2.in new file mode 100644 index 0000000..b15bc55 --- /dev/null +++ b/test/2.in @@ -0,0 +1,9 @@ +./sdb ./hello +break 0x401005 +break 40102b +info break +si +si +cont +info reg +cont diff --git a/test/3.ans b/test/3.ans new file mode 100644 index 0000000..9d12065 --- /dev/null +++ b/test/3.ans @@ -0,0 +1,49 @@ +** program './guess' loaded. entry point 0x40108b. + 40108b: f3 0f 1e fa endbr64 + 40108f: 55 push rbp + 401090: 48 89 e5 mov rbp, rsp + 401093: 48 83 ec 10 sub rsp, 0x10 + 401097: ba 12 00 00 00 mov edx, 0x12 +(sdb) break 0x4010de +** set a breakpoint at 0x4010de. +(sdb) cont +guess a number > 1 +** hit a breakpoint at 0x4010de. + 4010de: 48 89 c7 mov rdi, rax + 4010e1: e8 1a ff ff ff call 0x401000 + 4010e6: 85 c0 test eax, eax + 4010e8: 75 1b jne 0x401105 + 4010ea: ba 06 00 00 00 mov edx, 6 +(sdb) patch 0x4010e8 0x9090 2 +** patch memory at address 0x4010e8. +(sdb) si + 4010e1: e8 1a ff ff ff call 0x401000 + 4010e6: 85 c0 test eax, eax + 4010e8: 90 nop + 4010e9: 90 nop + 4010ea: ba 06 00 00 00 mov edx, 6 +(sdb) info break +Num Address +0 0x4010de +(sdb) delete 0 +** delete breakpoint 0. +(sdb) break 0x4010ea +** set a breakpoint at 0x4010ea. +(sdb) delete 0 +** breakpoint 0 does not exist. +(sdb) info break +Num Address +1 0x4010ea +(sdb) cont +** hit a breakpoint at 0x4010ea. + 4010ea: ba 06 00 00 00 mov edx, 6 + 4010ef: 48 8d 05 1f 0f 00 00 lea rax, [rip + 0xf1f] + 4010f6: 48 89 c6 mov rsi, rax + 4010f9: bf 01 00 00 00 mov edi, 1 + 4010fe: e8 25 00 00 00 call 0x401128 +(sdb) patch 0x402015 0x4e49570a 4 +** patch memory at address 0x402015. +(sdb) cont + +WIN +** the target program terminated. diff --git a/test/3.in b/test/3.in new file mode 100644 index 0000000..7a25c0d --- /dev/null +++ b/test/3.in @@ -0,0 +1,14 @@ +./sdb ./guess +break 0x4010de +cont +1 +patch 0x4010e8 0x9090 2 +si +info break +delete 0 +break 0x4010ea +delete 0 +info break +cont +patch 0x402015 0x4e49570a 4 +cont diff --git a/test/4.ans b/test/4.ans new file mode 100644 index 0000000..db45492 --- /dev/null +++ b/test/4.ans @@ -0,0 +1,45 @@ +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +(sdb) break 0x401005 +** set a breakpoint at 0x401005. +(sdb) break 40102b +** set a breakpoint at 0x40102b. +(sdb) cont +** hit a breakpoint at 0x401005. + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +(sdb) syscall +** hit a breakpoint at 0x40102b. + 40102b: b8 01 00 00 00 mov eax, 1 + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall +(sdb) syscall +** enter a syscall(1) at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +(sdb) syscall +hello world! +** leave a syscall(1) = 14 at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +(sdb) syscall +** enter a syscall(60) at 0x401040. + 401040: 0f 05 syscall +** the address is out of the range of the text section. +(sdb) syscall +** the target program terminated. diff --git a/test/4.in b/test/4.in new file mode 100644 index 0000000..868f712 --- /dev/null +++ b/test/4.in @@ -0,0 +1,9 @@ +./sdb ./hello +break 0x401005 +break 40102b +cont +syscall +syscall +syscall +syscall +syscall diff --git a/test/5.ans b/test/5.ans new file mode 100644 index 0000000..e694b67 --- /dev/null +++ b/test/5.ans @@ -0,0 +1,32 @@ +** program './guess' loaded. entry point 0x40108b. + 40108b: f3 0f 1e fa endbr64 + 40108f: 55 push rbp + 401090: 48 89 e5 mov rbp, rsp + 401093: 48 83 ec 10 sub rsp, 0x10 + 401097: ba 12 00 00 00 mov edx, 0x12 +(sdb) patch 0x4010e8 0x9090 2 +** patch memory at address 0x4010e8. +(sdb) break 0x4010e8 +** set a breakpoint at 0x4010e8. +(sdb) cont +guess a number > 1 +** hit a breakpoint at 0x4010e8. + 4010e8: 90 nop + 4010e9: 90 nop + 4010ea: ba 06 00 00 00 mov edx, 6 + 4010ef: 48 8d 05 1f 0f 00 00 lea rax, [rip + 0xf1f] + 4010f6: 48 89 c6 mov rsi, rax +(sdb) break 4010ea +** set a breakpoint at 0x4010ea. +(sdb) patch 4010ea 0x03ba 4 +** patch memory at address 0x4010ea. +(sdb) cont +** hit a breakpoint at 0x4010ea. + 4010ea: ba 03 00 00 00 mov edx, 3 + 4010ef: 48 8d 05 1f 0f 00 00 lea rax, [rip + 0xf1f] + 4010f6: 48 89 c6 mov rsi, rax + 4010f9: bf 01 00 00 00 mov edi, 1 + 4010fe: e8 25 00 00 00 call 0x401128 +(sdb) cont + +ye** the target program terminated. diff --git a/test/5.in b/test/5.in new file mode 100644 index 0000000..096adfe --- /dev/null +++ b/test/5.in @@ -0,0 +1,9 @@ +./sdb ./guess +patch 0x4010e8 0x9090 2 +break 0x4010e8 +cont +1 +break 4010ea +patch 4010ea 0x03ba 4 +cont +cont diff --git a/test/6.ans b/test/6.ans new file mode 100644 index 0000000..948f8ac --- /dev/null +++ b/test/6.ans @@ -0,0 +1,27 @@ +** program './guess' loaded. entry point 0x40108b. + 40108b: f3 0f 1e fa endbr64 + 40108f: 55 push rbp + 401090: 48 89 e5 mov rbp, rsp + 401093: 48 83 ec 10 sub rsp, 0x10 + 401097: ba 12 00 00 00 mov edx, 0x12 +(sdb) break 0x401128 +** set a breakpoint at 0x401128. +(sdb) cont +** hit a breakpoint at 0x401128. + 401128: b8 01 00 00 00 mov eax, 1 + 40112d: 0f 05 syscall + 40112f: c3 ret + 401130: b8 00 00 00 00 mov eax, 0 + 401135: 0f 05 syscall +(sdb) cont +guess a number > 1 +** hit a breakpoint at 0x401128. + 401128: b8 01 00 00 00 mov eax, 1 + 40112d: 0f 05 syscall + 40112f: c3 ret + 401130: b8 00 00 00 00 mov eax, 0 + 401135: 0f 05 syscall +(sdb) cont + +no no no +** the target program terminated. diff --git a/test/6.in b/test/6.in new file mode 100644 index 0000000..6bf6fa5 --- /dev/null +++ b/test/6.in @@ -0,0 +1,6 @@ +./sdb ./guess +break 0x401128 +cont +cont +1 +cont diff --git a/test/7.ans b/test/7.ans new file mode 100644 index 0000000..f1c0a01 --- /dev/null +++ b/test/7.ans @@ -0,0 +1,35 @@ +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +(sdb) break 0x401030 +** set a breakpoint at 0x401030. +(sdb) break 0x401040 +** set a breakpoint at 0x401040. +(sdb) syscall +** hit a breakpoint at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +(sdb) syscall +** enter a syscall(1) at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +(sdb) cont +hello world! +** hit a breakpoint at 0x401040. + 401040: 0f 05 syscall +** the address is out of the range of the text section. +(sdb) syscall +** enter a syscall(60) at 0x401040. + 401040: 0f 05 syscall +** the address is out of the range of the text section. +(sdb) syscall +** the target program terminated. diff --git a/test/7.in b/test/7.in new file mode 100644 index 0000000..7d621d4 --- /dev/null +++ b/test/7.in @@ -0,0 +1,8 @@ +./sdb ./hello +break 0x401030 +break 0x401040 +syscall +syscall +cont +syscall +syscall diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..a71706b --- /dev/null +++ b/test/Makefile @@ -0,0 +1,8 @@ +all: + chmod +x run_examples.py guess hello + +test: all + ./run_examples.py + +clean: + rm -f *.out diff.txt diff --git a/test/guess b/test/guess new file mode 100755 index 0000000..b6d0bbf Binary files /dev/null and b/test/guess differ diff --git a/test/hello b/test/hello new file mode 100755 index 0000000..47aabfb Binary files /dev/null and b/test/hello differ diff --git a/test/run_examples.py b/test/run_examples.py new file mode 100755 index 0000000..1be8fd0 --- /dev/null +++ b/test/run_examples.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +from typing import List +from pwn import process, context + +context.log_level = "error" + +cases_to_run = ["1", "2", "3", "4", "5", "6", "7"] + +TIMEOUT_SECONDS = 0.01 + + +def wrap_recvrepeat(r): + if r.poll() is not None: + return b"" + return r.recvrepeat(TIMEOUT_SECONDS) + + +def recvrepeats(r): + output = wrap_recvrepeat(r) + while output == b"": + if r.poll() is not None: + break + output = wrap_recvrepeat(r) + + ret = b"" + + while output != b"": + ret += output + output = wrap_recvrepeat(r) + + return ret + + +def execute_process( + case: str, command: List[str], stdin: List[str] +) -> tuple[int, bytes]: + """Returns the exit code and output of the process (including stdin and stderr)""" + print(f"Running case {case} with command: {command}") + try: + r = process(command, shell=False) + output = b"" + for line in stdin: + ret = recvrepeats(r) + output += ret + output += line.encode("utf-8") + if r.poll() is None: # Only send if the process is still running + r.send(line.encode("utf-8")) + output += recvrepeats(r) + r.close() + + except Exception as e: + print(f"Error: {e}") + return 1, b"" + + return 0, output + + +if __name__ == "__main__": + # Clean up the diff file + with open("diff.txt", "w") as f: + f.write("") + + for case in cases_to_run: + + with open(f"{case}.in", "r") as f: + lines = f.readlines() + run_command: List[str] = lines[0].split() + input = lines[1:] + + _, output = execute_process(case, run_command, input) + + # Remove the last prompt + if output.endswith(b"(sdb) "): + output = output[:-6] + + # Remove null bytes + output = output.replace(b"\x00", b"") + + # Write the output to a file + with open(f"{case}.out", "wb") as f: + f.write(output) + + diff_command = f"diff -w -B -u {case}.out {case}.ans" + diff_process = process(diff_command, shell=True) + diff_output = diff_process.recvall() + diff_process.close() + + diff_lines = diff_output.decode("utf-8").split("\n") + diff_lines = [ + line for line in diff_lines if line.startswith("-") or line.startswith("+") + ] + diff_lines = [line for line in diff_lines if not line.startswith("---")] + diff_lines = [line for line in diff_lines if not line.startswith("+++")] + + i = 0 + while True: + if i + 1 >= len(diff_lines): + break + + if "-$rbp" in diff_lines[i] and "+$rbp" in diff_lines[i + 1]: + output_line = diff_lines.pop(i)[1:].split() + expected_line = diff_lines.pop(i)[1:].split() + + if len(output_line) != 6: + diff_lines.append(f"error") + break + + output_rbp = int(output_line[1], 16) + output_rsp = int(output_line[3], 16) + output_r8 = int(output_line[5], 16) + expected_rbp = int(expected_line[1], 16) + expected_rsp = int(expected_line[3], 16) + expected_r8 = int(expected_line[5], 16) + + if ( + output_rbp - output_rsp != expected_rbp - expected_rsp + or output_r8 != expected_r8 + ): + diff_lines.append(f"error") + break + + continue + + i += 1 + + # Print the diff output if there is a difference + print(f"Case {case}: {'PASS' if len(diff_lines) == 0 else 'FAIL'}", end="\n\n") + + # Print the diff output to `diff.txt` + if len(diff_lines) > 0: + with open("diff.txt", "a") as f: + f.write(diff_output.decode("utf-8")) + f.write("\n\n") diff --git a/test_case/Makefile b/test_case/Makefile new file mode 100644 index 0000000..98c2918 --- /dev/null +++ b/test_case/Makefile @@ -0,0 +1,13 @@ +.PHONY: all + +all: ../sdb + python run.py 1 + python run.py 2 + python run.py 3 + python run.py 4 + python run.py h1 + python run.py h2 + python run.py h3 + python run.py h4 + python run.py h5 + python run.py h6 diff --git a/test_case/deep b/test_case/deep new file mode 100755 index 0000000..56b3e8d Binary files /dev/null and b/test_case/deep differ diff --git a/test_case/game b/test_case/game new file mode 100755 index 0000000..041938d Binary files /dev/null and b/test_case/game differ diff --git a/test_case/guess b/test_case/guess new file mode 100755 index 0000000..b6d0bbf Binary files /dev/null and b/test_case/guess differ diff --git a/test_case/hello b/test_case/hello new file mode 100755 index 0000000..47aabfb Binary files /dev/null and b/test_case/hello differ diff --git a/test_case/in/1.in b/test_case/in/1.in new file mode 100644 index 0000000..e2d6dd7 --- /dev/null +++ b/test_case/in/1.in @@ -0,0 +1,6 @@ +../sdb +si +load ./hello +si +si +cont \ No newline at end of file diff --git a/test_case/in/2.in b/test_case/in/2.in new file mode 100644 index 0000000..c77a2e3 --- /dev/null +++ b/test_case/in/2.in @@ -0,0 +1,9 @@ +../sdb ./hello +break 0x401005 +break 40102b +info break +si +si +cont +info reg +cont \ No newline at end of file diff --git a/test_case/in/3.in b/test_case/in/3.in new file mode 100644 index 0000000..bf8c733 --- /dev/null +++ b/test_case/in/3.in @@ -0,0 +1,14 @@ +../sdb ./guess +break 0x4010de +cont +1 +patch 0x4010e8 0x9090 2 +si +info break +delete 0 +break 0x4010ea +delete 0 +info break +cont +patch 0x402015 0x4e49570a 4 +cont \ No newline at end of file diff --git a/test_case/in/4.in b/test_case/in/4.in new file mode 100644 index 0000000..6e49d10 --- /dev/null +++ b/test_case/in/4.in @@ -0,0 +1,9 @@ +../sdb ./hello +break 0x401005 +break 40102b +cont +syscall +syscall +syscall +syscall +syscall diff --git a/test_case/in/h1.in b/test_case/in/h1.in new file mode 100644 index 0000000..f3786f6 --- /dev/null +++ b/test_case/in/h1.in @@ -0,0 +1,13 @@ +../sdb ./game +break 401005 +break 401009 +info break +syscall +syscall +delete 0 +cont +2 +break 401005 +info break +cont +2 \ No newline at end of file diff --git a/test_case/in/h2.in b/test_case/in/h2.in new file mode 100644 index 0000000..7579489 --- /dev/null +++ b/test_case/in/h2.in @@ -0,0 +1,9 @@ +../sdb ./game +break 401005 +cont +patch 40101a 0x02f88348 4 +cont +1 +patch 40101a 0x01f88348 4 +cont +1 \ No newline at end of file diff --git a/test_case/in/h3.in b/test_case/in/h3.in new file mode 100644 index 0000000..9943792 --- /dev/null +++ b/test_case/in/h3.in @@ -0,0 +1,6 @@ +../sdb +si +load ./deep +si +si +cont \ No newline at end of file diff --git a/test_case/in/h4.in b/test_case/in/h4.in new file mode 100644 index 0000000..049699a --- /dev/null +++ b/test_case/in/h4.in @@ -0,0 +1,9 @@ +../sdb ./deep +break 401136 +break 40113d +info break +si +si +cont +info reg +cont \ No newline at end of file diff --git a/test_case/in/h5.in b/test_case/in/h5.in new file mode 100644 index 0000000..dc85b52 --- /dev/null +++ b/test_case/in/h5.in @@ -0,0 +1,12 @@ +../sdb ./deep +break 40109c +info break +break 401031 +info break +delete 0 +info break +delete 3 +info break +cont +info break +cont \ No newline at end of file diff --git a/test_case/in/h6.in b/test_case/in/h6.in new file mode 100644 index 0000000..08b67d1 --- /dev/null +++ b/test_case/in/h6.in @@ -0,0 +1,9 @@ +../sdb ./game +break 0x4010f1 +syscall +syscall +syscall +syscall +3 +syscall +syscall \ No newline at end of file diff --git a/test_case/out/1.ans b/test_case/out/1.ans new file mode 100644 index 0000000..c7d21f9 --- /dev/null +++ b/test_case/out/1.ans @@ -0,0 +1,19 @@ +** please load a program first. +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +hello world! +** the target program terminated. diff --git a/test_case/out/2.ans b/test_case/out/2.ans new file mode 100644 index 0000000..743e33d --- /dev/null +++ b/test_case/out/2.ans @@ -0,0 +1,36 @@ +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +** set a breakpoint at 0x401005. +** set a breakpoint at 0x40102b. +Num Address +0 0x401005 +1 0x40102b + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax +** hit a breakpoint at 0x401005. + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +** hit a breakpoint at 0x40102b. + 40102b: b8 01 00 00 00 mov eax, 1 + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall +$rax 0x0000000000402000 $rbx 0x0000000000000000 $rcx 0x0000000000000000 +$rdx 0x000000000000000e $rsi 0x0000000000402000 $rdi 0x0000000000000001 +$rbp 0x00007ffe0e5cd5b8 $rsp 0x00007ffe0e5cd5b0 $r8 0x0000000000000000 +$r9 0x0000000000000000 $r10 0x0000000000000000 $r11 0x0000000000000000 +$r12 0x0000000000000000 $r13 0x0000000000000000 $r14 0x0000000000000000 +$r15 0x0000000000000000 $rip 0x000000000040102b $eflags 0x0000000000000202 +hello world! +** the target program terminated. diff --git a/test_case/out/3.ans b/test_case/out/3.ans new file mode 100644 index 0000000..70d7bb0 --- /dev/null +++ b/test_case/out/3.ans @@ -0,0 +1,37 @@ +** program './guess' loaded. entry point 0x40108b. + 40108b: f3 0f 1e fa endbr64 + 40108f: 55 push rbp + 401090: 48 89 e5 mov rbp, rsp + 401093: 48 83 ec 10 sub rsp, 0x10 + 401097: ba 12 00 00 00 mov edx, 0x12 +** set a breakpoint at 0x4010de. +guess a number > +** hit a breakpoint at 0x4010de. + 4010de: 48 89 c7 mov rdi, rax + 4010e1: e8 1a ff ff ff call 0x401000 + 4010e6: 85 c0 test eax, eax + 4010e8: 75 1b jne 0x401105 + 4010ea: ba 06 00 00 00 mov edx, 6 +** patch memory at address 0x4010e8. + 4010e1: e8 1a ff ff ff call 0x401000 + 4010e6: 85 c0 test eax, eax + 4010e8: 90 nop + 4010e9: 90 nop + 4010ea: ba 06 00 00 00 mov edx, 6 +Num Address +0 0x4010de +** delete breakpoint 0. +** set a breakpoint at 0x4010ea. +** breakpoint 0 does not exist. +Num Address +1 0x4010ea +** hit a breakpoint at 0x4010ea. + 4010ea: ba 06 00 00 00 mov edx, 6 + 4010ef: 48 8d 05 1f 0f 00 00 lea rax, [rip + 0xf1f] + 4010f6: 48 89 c6 mov rsi, rax + 4010f9: bf 01 00 00 00 mov edi, 1 + 4010fe: e8 25 00 00 00 call 0x401128 +** patch memory at address 0x402015. + +WIN +** the target program terminated. diff --git a/test_case/out/4.ans b/test_case/out/4.ans new file mode 100644 index 0000000..ec529e1 --- /dev/null +++ b/test_case/out/4.ans @@ -0,0 +1,37 @@ +** program './hello' loaded. entry point 0x401000. + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +** set a breakpoint at 0x401005. +** set a breakpoint at 0x40102b. +** hit a breakpoint at 0x401005. + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] + 401014: 48 89 c6 mov rsi, rax + 401017: bf 01 00 00 00 mov edi, 1 +** hit a breakpoint at 0x40102b. + 40102b: b8 01 00 00 00 mov eax, 1 + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall +** enter a syscall(1) at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +hello world! +** leave a syscall(1) = 14 at 0x401030. + 401030: 0f 05 syscall + 401032: c3 ret + 401033: b8 00 00 00 00 mov eax, 0 + 401038: 0f 05 syscall + 40103a: c3 ret +** enter a syscall(60) at 0x401040. + 401040: 0f 05 syscall +** the address is out of the range of the text section. +** the target program terminated. diff --git a/test_case/out/h1.ans b/test_case/out/h1.ans new file mode 100644 index 0000000..f28ed48 --- /dev/null +++ b/test_case/out/h1.ans @@ -0,0 +1,44 @@ +** program './game' loaded. entry point 0x401000. + 401000: b9 05 00 00 00 mov ecx, 5 + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 +** set a breakpoint at 0x401005. +** set a breakpoint at 0x401009. +Num Address +0 0x401005 +1 0x401009 +** hit a breakpoint at 0x401005. + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c +** hit a breakpoint at 0x401009. + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c + 40101a: 48 83 f8 01 cmp rax, 1 +** delete breakpoint 0. +guess a number : +wrong +** hit a breakpoint at 0x401009. + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c + 40101a: 48 83 f8 01 cmp rax, 1 +** set a breakpoint at 0x401005. +Num Address +1 0x401009 +2 0x401005 +guess a number : +wrong +** hit a breakpoint at 0x401005. + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c diff --git a/test_case/out/h2.ans b/test_case/out/h2.ans new file mode 100644 index 0000000..0d709fe --- /dev/null +++ b/test_case/out/h2.ans @@ -0,0 +1,26 @@ +** program './game' loaded. entry point 0x401000. + 401000: b9 05 00 00 00 mov ecx, 5 + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 +** set a breakpoint at 0x401005. +** hit a breakpoint at 0x401005. + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c +** patch memory at address 0x40101a. +guess a number : +wrong +** hit a breakpoint at 0x401005. + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 + 401015: e8 72 00 00 00 call 0x40108c +** patch memory at address 0x40101a. +guess a number : +you win +** the target program terminated. diff --git a/test_case/out/h3.ans b/test_case/out/h3.ans new file mode 100644 index 0000000..7aa13dd --- /dev/null +++ b/test_case/out/h3.ans @@ -0,0 +1,21 @@ +** please load a program first. +** program './deep' loaded. entry point 0x401131. + 401131: f3 0f 1e fa endbr64 + 401135: 55 push rbp + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 + 401135: 55 push rbp + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 + 401142: e8 3f ff ff ff call 0x401086 + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 + 401142: e8 3f ff ff ff call 0x401086 + 401147: 89 45 fc mov dword ptr [rbp - 4], eax +this is callee +hello world +hello unix +** the target program terminated. diff --git a/test_case/out/h4.ans b/test_case/out/h4.ans new file mode 100644 index 0000000..120a85a --- /dev/null +++ b/test_case/out/h4.ans @@ -0,0 +1,38 @@ +** program './deep' loaded. entry point 0x401131. + 401131: f3 0f 1e fa endbr64 + 401135: 55 push rbp + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 +** set a breakpoint at 0x401136. +** set a breakpoint at 0x40113d. +Num Address +0 0x401136 +1 0x40113d + 401135: 55 push rbp + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 + 401142: e8 3f ff ff ff call 0x401086 +** hit a breakpoint at 0x401136. + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 + 401142: e8 3f ff ff ff call 0x401086 + 401147: 89 45 fc mov dword ptr [rbp - 4], eax +** hit a breakpoint at 0x40113d. + 40113d: b8 00 00 00 00 mov eax, 0 + 401142: e8 3f ff ff ff call 0x401086 + 401147: 89 45 fc mov dword ptr [rbp - 4], eax + 40114a: b8 00 00 00 00 mov eax, 0 + 40114f: e8 5d ff ff ff call 0x4010b1 +$rax 0x0000000000000000 $rbx 0x0000000000000000 $rcx 0x0000000000000000 +$rdx 0x0000000000000000 $rsi 0x0000000000000000 $rdi 0x0000000000000000 +$rbp 0x00007fffffffe0a8 $rsp 0x00007fffffffe098 $r8 0x0000000000000000 +$r9 0x0000000000000000 $r10 0x0000000000000000 $r11 0x0000000000000000 +$r12 0x0000000000000000 $r13 0x0000000000000000 $r14 0x0000000000000000 +$r15 0x0000000000000000 $rip 0x000000000040113d $eflags 0x0000000000000202 +this is callee +hello world +hello unix +** the target program terminated. diff --git a/test_case/out/h5.ans b/test_case/out/h5.ans new file mode 100644 index 0000000..93dfc56 --- /dev/null +++ b/test_case/out/h5.ans @@ -0,0 +1,31 @@ +** program './deep' loaded. entry point 0x401131. + 401131: f3 0f 1e fa endbr64 + 401135: 55 push rbp + 401136: 48 89 e5 mov rbp, rsp + 401139: 48 83 ec 10 sub rsp, 0x10 + 40113d: b8 00 00 00 00 mov eax, 0 +** set a breakpoint at 0x40109c. +Num Address +0 0x40109c +** set a breakpoint at 0x401031. +Num Address +0 0x40109c +1 0x401031 +** delete breakpoint 0. +Num Address +1 0x401031 +** breakpoint 3 does not exist. +Num Address +1 0x401031 +** hit a breakpoint at 0x401031. + 401031: 48 89 c6 mov rsi, rax + 401034: bf 01 00 00 00 mov edi, 1 + 401039: e8 20 01 00 00 call 0x40115e + 40103e: 90 nop + 40103f: c9 leave +Num Address +1 0x401031 +this is callee +hello world +hello unix +** the target program terminated. diff --git a/test_case/out/h6.ans b/test_case/out/h6.ans new file mode 100644 index 0000000..b3cc494 --- /dev/null +++ b/test_case/out/h6.ans @@ -0,0 +1,44 @@ +** program './game' loaded. entry point 0x401000. + 401000: b9 05 00 00 00 mov ecx, 5 + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 +** set a breakpoint at 0x4010f1. +** enter a syscall(1) at 0x401052. + 401052: 0f 05 syscall + 401054: 59 pop rcx + 401055: c3 ret + 401056: 51 push rcx + 401057: b8 01 00 00 00 mov eax, 1 +guess a number : +** leave a syscall(1) = 18 at 0x401052. + 401052: 0f 05 syscall + 401054: 59 pop rcx + 401055: c3 ret + 401056: 51 push rcx + 401057: b8 01 00 00 00 mov eax, 1 +** enter a syscall(0) at 0x401088. + 401088: 0f 05 syscall + 40108a: 59 pop rcx + 40108b: c3 ret + 40108c: 51 push rcx + 40108d: b8 00 00 00 00 mov eax, 0 +** leave a syscall(0) = 2 at 0x401088. + 401088: 0f 05 syscall + 40108a: 59 pop rcx + 40108b: c3 ret + 40108c: 51 push rcx + 40108d: b8 00 00 00 00 mov eax, 0 +** hit a breakpoint at 0x4010f1. + 4010f1: 48 8d 35 2b 0f 00 00 lea rsi, [rip + 0xf2b] + 4010f8: ba 06 00 00 00 mov edx, 6 + 4010fd: 0f 05 syscall + 4010ff: 59 pop rcx + 401100: c3 ret +** enter a syscall(1) at 0x4010fd. + 4010fd: 0f 05 syscall + 4010ff: 59 pop rcx + 401100: c3 ret + 401101: b8 3c 00 00 00 mov eax, 0x3c + 401106: 48 31 ff xor rdi, rdi diff --git a/test_case/output.txt b/test_case/output.txt new file mode 100644 index 0000000..b5b9ada --- /dev/null +++ b/test_case/output.txt @@ -0,0 +1,44 @@ +** program './game' loaded. entry point 0x401000. + 401000: b9 05 00 00 00 mov ecx, 5 + 401005: 48 83 f9 00 cmp rcx, 0 + 401009: 74 1f je 0x40102a + 40100b: e8 2b 00 00 00 call 0x40103b + 401010: e8 5c 00 00 00 call 0x401071 +** set a breakpoint at 0x4010f1. +** enter a syscall(1) at 0x401052. + 401052: 0f 05 syscall + 401054: 59 pop rcx + 401055: c3 ret + 401056: 51 push rcx + 401057: b8 01 00 00 00 mov eax, 1 +guess a number : +** leave a syscall(1) = 18 at 0x401052. + 401052: 0f 05 syscall + 401054: 59 pop rcx + 401055: c3 ret + 401056: 51 push rcx + 401057: b8 01 00 00 00 mov eax, 1 +** enter a syscall(0) at 0x401088. + 401088: 0f 05 syscall + 40108a: 59 pop rcx + 40108b: c3 ret + 40108c: 51 push rcx + 40108d: b8 00 00 00 00 mov eax, 0 +** leave a syscall(0) = 2 at 0x401088. + 401088: 0f 05 syscall + 40108a: 59 pop rcx + 40108b: c3 ret + 40108c: 51 push rcx + 40108d: b8 00 00 00 00 mov eax, 0 +** hit a breakpoint at 0x4010f1. + 4010f1: 48 8d 35 2b 0f 00 00 lea rsi, [rip + 0xf2b] + 4010f8: ba 06 00 00 00 mov edx, 6 + 4010fd: 0f 05 syscall + 4010ff: 59 pop rcx + 401100: c3 ret +** enter a syscall(1) at 0x4010fd. + 4010fd: 0f 05 syscall + 4010ff: 59 pop rcx + 401100: c3 ret + 401101: b8 3c 00 00 00 mov eax, 0x3c + 401106: 48 31 ff xor rdi, rdi diff --git a/test_case/run.py b/test_case/run.py new file mode 100644 index 0000000..d3167af --- /dev/null +++ b/test_case/run.py @@ -0,0 +1,71 @@ +from pwn import * +import time +import sys +import difflib + +def read_file(filename): + """Read a file and return its contents as a list of lines.""" + with open(filename, "r") as f: + return f.readlines() + +def normalize_line(line): + """Normalize a line by stripping leading/trailing whitespace and reducing internal whitespace to a single space.""" + return ' '.join(line.split()) + +def compare_files(file1, file2): + """Compare two files and print the differences or 'accept' if they are the same.""" + content1 = read_file(file1) + content2 = read_file(file2) + + # Normalize lines to ignore whitespace differences + normalized1 = [normalize_line(line) for line in content1] + normalized2 = [normalize_line(line) for line in content2] + + diff = difflib.unified_diff(normalized1, normalized2, fromfile=file1, tofile=file2) + + + # Convert the generator to a list to check if there are any differences + diff_list = list(diff) + + if not diff_list: + print("accept") + else: + for line in diff_list: + print(line) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: ./run.py ") + sys.exit(1) + + filename = "./in/" + sys.argv[1] + ".in" + f = open(filename) + input_lines = f.read().splitlines() + + # start process + p_run = input_lines[0].split(" ") + if len(p_run) == 1: + p = process([p_run[0]]) + else: + p = process([p_run[0]] + [p_run[1]]) + + for i in range(1, len(input_lines)): + # info(input_lines[i]) + p.sendline(input_lines[i].encode()) + time.sleep(0.2) + + # wait + time.sleep(1) + + # \x00, (sdb)... + with open("output.txt", "w", encoding="utf-8") as f: + output = p.recvall(timeout=1).decode("utf-8") + output = output.replace("\x00", "") # \x00 terminate + output = output.replace("(sdb) ", "") + output = output.replace("guess a number > ", "guess a number > \n") + f.write(output) + p.close() + + ans_file = "./out/" + sys.argv[1] + ".ans" + info(ans_file) + compare_files("output.txt", ans_file) diff --git a/tmp b/tmp new file mode 100644 index 0000000..0f4c36c --- /dev/null +++ b/tmp @@ -0,0 +1,7 @@ +** program 'test/hello' loaded. entry point 0x401000 + 401000: f3 0f 1e fa endbr64 + 401004: 55 push rbp + 401005: 48 89 e5 mov rbp, rsp + 401008: ba 0e 00 00 00 mov edx, 0xe + 40100d: 48 8d 05 ec 0f 00 00 lea rax, [rip + 0xfec] +(sdb)  \ No newline at end of file diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..0e17b36 --- /dev/null +++ b/vector.c @@ -0,0 +1,27 @@ +#include "vector.h" + +#include + +struct bps_node *bps = NULL; +int bps_len = 0, bps_cnt = 0, bps_max = 0; + +const struct bps_node *find(uint64_t addr) +{ + for (int i = 0; i < bps_len; i++) + if (bps[i].addr && bps[i].addr == addr) + return &bps[i]; + return NULL; +} + +int bps_push(uint64_t addr, uint8_t data) +{ + if (bps_max == 0) + bps = malloc((bps_max = 1) * sizeof(struct bps_node)); + if (bps_len + 1 >= bps_max) + bps = realloc(bps, (bps_max <<= 1) * sizeof(struct bps_node)); + + bps[bps_len].addr = addr; + bps[bps_len].data = data; + bps_cnt++; + return bps_len++; +} diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..cfe08a3 --- /dev/null +++ b/vector.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +struct bps_node { + uint64_t addr; + uint8_t data; +}; + +const struct bps_node *find(uint64_t addr); +int bps_push(uint64_t addr, uint8_t data); + +extern struct bps_node *bps; +extern int bps_len, bps_cnt, bps_max;