Skip to content

Instantly share code, notes, and snippets.

@basinilya
Created March 17, 2018 20:14
Show Gist options
  • Save basinilya/670b1c749f88a6006a91aba5d1c37a40 to your computer and use it in GitHub Desktop.
Save basinilya/670b1c749f88a6006a91aba5d1c37a40 to your computer and use it in GitHub Desktop.
segvrecover
#define _GNU_SOURCE /* for REG_RIP */
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#ifdef _MSC_VER
/* Visual C */
#include <excpt.h>
#define METHOD "SEH"
int deref_i(int *p, int *failed)
{
int result = 0;
*failed = 1;
__try {
result = *p;
*failed = 0;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
/**/
}
return result;
}
#else
/* fallback to signals */
// TODO: check availability of gcc/clang inline assembly
#if defined(__i386__) || defined(__x86_64__)
#define METHOD "IP++"
#if defined(__x86_64__)
#define IP_REG REG_RIP
#else
#define IP_REG REG_EIP
#endif
extern const int deref_asmsz_i;
void deref_L1_i();
static void segvhan(int signo, siginfo_t *info, void *vcontext)
{
greg_t *p_ip;
ucontext_t* context = (ucontext_t*)vcontext;
p_ip = &context->uc_mcontext.gregs[IP_REG];
if (deref_L1_i == (void*)*p_ip) {
*p_ip += deref_asmsz_i;
return;
}
// TODO: restore prev handler and raise
// TODO: what if prev handler longjmps?
raise(SIGTERM);
}
__attribute__((noinline))
int deref_i(int *p, int *p_failed) {
int res, failed;
asm ( "\n\t"
".global deref_L1_i\n\t"
".type deref_L1_i, STT_FUNC\n\t"
"\n\t"
"mov $1, %1\n\t"
"deref_L1_i:\n\t"
"mov (%2), %0\n\t"
"mov $0, %1\n\t"
"deref_L2_i:\n\t"
"\n\t"
".data\n\t"
".align 2\n\t"
"\n\t"
"deref_asmsz_i:\n\t"
".int deref_L2_i - deref_L1_i\n\t"
"\n\t"
"\n\t"
".text\n\t"
""
: "=r" (res), "=&r" (failed)
: "r" (p)
);
*p_failed = failed;
return res;
}
static void insthan() {
struct sigaction acts;
sigemptyset(&acts.sa_mask);
acts.sa_flags=SA_SIGINFO;
acts.sa_sigaction = segvhan;
sigaction(SIGSEGV,&acts,NULL);
}
#else
/* fallback to long jump */
#define METHOD "Long Jump"
static sigjmp_buf env;
static void segvhan(int signo)
{
siglongjmp(env, 1);
}
int deref_i(int *p, int *failed)
{
int result = 0;
*failed = 1;
if (!sigsetjmp(env, 1)) {
result = *p;
*failed = 0;
}
return result;
}
static void insthan() {
signal(SIGSEGV, segvhan);
}
#endif /* long jump */
static void check(int *p) {
int res, failed;
res = deref_i(p, &failed);
if (failed) {
fprintf(stderr, "error reading memory at %p\n", p);
} else {
printf("int value at %p is %d\n", p, res);
fflush(stdout);
}
}
#endif /* signals */
int main(int argc, char* argv[])
{
int i = 42;
printf("recover: %s\n", METHOD);
fflush(stdout);
insthan();
check(NULL);
check((int*)0x4);
check(&i);
/* this should terminate */
*(volatile int*)0 = 0;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment