Compare commits

...

10 Commits

Author SHA1 Message Date
kamkow1
2ee6e9666e don't print function name in dbg_handle_sigsegv() 2025-03-13 18:13:20 +01:00
kamkow1
ec08cbae07 Multiple test files for testing 2025-03-13 18:06:59 +01:00
kamkow1
a7378a5210 Rename baddr() to boff() 2025-03-13 16:07:15 +01:00
kamkow1
46c8a1e26b bl()/rmbl() commands for setting/removing breakpoints at source line 2025-03-13 16:02:15 +01:00
kamkow1
07c4b9283b ebfn()/dbfn() commands for enabling/disabling breakpoints by func name 2025-03-13 14:14:55 +01:00
kamkow1
7a5980de69 ebaddr()/dbaddr() commands for enabling/disabling breakpoints at address 2025-03-13 14:09:24 +01:00
kamkow1
4f60dfcd4d Clean up libelfin_wrap functions api 2025-03-13 14:03:04 +01:00
kamkow1
a9f555d916 Print source on SIGSEGV 2025-03-12 20:52:02 +01:00
kamkow1
0742db18d8 Print expanded source 2025-03-12 20:46:49 +01:00
kamkow1
14b5368e7c Print source on breakpoint hit 2025-03-12 20:31:27 +01:00
9 changed files with 295 additions and 104 deletions

2
.debugusrc1.js Normal file
View File

@@ -0,0 +1,2 @@
lif("Loading user init script for program ./test");
lsf();

View File

@@ -1,35 +0,0 @@
// User script
lif("Loading user init script for program ./test");
ler("Test error message");
var offset = "@DUPA_ADDR"; // dupa(), objdump -d ./test
// Testing...
// pf();
// ppid();
// pplo();
// baddr(offset);
bfn("dupa");
// rmbfn("dupa");
lsbrk();
lsf();
// lif("1 --------------------------------------------");
// dr();
// var r14 = gr("r14");
// sr("r14", "0x0");
// lif("2 --------------------------------------------");
// dr();
// sr("r14", r14);
// lif("3 --------------------------------------------");
// dr();
// var r14_mem = mrdr("r14");
// lif("Memory at r14 " + r14_mem);
// mwrr("r14", "0x696969");
// lif("Memory at r14 " + mrdr("r14"));
// mwrr("r14", r14_mem);
// lif("Memory at r14 " + mrdr("r14"));
// cont();

1
.gitignore vendored
View File

@@ -2,4 +2,3 @@
*.d
debugus
test
.debugusrc1.js

View File

@@ -6,17 +6,14 @@ SRCS=debugus.c linenoise.c hash.c pmparser.c
OBJS=$(patsubst %.c,%.o,$(SRCS))
DEPS=$(patsubst %.c,%.d,$(SRCS))
all: debugus .debugusrc1.js test
all: debugus test
test: test.o
$(CC) $(CFLAGS) -o $@ $^
test: test.o test2.o
$(CC) -o $@ $^
debugus: $(OBJS) ./mujs/build/debug/libmujs.o ./libelfin_wrap.o
$(CC) -o $@ $^ $(LDFLAGS)
.debugusrc1.js: test .debugusrc1.js.txt
cat .debugusrc1.js.txt | sed "s/@DUPA_ADDR/0x$$(nm ./test | grep 'dupa' | awk '{ print $$1 }')/g" > .debugusrc1.js
./mujs/build/debug/libmujs.o:
make -C mujs -j$(shell nproc)
@@ -25,7 +22,7 @@ debugus: $(OBJS) ./mujs/build/debug/libmujs.o ./libelfin_wrap.o
-include $(DEPS)
clean: $(OBJS) test.o $(DEPS) .debugusrc1.js
clean: $(OBJS) test.o test2.o $(DEPS)
rm -f $^
make -C mujs clean

286
debugus.c
View File

