【问题标题】:Understanding how rootadb finds method call in ELF binary了解 rootadb 如何在 ELF 二进制文件中找到方法调用
【发布时间】:2020-01-28 21:44:30
【问题描述】:

在 Android 设备上运行的 android 调试桥守护程序 adbd 可以在没有 root 支持的情况下编译 (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 究竟填充了什么样的数据?

【问题讨论】:

    标签: android c arm adb reverse-engineering


    【解决方案1】:

    我不是 100% 确定,但我有一个合理的想法。

    第一行代码加载一个指向setgid地址的指针,并把它当作一个32位指针。

    循环遍历二进制文件,并寻找与 setgid 函数地址相等的 8 个字节。如果找到,它会应用补丁,从该位置的第一个字节开始。

    【讨论】:

    • 为什么sgidrootadb二进制中的地址和adbd本身内部的地址一样?
    • @cweiske 可能是因为它们都不是静态链接的。
    • @cweiske 我不是 100% 确定它是如何工作的,但如果位置无关代码被关闭,它可能每次都以可预测的地址结束。它可能会放在 PLT 表中,尽管我不确定他们是如何知道它每次都在表中的同一个位置结束的。从您发布的代码中可以清楚地看出,他们就是这样做的。
    【解决方案2】:

    sgid 和 __setuid 究竟填充了什么样的数据?

    'u32 *sgid'包含函数'setgid'的地址,'u32 *cap'包含'capset'的地址。 __setuid 是函数本身,但没有括号 '()' 我们可以检索函数的地址。

    我确信 0xe3a00000 不是任何函数堆栈帧的地址。而且它不指向内存中的任何位置。

    根据给出的信息,我认为'patch'中的0xe3a00000在程序中用于恢复子程序调用后的状态并防止调用后发生的操作,

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

    下面是sn-p,在调用后搜索替换指令,

    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://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

    【讨论】:

    • E3A00000 是一个“move r0,#0”,它将寄存器 0 设置为值 0。r0 稍后用于查看指令是否成功。见armconverter.com/conversions.php?p=86
    • @cweiske 谢谢你的洞察力。我是一个没有汇编知识的 C 程序员。最近来自Calling C functions from x86 Assembly 的推论我们可以理解函数setgidsetgid(2) man page 接受一个参数,而这恰好是从索引&sgid[0] 开始的第一个push 语句,&sgid[1] 是函数调用指令,我们覆盖这并使用补丁指令撤消推送(第一条指令)。因此我们产生了无操作。
    猜你喜欢
    • 2014-05-02
    • 2013-03-20
    • 2017-04-08
    • 2015-11-07
    • 1970-01-01
    • 2016-03-21
    • 2021-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多