专栏名称: 看雪学苑
致力于移动与安全研究的开发者社区,看雪学院(kanxue.com)官方微信公众帐号。
目录
相关文章推荐
IDC圈  ·  关注|数据中心应该如何+“绿电”? ·  4 小时前  
计算机与网络安全  ·  JR∕T 0079-2025 ... ·  昨天  
计算机与网络安全  ·  2024年度全球软件漏洞与威胁情报报告 ·  昨天  
宁波日报  ·  由360集团运营!宁波新增一个产业基地 ·  2 天前  
51好读  ›  专栏  ›  看雪学苑

Frida 版 xposed

看雪学苑  · 公众号  · 互联网安全  · 2025-05-01 17:59

正文

请到「今天看啥」查看全文




5

静态注入


将我们的逻辑用c++写好之后编译成so,通过lief静态注入到zpp_process64


def inject_so_tail(src: str | Path,
                   new_so: str,
                   dst: str | Path | None = None) -> Path:
    bin_ = lief.parse(str(src))

    # 1. 记录旧依赖顺序并全部移除
    old_needed = [lib for lib in bin_.libraries if lib != new_so]
    for lib in list(bin_.libraries):
        bin_.remove_library(lib)

    # 2. 先插入新库,再按「旧依赖逆序」重建
    bin_.add_library(new_so)                              # 现在它在最前
    for lib in reversed(old_needed):                      # 逐个插到前面
        bin_.add_library(lib)

    out = Path(dst) if dst else Path(str(src) + ".patched")
    bin_.write(str(out))
    return out


修改好之后,需要借助apatch来实现替换,对于apatch,做了一些了解,它主要是通过向内核注入一段程序,来实现所有功能的,包括kpm,apm,root等。并且他会给用户态暴露系统调用,由这个系统调用统筹管理所有功能,以下这段程序即可使线程获得至高权限,并且pid也不是0,注意,这个线程去执行命令,是没有高权限的,只是他自己有。


#include 
#include 
#include 
#include 
#include 
#include     // 为 O_WRONLY、O_CREAT、O_TRUNC 添加
#include    // 为 strerror 添加
#include  // 为 stat 结构体和函数添加
#include  // 添加基本类型定义

// 大多数Android设备上SuperCall系统调用号
#define __NR_supercall 45

// 线程级提权命令
#define SUPERCALL_THREAD_SU 0x1011


static inline int ver_and_cmd(const char *key, int cmd) {
    int v = 0;
    for (int i = 0; key[i]; ++i) {
        v += key[i];
    }
    return ((v & 0xffff) <16) | (cmd & 0xffff);
}

int main(int argc, char *argv[]) {
   
    const char *key = "xxxxx";
    int tid = gettid();
   
    // 执行提权
    long result = syscall(__NR_supercall, key, ver_and_cmd(key, SUPERCALL_THREAD_SU), tid, "u:r:magisk:s0");
    printf("提权调用返回值: %ld\n", result);
   
 
   
    // 只输出一些核心信息以便快速判断
    printf("\n--- 权限验证信息 ---\n");
    system("cat /proc/self/status | grep -E 'Uid:|Gid:|CapEff:'");
   
    return 0;
}


这个系统调用日后应当有大作为,目前暂时不用,目前只是用一下apm模块,apm模块主要做的事情就是完成app_process64的替换,同时往/system/lib64里挂载上我们的主要功能so,功能so主要实现如下。


读取配置文件,hook setthreadname,判断是不是目标进程。


static void hook_thread() {
    // 1. 初始化 Gum
    LOGD"[*] hook_thread() called");
    sleep(2);
    gum_init_embedded();
    GumAddress base_adress=get_module_base("libandroid_runtime.so");

    GumAddress SpecializeCommon=base_adress+(GumAddress)0x20AB70;
    GumAddress SetThreadName=base_adress+(GumAddress)0x2118A0;
    ExtractJString=(ExtractJStringFunc)(base_adress+(GumAddress)0x2115E0);
    
    auto on_enter = [](GumInvocationContext* ic, gpointer) {
        gpointer raw = gum_invocation_context_get_nth_argument(ic, 0);
        auto str = reinterpret_cast(raw);
//        LOGD("[on_enter] SetThreadName %d %s",getpid(),str->c_str());
        entry(str->c_str());

    };

    auto on_leave = [](GumInvocationContext* ic, gpointer) {

        gpointer raw = gum_invocation_context_get_nth_argument(ic, 0);
        auto str = reinterpret_cast(raw);
        LOGD("[on_leave] SetThreadName %d %s",getpid(),str->c_str());
    };

    gum_hook((GumAddress)SetThreadName,
            on_enter,
             nullptr,
            nullptr);


    LOGD"[*] hook_thread() finished");
}


__attribute__((constructor))
void test_entry(){
//
     std::thread t(hook_thread);
     t.detach();








}


如果是目标进程,启动两个线程,其中一个线程负责接收外部指令,也就是load脚本哪些;另一个线程负责具体处理脚本相关的操作,使用frida-gumjs的c api,两个线程内部使用zmq的inproc功能通信,server线程使用zmq的socket通信,zmq是一个轻量级的强大的消息中间件,使用它可以避免原始socket,功能太低级,要处理很多繁琐的细节。







请到「今天看啥」查看全文