@@ -9,6 +9,7 @@
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
@@ -22,6 +23,27 @@
#include "da.h"
#include "libelfin_wrap.h"
// They took my usleep() away >;( fuck u POSIX
int msleep(long msec)
{
struct timespec ts;
int res;
if (msec < 0) {
errno = EINVAL;
return -1;
}
ts.tv_sec = msec/1000;
ts.tv_nsec = (msec%1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
} while(res && errno == EINTR);
return res;
}
#define LOG_ERR(fmt, ...) fprintf(stderr, "Error: " fmt, ##__VA_ARGS__)
#define LOG_INF(fmt, ...) fprintf(stdout, "Info: " fmt, ##__VA_ARGS__)
@@ -32,6 +54,53 @@
#define INIT_SCRIPT ".debugusrc.js"
#define MAX_JS_FUNCS 100
#define SOURCE_EXPANSION 4
void print_source(const char *file, size_t line)
{
FILE *src = fopen(file, "r");
if (src == NULL) {
LOG_ERR("No source file found\n");
return;
}
fseek(src, 0L, SEEK_END);
long sz = ftell(src);
rewind(src);
char *srcbuf = malloc(sz+1);
fread(srcbuf, sz, 1, src);
srcbuf[sz] = '\0';
const int expand = SOURCE_EXPANSION;
char *p = srcbuf;
size_t i = 0;
while (p) {
char *next = strchr(p, '\n');
if (next) {
*next = '\0';
}
int lo = line - expand;
int hi = line + expand;
if (i >= (lo < 0 ? 0 : lo) && i <= hi ) {
char prefix[20];
snprintf(prefix, sizeof(prefix), "%2s %6zu", i == line ? "->" : "", i);
printf("%s %s\n", prefix, p);
}
if (next) {
*next = '\n';
}
p = next ? (next + 1) : NULL;
i++;
}
free(srcbuf);
fclose(src);
}
// Registers
// Taken from da goat https://source.winehq.org/source/dlls/dbghelp/cpu_x86_64.c
@@ -119,7 +188,6 @@ static RegisterDescriptor reg_descriptors[MAX_REGISTERS] = {
typedef struct {
pid_t pid;
pid_t proc_pid;
uintptr_t addr;
bool enabled;
uint8_t data;
@@ -145,14 +213,6 @@ void brk_disable(Brk *brk)
brk->enabled = false;
}
void symbols_deinit(Symbols *s)
{
for (int i = 0; i < s->count; i++) {
free((char *)s->items[i].name);
}
da_deinit(s);
}
typedef struct {
const char *file;
pid_t pid;
@@ -160,11 +220,22 @@ typedef struct {
HashTable brks;
uintptr_t program_load_offset;
HashTable js_descs;
Symbols symbols;
Funcs funcs;
FILE *binfile;
PLibelfinBinding plibelfin;
} Dbg;
void funcs_deinit(Funcs *fs)
{
for (int i = 0; i < fs->count; i++) {
free((char *)fs->items[i].name);
if (fs->items[i].ai != NULL) {
libelfin_wrap_free_info(fs->items[i].ai);
}
}
da_deinit(fs);
}
siginfo_t dbg_get_siginfo(Dbg *dbg)
{
siginfo_t i;
@@ -178,22 +249,12 @@ void dbg_handle_sigsegv(Dbg *dbg, siginfo_t info)
uint64_t dbg_get_rip(Dbg *dbg);
LOG_ERR("Caught a segfault %d. SKILL ISSUE BRO\n", info.si_code);
uintptr_t rip = (uintptr_t)dbg_get_rip(dbg);
uintptr_t nearest = rip - dbg->symbols.items[0].addr;
for (int i = 0; i < dbg->symbols.count; i++) {
if (rip - dbg->symbols.items[i].addr < nearest) {
nearest = rip - dbg->symbols.items[i].addr;
}
}
uintptr_t addr = rip - nearest;
for (int i = 0; i < dbg->symbols.count; i++) {
if (dbg->symbols.items[i].addr == addr) {
AddrInfo *ai = libelfin_wrap_info_from_rip(dbg->plibelfin, (uint64_t)(addr - dbg->program_load_offset));
if (ai != NULL) {
LOG_INF("%s:%zu in function %s()\n", ai->file, (size_t)ai->line, dbg->symbols.items[i].name);
libelfin_wrap_free_info(ai);
}
break;
}
AddrInfo *ai = libelfin_wrap_info_from_rip(dbg->plibelfin, (uint64_t)(rip - dbg->program_load_offset));
if (ai != NULL) {
print_source(ai->file, ai->line);
libelfin_wrap_free_info(ai);
}
}
@@ -211,6 +272,7 @@ void dbg_handle_sigtrap(Dbg *dbg, siginfo_t info)
ai = libelfin_wrap_info_from_rip(dbg->plibelfin, dbg_get_rip(dbg) - (uint64_t)dbg->program_load_offset);
if (ai != NULL) {
LOG_INF("Hit breakpoint at 0x%"PRIxPTR", %s:%zu\n", dbg_get_rip(dbg), ai->file, (size_t)ai->line);
print_source(ai->file, (size_t)ai->line);
libelfin_wrap_free_info(ai);
} else {
LOG_INF("Hit breakpoint at 0x%"PRIxPTR"\n", dbg_get_rip(dbg));
@@ -326,7 +388,7 @@ void dbg_js_cont(js_State *js)
js_pushundefined(js);
}
void dbg_js_baddr(js_State *js)
void dbg_js_boff(js_State *js)
{
Dbg *dbg = getdbg();
const char *addr_str = js_tostring(js, 1);
@@ -345,13 +407,13 @@ void dbg_js_bfn(js_State *js)
{
Dbg *dbg = getdbg();
const char *fn_name = js_tostring(js, 1);
for (int i = 0; i < dbg->symbols.count; i++) {
Symbol *s = &dbg->symbols.items[i];
if (strcmp(fn_name, s->name) == 0) {
uintptr_t addr = s->addr;
for (int i = 0; i < dbg->funcs.count; i++) {
Func *f = &dbg->funcs.items[i];
if (strcmp(fn_name, f->name) == 0) {
char addr_str2[20];
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR, addr);
Brk brk = { .pid = dbg->pid, .addr = addr };
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR,
libelfin_wrap_func_addr(dbg->plibelfin, f));
Brk brk = { .pid = dbg->pid, .addr = libelfin_wrap_func_addr(dbg->plibelfin, f) };
brk_enable(&brk);
hashtable_set(&dbg->brks, addr_str2, &brk, sizeof(brk));
break;
@@ -365,12 +427,12 @@ void dbg_js_rmbfn(js_State *js)
Dbg *dbg = getdbg();
const char *fn_name = js_tostring(js, 1);
for (int i = 0; i < dbg->symbols.count; i++) {
Symbol *s = &dbg->symbols.items[i];
if (strcmp(fn_name, s->name) == 0) {
uintptr_t addr = s->addr;
for (int i = 0; i < dbg->funcs.count; i++) {
Func *f = &dbg->funcs.items[i];
if (strcmp(fn_name, f->name) == 0) {
char addr_str2[20];
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR, addr);
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR,
libelfin_wrap_func_addr(dbg->plibelfin, f));
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str2);
if (brk == NULL) {
@@ -449,12 +511,12 @@ void dbg_js_lsbrk(js_State *js)
void dbg_js_lsf(js_State *js)
{
Dbg *dbg = getdbg();
for (int i = 0; i < dbg->symbols.count; i++) {
Symbol *s = &dbg->symbols.items[i];
AddrInfo *ai = libelfin_wrap_info_from_rip(dbg->plibelfin, (uint64_t)(s->addr - dbg->program_load_offset));
if (ai != NULL) {
LOG_INF("Sym %s 0x%"PRIxPTR" %s:%zu\n", s->name, s->addr, ai->file, (size_t)ai->line);
libelfin_wrap_free_info(ai);
for (int i = 0; i < dbg->funcs.count; i++) {
Func *f = &dbg->funcs.items[i];
if (f->ai != NULL) {
LOG_INF("Sym %s 0x%"PRIxPTR" %s:%zu\n", f->name,
libelfin_wrap_func_addr(dbg->plibelfin, f), f->ai->file,
(size_t)f->ai->line);
}
}
@@ -557,6 +619,122 @@ void dbg_js_help(js_State *js)
js_pushundefined(js);
}
void dbg_js_ebaddr(js_State *js)
{
Dbg *dbg = getdbg();
const char *addr_str = js_tostring(js, 1);
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str);
if (brk == NULL) {
LOG_ERR("No breakpoint at address: %s\n", addr_str);
goto done;
}
brk_enable(brk);
done:
js_pushundefined(js);
}
void dbg_js_dbaddr(js_State *js)
{
Dbg *dbg = getdbg();
const char *addr_str = js_tostring(js, 1);
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str);
if (brk == NULL) {
LOG_ERR("No breakpoint at address: %s\n", addr_str);
goto done;
}
brk_disable(brk);
done:
js_pushundefined(js);
}
void dbg_js_ebfn(js_State *js)
{
Dbg *dbg = getdbg();
const char *fn_name = js_tostring(js, 1);
for (int i = 0; i < dbg->funcs.count; i++) {
Func *f = &dbg->funcs.items[i];
if (strcmp(fn_name, f->name) == 0) {
char addr_str2[20];
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR,
libelfin_wrap_func_addr(dbg->plibelfin, f));
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str2);
if (brk == NULL) {
LOG_ERR("No breakpoint at function: %s\n", fn_name);
goto done;
}
brk_enable(brk);
break;
}
}
done:
js_pushundefined(js);
}
void dbg_js_dbfn(js_State *js)
{
Dbg *dbg = getdbg();
const char *fn_name = js_tostring(js, 1);
for (int i = 0; i < dbg->funcs.count; i++) {
Func *f = &dbg->funcs.items[i];
if (strcmp(fn_name, f->name) == 0) {
char addr_str2[20];
snprintf(addr_str2, sizeof(addr_str2), "0x%"PRIxPTR,
libelfin_wrap_func_addr(dbg->plibelfin, f));
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str2);
if (brk == NULL) {
LOG_ERR("No breakpoint at function: %s\n", fn_name);
goto done;
}
brk_disable(brk);
break;
}
}
done:
js_pushundefined(js);
}
void dbg_js_bl(js_State *js)
{
Dbg *dbg = getdbg();
const char *filepath = js_tostring(js, 1);
size_t line = (size_t)js_tonumber(js, 2);
AddrInfo *ai = libelfin_wrap_info_from_line(dbg->plibelfin, filepath, line);
if (ai != NULL) {
char addr_str[20];
snprintf(addr_str, sizeof(addr_str), "0x%"PRIxPTR, ai->addr);
Brk brk = { .pid = dbg->pid, .addr = ai->addr };
brk_enable(&brk);
hashtable_set(&dbg->brks, addr_str, &brk, sizeof(brk));
libelfin_wrap_free_info(ai);
}
js_pushundefined(js);
}
void dbg_js_rmbl(js_State *js)
{
Dbg *dbg = getdbg();
const char *filepath = js_tostring(js, 1);
size_t line = (size_t)js_tonumber(js, 2);
AddrInfo *ai = libelfin_wrap_info_from_line(dbg->plibelfin, filepath, line);
if (ai != NULL) {
char addr_str[20];
snprintf(addr_str, sizeof(addr_str), "0x%"PRIxPTR, ai->addr);
Brk *brk = (Brk *)hashtable_get(&dbg->brks, addr_str);
if (brk != NULL) {
brk_disable(brk);
hashtable_delete(&dbg->brks, addr_str);
}
libelfin_wrap_free_info(ai);
}
js_pushundefined(js);
}
void dbg_init_js(Dbg *dbg)
{
dbg->js = js_newstate(NULL, NULL, JS_STRICT);
@@ -569,7 +747,7 @@ void dbg_init_js(Dbg *dbg)
} while(0)
make_js_func(cont, 0, "Continue execution, ARGS=None");
make_js_func(baddr, 1, "Place breakpoint at address, ARGS=Address:hex string");
make_js_func(boff, 1, "Place breakpoint at offset, ARGS=Offset:hex string");
make_js_func(rmbaddr, 1, "Remove breakpoint at address, ARGS=Address:hex string");
make_js_func(lsbrk, 0, "List all breakpoints");
make_js_func(splo, 1, "Set program load offset, ARGS=Offset:hex string");
@@ -587,12 +765,22 @@ void dbg_init_js(Dbg *dbg)
make_js_func(lsf, 0, "List functions in executable, ARGS=None");
make_js_func(bfn, 1, "Set breakpoint at function, ARGS=Function name:string");
make_js_func(rmbfn, 1, "Remove breakpoint at function, ARGS=Function name:string");
make_js_func(ebaddr, 1, "Enable breakpoint at address, ARGS=Address:hex string");
make_js_func(dbaddr, 1, "Disable breakpoint at address, ARGS=Address:hex string");
make_js_func(ebfn, 1, "Enable breakpoint at function, ARGS=Function name:string");
make_js_func(dbfn, 1, "Disable breakpoint at function, ARGS=Function name:string");
make_js_func(bl, 2, "Set breakpoint at line in file, ARGS=File:string,Line:int");
make_js_func(rmbl, 2, "Remove breakpoint at line in file, ARGS=File:string,Line:int");
#undef make_js_func
}
void dbg_init_load_offset(Dbg *dbg)
{
msleep(500); // We need to sleep for a bit, because we need to ensure that our debuggee
// program has already been loaded into memory. This is technically a bad
// practice since we're sleeping for 500ms and praying that it's enough
// time to ensure that debuggee has been loaded, but if it works, it works.
procmaps_iterator maps_iter = {0};
procmaps_error_t parser_err = PROCMAPS_SUCCESS;
@@ -645,9 +833,9 @@ void dbg_libelfin_wrap_init(Dbg *dbg)
dbg->plibelfin = libelfin_wrap_get_binding(fileno(dbg->binfile), dbg->program_load_offset);
}
void dbg_load_symbols(Dbg *dbg)
void dbg_load_funcs(Dbg *dbg)
{
libelfin_wrap_get_syms(dbg->plibelfin, &dbg->symbols);
libelfin_wrap_get_funcs(dbg->plibelfin, &dbg->funcs);
}
void dbg_init(Dbg *dbg, const char *file, pid_t pid)
@@ -660,7 +848,7 @@ void dbg_init(Dbg *dbg, const char *file, pid_t pid)
dbg_init_load_offset(dbg);
dbg_init_bin(dbg);
dbg_libelfin_wrap_init(dbg);
dbg_load_symbols(dbg);
dbg_load_funcs(dbg);
hashtable_init(&dbg->brks, MAX_BRKS);
dbg_load_script(dbg, INIT_SCRIPT);
}
@@ -670,7 +858,7 @@ void dbg_deinit(Dbg *dbg)
js_freestate(dbg->js);
hashtable_deinit(&dbg->brks);
hashtable_deinit(&dbg->js_descs);
symbols_deinit(&dbg->symbols);
funcs_deinit(&dbg->funcs);
libelfin_wrap_free_binding(dbg->plibelfin);
fclose(dbg->binfile);
}

View File

@@ -19,7 +19,7 @@ class LibelfinBinding
uintptr_t loadoffset;
};
DEBUGUS_EXTERNC void libelfin_wrap_get_syms(PLibelfinBinding *pbind, Symbols *syms)
DEBUGUS_EXTERNC void libelfin_wrap_get_funcs(PLibelfinBinding pbind, Funcs *funcs)
{
LibelfinBinding *bind = (LibelfinBinding *)pbind;
for (auto &section : bind->elf.sections()) {
@@ -27,18 +27,43 @@ DEBUGUS_EXTERNC void libelfin_wrap_get_syms(PLibelfinBinding *pbind, Symbols *sy
for (auto sym : section.as_symtab()) {
auto &d = sym.get_data();
if (d.type() == elf::stt::func) {
Symbol s = {
Func f = {
.name = (const char *)malloc(strlen(sym.get_name().c_str())+1),
.addr = bind->loadoffset + (uintptr_t)d.value,
.ai = libelfin_wrap_info_from_rip(pbind, (uintptr_t)d.value),
};
strcpy((char*)s.name, sym.get_name().c_str());
da_append(syms, s);
strcpy((char*)f.name, sym.get_name().c_str());
da_append(funcs, f);
}
}
}
}
}
DEBUGUS_EXTERNC AddrInfo *libelfin_wrap_info_from_line(PLibelfinBinding pbind, const char *file, size_t line)
{
LibelfinBinding *bind = (LibelfinBinding *)pbind;
for (auto &cu : bind->dwarf.compilation_units()) {
auto &lt = cu.get_line_table();
for (auto &ent : lt) {
if (ent.is_stmt && ent.line == line) {
AddrInfo *ai = (AddrInfo *)malloc(sizeof(*ai));
ai->addr = bind->loadoffset + (uintptr_t)ent.address;
ai->line = (ssize_t)line;
ai->file = (const char*)malloc(strlen(file)+1);
strcpy((char*)ai->file, file);
return ai;
}
}
}
return NULL;
}
DEBUGUS_EXTERNC uintptr_t libelfin_wrap_func_addr(PLibelfinBinding pbind, Func *f)
{
LibelfinBinding *bind = (LibelfinBinding *)pbind;
return bind->loadoffset + f->ai->addr;
}
DEBUGUS_EXTERNC PLibelfinBinding libelfin_wrap_get_binding(int fd, uintptr_t loadoffset)
{
return (PLibelfinBinding)new LibelfinBinding(fd, loadoffset);

View File

@@ -17,18 +17,18 @@
typedef struct {
ssize_t line;
const char *file;
uint64_t addr;
uintptr_t addr;
} AddrInfo;
typedef struct {
const char *name;
uintptr_t addr;
} Symbol;
AddrInfo *ai;
} Func;
typedef struct {
Symbol *items;
Func *items;
size_t count, capacity;
} Symbols;
} Funcs;
typedef void * PLibelfinBinding;
@@ -36,6 +36,8 @@ DEBUGUS_EXTERNC PLibelfinBinding libelfin_wrap_get_binding(int fd, uintptr_t loa
DEBUGUS_EXTERNC void libelfin_wrap_free_binding(PLibelfinBinding pbind);
DEBUGUS_EXTERNC AddrInfo *libelfin_wrap_info_from_rip(PLibelfinBinding pbind, uint64_t rip);
DEBUGUS_EXTERNC void libelfin_wrap_free_info(AddrInfo *ai);
DEBUGUS_EXTERNC void libelfin_wrap_get_syms(PLibelfinBinding *pbind, Symbols *syms);
DEBUGUS_EXTERNC void libelfin_wrap_get_funcs(PLibelfinBinding pbind, Funcs *funcs);
DEBUGUS_EXTERNC uintptr_t libelfin_wrap_func_addr(PLibelfinBinding pbind, Func *f);
DEBUGUS_EXTERNC AddrInfo *libelfin_wrap_info_from_line(PLibelfinBinding pbind, const char *file, size_t line);
#endif // LIBELFIN_WRAP_H_

8
test.c
View File

@@ -2,8 +2,12 @@
#include <inttypes.h>
#include <stdlib.h>
extern void dupa2(void);
void dupa(void) {
printf("KSKSKKSKSKSK\n");
printf("a\n");
printf("b\n");
printf("c\n");
}
int main(void)
@@ -17,6 +21,8 @@ int main(void)
}
}
dupa2();
int *a = NULL;
*a = 6969;

7
test2.c Normal file
View File

@@ -0,0 +1,7 @@
#include <stdio.h>
void dupa2(void)
{
printf("function in test2.c\n");
}