首页
社区
课程
招聘
[原创] Frida 自机简易持久化方案 (需要root)
2021-4-1 14:42 16771

[原创] Frida 自机简易持久化方案 (需要root)

2021-4-1 14:42
16771

app的启动在很早期的阶段会经由zygote进程fork出子进程, 该过程在android源码中搜索nativeForkAndSpecialize可以找到.

 

通过lief之类的工具感染android系统库令zygote加载我们的so, 并在so中用pthread_atfork捕获fork行为, 之后可以进行app包名的判断, 然后干点啥 (比如加载frida-gadget).

 

在这个过程中有小踩了几个坑, 分别是:

 

较高版本的android的链接器命名空间机制导致不能将so随便放(比如/data/local/tmp/),就算指定绝对路进行加载, 在开启了selinux的情况下仍然会报错找不到库从而加载失败. 解决方式可以看看 https://github.com/hack0z/byOpen 或者关闭selinux, 或者老老实实把库放在/system/lib(64)/目录下面.

 

在fork之后不能马上进行用户或app的判断(此时还是root用户)和线程的创建(加载frida-gadget会创建线程), 否则会发生selinux_context的切换失败导致app无法启动. 解决方式是通过setitimer和signal延迟一段时间等它切换完了再执行. 在自机上测的是使用ITIMER_PROF的话等待60毫秒就可以了, 可以再长些因为之后还要等它将cmdline切换至app的进程名.

 

frida-gadget配置使用v8环境时在调用dlopen进行初始化时需要一个足够大的栈空间, 否则v8会初始化失败, 错误原因是栈空间不足, 可以通过手动指定一个足够大的栈空间解决.

 

源码见 https://github.com/tacesrever/easy-frida/tree/master/gadget

 

