3

The android debug bridge daemon adbd that runs on Android devices may be compiled without root support (ALLOW_ADBD_ROOT=0). There is a tool called rootadb which is able to patch an existing adbd binary by (as I understood it) replacing the calls to setuid() and setgid() with NOP instructions, effectively preventing it from dropping its privileges.

I don't understand how the code finds the place of the syscalls inside the binary.

As far as I see, it iterates over the all the bytes and checks if the bytes match something:

u32 *sgid = (u32*)&setgid;

int fd = open( "/sbin/adbd", O_RDWR );
fstat( fd, &st );
buf = memalign( 32, st.st_size );
read( fd, buf, st.st_size );
lseek64( fd, 0, SEEK_SET );

for( start = buf, end = start + st.st_size - 0x20; start < end; start++ )
    if( !memcmp( &start[1], &sgid[1], sizeof( u32 ) * 2 ) )
        memcpy( &start[1], patch, sizeof( patch ) );

How does this work? With what kind of data are sgid and __setuid actually filled?

cweiske
  • 30,033
  • 14
  • 133
  • 194

2 Answers2

4

I'm not 100% sure, but I have a reasonable idea.

The first line of code loads a pointer to the address of setgid, and treats it as a 32 bit pointer.

The loop iterates over the binary, and looks for occurrences of 8 bytes that equal address of the setgid function. If it finds one, it applies the patch, starting at the first byte of that location.

adzy2k6
  • 105
  • 1
  • 10
  • Why would the address of `sgid` in the `rootadb` binary be the same as inside `adbd` itself? – cweiske Oct 02 '19 at 07:53
  • @cweiske Probably because neither of those are statically linked. – Kamiccolo Oct 02 '19 at 11:25
  • @cweiske I'm not 100% sure how it works, but if Position Independent Code is turned off, it may be that it ends up in a predictable address each time. It likely get's placed in the PLT table, although I'm not sure how they know it ends up in the same place in the table each time. It is clear from he code you posted that this is how they are doing it though. – adzy2k6 Oct 02 '19 at 13:09
1

With what kind of data are sgid and __setuid actually filled?

'u32 *sgid' contains the address of the function 'setgid' and 'u32 *cap' contains the address of 'capset'. __setuid is the function itself but written without the parenthesis '()' we can retrieve the function's address.

I am confident that 0xe3a00000 is not an address to any function's stack frame. And it doesn't point to any location in memory.

With the information given I think 0xe3a00000 in 'patch' is used in the program to restore the state after the sub-routine call and prevent operations that happens after the call,

 u32 patch[] =
    {
        0xe3a00000,
        0
    };

Below is the snippet that searches and replaces instructions following the call,

for( start = buf, end = start + st.st_size - 0x20; start < end; start++ )
    if( !memcmp( &start[1], &sgid[1], sizeof( u32 ) * 2 ) )
            memcpy( &start[1], patch, sizeof( patch ) );

Here the next 8 bytes from &sgid[1] should have state information along with the jump instructions to setgid function which is replaced by instruction in patch.

This effectively results in no-op. This is my understanding.

Please check how stack and frame tends to grow in android architecture also about the prologue and epilogue of the functions in this architecture. It will point you in the right direction as to why &sgid[1] (or sgid + 4 bytes) was used.

You could also refer,

https://softwareengineering.stackexchange.com/questions/195385/understanding-stack-frame-of-function-call-in-c-c

https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers

Community
  • 1
  • 1
Vignesh Vedavyas
  • 369
  • 2
  • 10
  • 1
    `E3A00000` is a "move r0,#0", which sets register 0 to value 0. r0 is later used to see if the instruction succeeded. See http://armconverter.com/conversions.php?p=86 – cweiske Oct 06 '19 at 06:48
  • @cweiske Thank you for this insight. I am a C programmer with no assembly knowledge. A recent inference from, [Calling C functions from x86 Assembly](https://stackoverflow.com/questions/16255608/calling-c-functions-from-x86-assembly-language) We can understand function `setgid`, [setgid(2) man page](http://man7.org/linux/man-pages/man2/setgid.2.html) takes one argument and that happens to be the first push statement starting at index &sgid[0], &sgid[1] is function call instruction and we overwrite this and undo push (the first instruction) with patch instruction. Thus we produce No-op. – Vignesh Vedavyas Oct 06 '19 at 15:48