了解 rootadb 如何在 ELF 二进制文件中查找方法调用
Understanding how rootadb finds method call in ELF binary
在 Android 设备上运行的 android 调试桥守护程序 adbd
可以在没有根支持的情况下编译 (ALLOW_ADBD_ROOT=0
)。
有一个名为 rootadb
的工具,它能够通过(据我了解)用 NOP 指令替换对 setuid()
和 setgid()
的调用来有效地修补现有的 adbd
二进制文件防止它放弃它的特权。
我不明白 the code 如何在二进制文件中找到系统调用的位置。
据我所知,它遍历所有字节并检查字节是否匹配:
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 ) );
这是如何工作的?
sgid
和__setuid
实际填充了什么样的数据?
我不是 100% 确定,但我有一个合理的想法。
第一行代码加载了一个指向setgid地址的指针,并将其视为一个32位指针。
循环遍历二进制文件,并查找等于 setgid 函数地址的 8 个字节的出现。如果找到一个,它会应用补丁,从该位置的第一个字节开始。
With what kind of data are sgid and __setuid actually filled?
'u32 *sgid'包含函数'setgid'的地址,'u32 *cap'包含'capset'的地址。 __setuid 是函数本身,但没有括号 '()' 我们可以检索函数的地址。
我确信 0xe3a00000 不是任何函数堆栈帧的地址。而且它不指向内存中的任何位置。
根据给出的信息我认为'patch'中的0xe3a00000在程序中用于恢复子程序调用后的状态,防止调用后发生的操作,
u32 patch[] =
{
0xe3a00000,
0
};
下面是调用后搜索和替换说明的代码段,
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 ) );
这里来自 &sgid[1] 的下 8 个字节应该包含状态信息以及到 setgid 函数的跳转指令,该函数由补丁中的指令替换。
这实际上导致了空操作。这是我的理解。
请检查堆栈和框架在android架构中如何增长,以及该架构中函数的序言和结尾。它将为您指出正确的方向,说明为什么使用 &sgid[1](或 sgid + 4 字节)。
你也可以参考,
https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers
在 Android 设备上运行的 android 调试桥守护程序 adbd
可以在没有根支持的情况下编译 (ALLOW_ADBD_ROOT=0
)。
有一个名为 rootadb
的工具,它能够通过(据我了解)用 NOP 指令替换对 setuid()
和 setgid()
的调用来有效地修补现有的 adbd
二进制文件防止它放弃它的特权。
我不明白 the code 如何在二进制文件中找到系统调用的位置。
据我所知,它遍历所有字节并检查字节是否匹配:
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 ) );
这是如何工作的?
sgid
和__setuid
实际填充了什么样的数据?
我不是 100% 确定,但我有一个合理的想法。
第一行代码加载了一个指向setgid地址的指针,并将其视为一个32位指针。
循环遍历二进制文件,并查找等于 setgid 函数地址的 8 个字节的出现。如果找到一个,它会应用补丁,从该位置的第一个字节开始。
With what kind of data are sgid and __setuid actually filled?
'u32 *sgid'包含函数'setgid'的地址,'u32 *cap'包含'capset'的地址。 __setuid 是函数本身,但没有括号 '()' 我们可以检索函数的地址。
我确信 0xe3a00000 不是任何函数堆栈帧的地址。而且它不指向内存中的任何位置。
根据给出的信息我认为'patch'中的0xe3a00000在程序中用于恢复子程序调用后的状态,防止调用后发生的操作,
u32 patch[] =
{
0xe3a00000,
0
};
下面是调用后搜索和替换说明的代码段,
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 ) );
这里来自 &sgid[1] 的下 8 个字节应该包含状态信息以及到 setgid 函数的跳转指令,该函数由补丁中的指令替换。
这实际上导致了空操作。这是我的理解。
请检查堆栈和框架在android架构中如何增长,以及该架构中函数的序言和结尾。它将为您指出正确的方向,说明为什么使用 &sgid[1](或 sgid + 4 字节)。
你也可以参考,
https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers