#include <pro.h>
#include <ida.hpp>
#include <auto.hpp>
#include <idalib.hpp>
#include <kernwin.hpp>
#include <registry.hpp>
#include <csetjmp>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#if defined(_WIN32)
#include <fcntl.h>
#include <io.h>
#include <process.h>
#else
#include <fcntl.h>
#include <unistd.h>
#endif
#if defined(__linux__)
#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <sys/mman.h>
#endif
#include "idakit_facade_internal.hpp"
#undef fflush
#undef setenv
namespace idakit_facade {
thread_local jmp_buf g_exit_jmp;
thread_local bool g_exit_guarded = false;
thread_local int g_exit_code = 0;
thread_local bool g_trapped = false;
thread_local std::string g_output;
thread_local std::string g_ui_output;
namespace {
typedef void (*exit_fn)(int);
typedef void (*abort_fn)(void);
exit_fn g_real_exit = nullptr;
abort_fn g_real_abort = nullptr;
bool g_trap_installed = false;
[[maybe_unused]] void idakit_exit(int status) {
if (g_exit_guarded) {
g_exit_guarded = false;
g_exit_code = status;
longjmp(g_exit_jmp, 1);
}
if (g_real_exit != nullptr)
g_real_exit(status);
_exit(status);
}
[[maybe_unused]] [[noreturn]] void idakit_abort(void) {
if (g_exit_guarded) {
g_exit_guarded = false;
g_exit_code = 134; longjmp(g_exit_jmp, 1);
}
if (g_real_abort != nullptr)
g_real_abort();
_exit(134);
}
#if defined(__linux__)
void overwrite_slot(void **slot, void *newval) {
long pg = sysconf(_SC_PAGESIZE);
uintptr_t addr = reinterpret_cast<uintptr_t>(slot);
uintptr_t page = addr & ~(uintptr_t)(pg - 1);
size_t len = (addr + sizeof(void *)) - page;
if (mprotect(reinterpret_cast<void *>(page), len, PROT_READ | PROT_WRITE) != 0)
return;
*slot = newval;
}
void scan_rela(ElfW(Addr) base, const ElfW(Rela) * rela, size_t count, const ElfW(Sym) * symtab,
const char *strtab, const char *symname, void *newfn) {
for (size_t i = 0; i < count; i++) {
unsigned long sym = ELF64_R_SYM(rela[i].r_info);
const char *name = strtab + symtab[sym].st_name;
if (strcmp(name, symname) == 0)
overwrite_slot(reinterpret_cast<void **>(base + rela[i].r_offset), newfn);
}
}
int redirect_cb(struct dl_phdr_info *info, size_t, void *) {
const char *objname = info->dlpi_name != nullptr ? info->dlpi_name : "";
if (strstr(objname, "libida") == nullptr)
return 0;
const ElfW(Dyn) *dyn = nullptr;
for (int i = 0; i < info->dlpi_phnum; i++) {
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
dyn = reinterpret_cast<const ElfW(Dyn) *>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
break;
}
}
if (dyn == nullptr)
return 0;
auto fixptr = [&](ElfW(Addr) p) -> ElfW(Addr) {
return p < info->dlpi_addr ? info->dlpi_addr + p : p;
};
const ElfW(Sym) *symtab = nullptr;
const char *strtab = nullptr;
const ElfW(Rela) *jmprel = nullptr, *rela = nullptr;
size_t pltrelsz = 0, relasz = 0, relaent = sizeof(ElfW(Rela));
for (const ElfW(Dyn) *d = dyn; d->d_tag != DT_NULL; d++) {
switch (d->d_tag) {
case DT_SYMTAB:
symtab = reinterpret_cast<const ElfW(Sym) *>(fixptr(d->d_un.d_ptr));
break;
case DT_STRTAB:
strtab = reinterpret_cast<const char *>(fixptr(d->d_un.d_ptr));
break;
case DT_JMPREL:
jmprel = reinterpret_cast<const ElfW(Rela) *>(fixptr(d->d_un.d_ptr));
break;
case DT_PLTRELSZ:
pltrelsz = d->d_un.d_val;
break;
case DT_RELA:
rela = reinterpret_cast<const ElfW(Rela) *>(fixptr(d->d_un.d_ptr));
break;
case DT_RELASZ:
relasz = d->d_un.d_val;
break;
case DT_RELAENT:
relaent = d->d_un.d_val;
break;
default:
break;
}
}
if (symtab == nullptr || strtab == nullptr)
return 0;
void *exit_trap = reinterpret_cast<void *>(&idakit_exit);
void *abort_trap = reinterpret_cast<void *>(&idakit_abort);
if (jmprel != nullptr && pltrelsz > 0) {
size_t n = pltrelsz / sizeof(ElfW(Rela));
scan_rela(info->dlpi_addr, jmprel, n, symtab, strtab, "exit", exit_trap);
scan_rela(info->dlpi_addr, jmprel, n, symtab, strtab, "abort", abort_trap);
}
if (rela != nullptr && relasz > 0 && relaent > 0) {
size_t n = relasz / relaent;
scan_rela(info->dlpi_addr, rela, n, symtab, strtab, "exit", exit_trap);
scan_rela(info->dlpi_addr, rela, n, symtab, strtab, "abort", abort_trap);
}
return 0;
}
#endif }
void install_fatal_traps() {
if (g_trap_installed)
return;
g_trap_installed = true;
#if defined(__linux__)
g_real_exit = reinterpret_cast<exit_fn>(dlsym(RTLD_DEFAULT, "exit"));
g_real_abort = reinterpret_cast<abort_fn>(dlsym(RTLD_DEFAULT, "abort"));
dl_iterate_phdr(redirect_cb, nullptr);
#endif
}
#if !defined(_WIN32)
capture_t begin_capture() {
capture_t cap;
(void)fflush(nullptr);
int fds[2];
if (pipe(fds) != 0)
return cap;
int fl = fcntl(fds[1], F_GETFL, 0);
if (fl != -1)
(void)fcntl(fds[1], F_SETFL, fl | O_NONBLOCK);
cap.rd = fds[0];
cap.wr = fds[1];
cap.saved_out = dup(1);
cap.saved_err = dup(2);
dup2(fds[1], 1);
dup2(fds[1], 2);
return cap;
}
void end_capture(capture_t &cap) {
if (cap.rd < 0)
return;
(void)fflush(nullptr);
if (cap.saved_out >= 0) {
dup2(cap.saved_out, 1);
close(cap.saved_out);
}
if (cap.saved_err >= 0) {
dup2(cap.saved_err, 2);
close(cap.saved_err);
}
close(cap.wr); g_output.clear();
char buf[4096];
for (;;) {
ssize_t n = read(cap.rd, buf, sizeof(buf));
if (n <= 0)
break;
g_output.append(buf, static_cast<size_t>(n));
}
close(cap.rd);
cap.rd = -1;
}
#else
capture_t begin_capture() { return capture_t{}; }
void end_capture(capture_t &cap) { (void)cap; }
#endif
namespace {
constexpr size_t UI_CAP = 8192;
ssize_t idaapi ui_msg_capture_cb(void *, int code, va_list va) {
if (code != ui_msg || g_ui_output.size() >= UI_CAP)
return 0;
const char *format = va_arg(va, const char *);
if (format == nullptr)
return 0;
char tmp[1024];
#if defined(_WIN32) || (defined(__APPLE__) && defined(__aarch64__))
qvsnprintf(tmp, sizeof(tmp), format, va_arg(va, va_list));
#else
va_list args;
va_copy(args, *reinterpret_cast<va_list *>(va_arg(va, void *)));
qvsnprintf(tmp, sizeof(tmp), format, args);
va_end(args);
#endif
g_ui_output += tmp;
return 0; }
}
void begin_ui_capture() {
g_ui_output.clear();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
hook_to_notification_point(HT_UI, ui_msg_capture_cb, nullptr);
#pragma GCC diagnostic pop
}
void end_ui_capture() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
unhook_from_notification_point(HT_UI, ui_msg_capture_cb, nullptr);
#pragma GCC diagnostic pop
if (!g_ui_output.empty() && g_output.find_first_not_of(" \t\r\n") == std::string::npos)
g_output = g_ui_output;
g_ui_output.clear();
}
}
using namespace idakit_facade;
namespace {
void swallow_exit_banner() {
(void)fflush(nullptr);
#if defined(_WIN32)
int devnull = _open("NUL", _O_WRONLY);
if (devnull >= 0) {
_dup2(devnull, 1);
_close(devnull);
}
#else
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0) {
dup2(devnull, 1);
close(devnull);
}
#endif
}
struct BannerFilter {
BannerFilter() { (void)atexit(swallow_exit_banner); }
};
[[maybe_unused]] BannerFilter g_banner_filter;
}
extern "C" int idakit_init_library(void) {
#if defined(_WIN32)
_putenv_s("TVHEADLESS", "1");
#else
setenv("TVHEADLESS", "1", 1);
#endif
return init_library(0, nullptr);
}
extern "C" void idakit_set_batch(int on) { batch = on != 0; }
extern "C" int idakit_guarded_open(const char *file_path, int run_auto) {
return guarded<int>(IDAKIT_EXIT_TRAPPED, true,
[&] { return open_database(file_path, run_auto != 0, nullptr); });
}
extern "C" int idakit_guarded_auto_wait(void) {
return guarded<int>(IDAKIT_EXIT_TRAPPED, false, [] { return auto_wait() ? 1 : 0; });
}
extern "C" int idakit_guarded_close(int save) {
return guarded<int>(IDAKIT_EXIT_TRAPPED, false, [&] {
close_database(save != 0);
return 0;
});
}
extern "C" int idakit_last_exit_code(void) { return g_exit_code; }
extern "C" int idakit_was_trapped(void) { return g_trapped ? 1 : 0; }
extern "C" int idakit_reg_read_int(const char *name, int defval) {
return reg_read_int(name, defval, nullptr);
}
extern "C" int idakit_accept_eula(void) {
reg_write_bool("EULA 90", 1, nullptr);
return reg_read_int("EULA 90", 0, nullptr);
}
extern "C" size_t idakit_last_output(char *buf, size_t cap) {
size_t n = g_output.size();
if (buf != nullptr && cap > 0) {
size_t copy = n < cap - 1 ? n : cap - 1;
memcpy(buf, g_output.data(), copy);
buf[copy] = 0;
}
return n;
}
#ifdef IDAKIT_TEST_SHIMS
extern "C" int idakit_test_fatal(int kind) {
return guarded<int>(IDAKIT_EXIT_TRAPPED, false, [kind]() -> int {
if (kind == IDAKIT_FATAL_EXIT)
idakit_exit(42);
else if (kind == IDAKIT_FATAL_ABORT)
idakit_abort();
else if (kind == IDAKIT_FATAL_INTERR)
interr(1);
return 0;
});
}
extern "C" int idakit_get_batch(void) { return batch ? 1 : 0; }
extern "C" void idakit_trigger_fatal(int kind) {
if (kind == IDAKIT_FATAL_EXIT)
idakit_exit(42);
else if (kind == IDAKIT_FATAL_ABORT)
idakit_abort();
else if (kind == IDAKIT_FATAL_INTERR)
interr(1);
}
#endif