完整食用方式

  1. 编辑源码中gadget-loader.h的LIB_FRIDA(frida库名, 可以修改, 此后以frida-gadget.so为例)和FRIDA_SCRIPT_DIR(脚本在手机上的位置), 注意如果将FRIDA_SCRIPT_DIR放在sdcard目录下需要为app分配存储卡使用权限.
  2. 编译 gadget-loader.cpp 可以使用自己常用的IDE新建一个android C++库项目导入gadget-loader.h, gadget-loader.cppnlohmann/json.hpp

  3. /proc/zygote's pid/maps中找一个看着顺眼的库, 以libpng.so为例

  4. 使用adb pull将该库的32位与64位版本都pull下来, 先做个备份

  5. 使用lief分别为它们添加一个依赖库, 库名称可以随便起, 看自己喜好

    1
    2
    3
    4
    import lief
    bin = lief.parse("libpng.so"
    bin.add_library("gadget-loader.so") # 你起的库名称 
    bin.write("libpng.so") # 之前要做好备份

    鉴于使用现在的0.11.5版本的LIEF库注入android库会出现问题(参考issue#396),提一下当时使用的环境是LIEF版本为0.10.1,系统为android 9
    如issue#396仍未解决,且加载修改后的android库出现bad relocation header问题,可以使用如下代码对inject后的so进行修补:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    import lief
    def read_sleb128(content, offset):
        value = 0
        len = 0
        shift = 0
        while 1:
            a_byte = content[offset]
            offset += 1
            value += (a_byte & 0x7f) << shift
            shift += 7
            len += 1
            if a_byte < 128:
                break
        return (value, len)
    def encode_leb128(value):
        if value == 0:
            return [0]
        ret = []
        while value != 0:
            a_byte = value & 0x7F
            value >>= 7
            if value > 0:
                a_byte |= 0x80
            ret.append(a_byte)
        return ret
    def add_library_for_androidso(target_path, libname):
        output_path = target_path + ".injected.so"
        sofile = lief.parse(target_path)
        if sofile.type == sofile.type.CLASS32:
            addr_size = 4
        else:
            addr_size = 8
        sofile.add_library(libname)
        sofile.write(output_path)
        seg_relr = sofile.get_section(".relr.dyn")
        seg_relro = sofile.get_section(".data.rel.ro")
        sofile.patch_address(seg_relr.virtual_address, seg_relro.virtual_address, addr_size)
        sofile.patch_address(seg_relro.virtual_address, seg_relro.virtual_address, addr_size)
        seg_got = sofile.get_section(".got")
        seg_rel = None
        if sofile.type == sofile.type.CLASS32:
            seg_rel = sofile.get_section(".rel.dyn")
        else:
            seg_rel = sofile.get_section(".rela.dyn")
        content = seg_rel.content
        current_pos = 4
        for i in range(4):
            tmp_value, len = read_sleb128(content, current_pos)
            current_pos += len
            # skip num_relocs r_offset group_size group_flags
        encoded_gotaddr = encode_leb128(seg_got.virtual_address)
        sofile.patch_address(seg_rel.virtual_address + current_pos, encoded_gotaddr)
        for e in sofile.dynamic_entries:
            if e.tag.value == 0x6000000f: # ANDROID_REL
                e.value = seg_rel.virtual_address
            elif e.tag.value == 0x24: # RELR
                e.value = seg_relr.virtual_address
            elif e.tag.value == 0x60000011: # ANDROID_RELA
                e.value = seg_rel.virtual_address
        sofile.write(output_path)
  6. 将编译完后的gadget-loader库名改为你起的名字, 和注入后的libpng.so, 下载下来改名后的frida-gadget.so一起用adb push放进/system/lib(64)/下面, 注意32位和64位的分别放一起,别放错了

  7. 参考gadget文档, 在/system/lib(64)/下分别新建与frida-gadget.so同名的扩展名为.config的文件, 内容为

    1
    2
    3
    4
    5
    6
    {
      "interaction": {
        "type": "script-directory",
        "path": "和FRIDA_SCRIPT_DIR相同"
      }
    }
  8. 运行adb shell pkill -f zygote, 等待手机从黑屏重新恢复, 这一步不会断开adb连接, 如果万一这一步手机黑屏醒不过来就要用到备份的libpng.so恢复, 错误原因可以使用logcat自查.

  9. 等手机重新亮起来后运行adb shell ps -ef | grep zygote得到zygote的pid, 再运行adb shell cat /proc/zygote的pid/maps | grep gadget-loader.so(你起的库名) 看到有输出就成功了.

  10. 之后的使用方法仍然参考gadget文档, 在手机上脚本目录下新建一个name.config文件, 内容为
    1
    {"filter": {"executables": ["目标app包名", "目标app其它运行时进程名"]}}
    之后将frida脚本放在脚本目录/name.js就可. name自己取, 建议为目标app包名, 便于管理.

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-4-21 10:56 被tacesrever编辑 ,原因: 更新lief相关问题
收藏
点赞4
打赏
分享
最新回复 (6)
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
York169 2021-6-24 10:00
2
2
JniInvocation: Failed to dlopen libart.so: dlopen failed: bad android relocation header.
用lief来添加依赖后系统启动报这个错误,可能是哪里有问题呢?需要自行修复relocation段么
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
柒晨 2021-6-24 10:26
3
0
强啊
雪    币: 3914
活跃值: (2075)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
anenn 2021-7-22 09:27
4
0
大佬,用你的 lief 库重新编译后,最终打出来的 libart.so 运行时还是报 bad android relocation header,求指点
雪    币: 867
活跃值: (1929)
能力值: ( LV8,RANK:123 )
在线值:
发帖
回帖
粉丝
tacesrever 2021-7-22 10:30
5
0
anenn 大佬,用你的 lief 库重新编译后,最终打出来的 libart.so 运行时还是报 bad android relocation header,求指点
用别的库试试,比如libpng.so
雪    币: 867
活跃值: (1929)
能力值: ( LV8,RANK:123 )
在线值:
发帖
回帖
粉丝
tacesrever 2021-7-22 10:33
6
0
我的libart.so在apex包里不方便测试...如果你的libart.so还在system/lib下面,那可能咱系统版本不一样
雪    币: 307
活跃值: (485)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
北冥鱼丶 2021-10-21 11:24
7
0

我直接使用的官方给的gadget的so文件,然后也是链接的libpng.so,但是发生了一些错误:

请问这是为什么?这种空指针是什么导致的?

游客
登录 | 注册 方可回帖
返回