最近用了古河大哥的有关Inject注入的代码,发现会常出现crash的问题。
通过研究debuggerd发现,其实古河大哥写的inject对进程attach_status只是做了一些简单的判断。
其实,crash问题出现的根本原因是因为系统调用的阻塞导致的,在debuggerd源码内有相关的解决办法,就是对tid线程进行PTRACE_ATTACH,知道发生SIGSTOP后再注入。这个过程如果没做好,就执行ptrace_cont肯定会撞车的。进程没有发出SIGSTOP就表示本次ATTACH没成功。
因此,只需要模仿debuggerd的代码局部调整一下就OK了。
tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0);
int ptrace_error = errno;
if(tid_attach_status < 0) {
//这里的错误是指ATTACH失败,导致的原因可能是对方进程有反ATTACH的功能,
//或者根本没有此进程,并不是导致crash的根本原因
LOG("ptrace attach failed: %s\n", strerror(ptrace_error));
goto done;
}
//一旦ATTACH成功了,就不需要再次PTRACE_ATTACH了,一直监听status吧
//底下来自debuggerd源码,不可直接使用,还要结合古河大哥的代码才行
const int sleep_time_usec = 200000; /* 0.2 seconds */
const int max_total_sleep_usec = 3000000; /* 3 seconds */
int loop_limit = max_total_sleep_usec / sleep_time_usec;
for(;;) {
if (loop_limit-- == 0) {
LOG("timed out waiting for pid=%d tid=%d uid=%d to die\n",
cr.pid, tid, cr.uid);
goto done;
}
n = waitpid(tid, &status, __WALL | WNOHANG);
if (n == 0) {
/* not ready yet */
XLOG("not ready yet\n");
usleep(sleep_time_usec);
continue;
}
if(n < 0) {
if(errno == EAGAIN) continue;
LOG("waitpid failed: %s\n", strerror(errno));
goto done;
}
XLOG("waitpid: n=%d status=%08x\n", n, status);
if(WIFSTOPPED(status)){
n = WSTOPSIG(status);
switch(n) {
case SIGSTOP:
//关键时刻来了,既然对方已经被你HOOL住了,开始干活了。
XLOG("stopped -- continuing\n");
n = ptrace(PTRACE_CONT, tid, 0, 0);
if(n) {
LOG("ptrace failed: %s\n", strerror(errno));
goto done;
}
continue;
//买糕的,这下面要是出问题了,就表示你HOOL住的对象被人抢了。我做过一个测试,
//注入了com.android.settings。当我在干活的时候还没干完,我操作了settings界面,
//又crash了。不知道大家有没有关于crash修复的东西,或者什么别的办法来避免这类操作的异常。
case SIGILL:
case SIGABRT:
case SIGBUS:
case SIGFPE:
case SIGSEGV:
case SIGSTKFLT: {
XLOG("stopped -- fatal signal\n");
need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
kill(tid, SIGSTOP);
goto done;
}
default:
XLOG("stopped -- unexpected signal\n");
goto done;
}
} else {
XLOG("unexpected waitpid response\n");
goto done;
}
}
另外debuggerd还千叮万嘱的告诉大家,你的父进程别忘了把一些信号屏蔽,否则,对方进程一抛出如下信号,你就死定了。
signal(SIGILL, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGSTKFLT, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
以上代码必须在你的main函数内,在inject干活之前执行。
因为我主要做的是注入com.android.settings,不知道怎么的老是会自动kill掉,导致我已经注入的代码还要重新注入,这时候如果用户在操作settings界面,那就会出现crash。这类问题有什么好的解决办法呢?
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课