Created
March 17, 2018 20:14
-
-
Save basinilya/670b1c749f88a6006a91aba5d1c37a40 to your computer and use it in GitHub Desktop.
segvrecover
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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