Trace大盘点

公众号: 奋飞安全
• 阅读 1313

一、目标

李老板: 奋飞呀,最近老听别人说Trace一下,啥是Trace呀?

奋飞:老板,先把上次的加班费结算一下。

Trace大盘点

Trace就是在更高抽象层次上去追踪程序的运行流程。

二、JNI_Trace

jnitrace

在Android下混饭吃,首推的就是jnitrace

https://github.com/chame1eon/jnitrace

老牌,经典,信息全,携带方便

jnitrace -l libnative-lib.so com.example.myapplication

TIP: -l 是监控的so名称, -l *就是监控所有so了。

缺点就是,hook的太全,欺负欺负小厂的还行,大厂的反调试一上,分分钟教你做人。

这里还有个优化的文章 https://blog.seeflower.dev/archives/82/

jnitrace-engine

jnitrace-engien是基于jnitrace裁剪出来的库,你可以取其所需,只hook你关心的函数。

https://github.com/chame1eon/jnitrace-engine

编译的时候需要nodejs,然后有一段时间没有更新了,导致某一个frida的库版本比较老,反正我是没有编译过去,有搞定的老铁留个言。

jtrace

方便定制的Trace才是好工具,jtrace感觉就比较帅了,信息齐全,直接在_agent.js或者_agent_stable.js 里面加自己的逻辑就行。

https://github.com/anjia0532/jtrace

比较推荐的玩法就是:不要一上来就 hook_all_jni();, 而是指定分析某段函数

Interceptor.attach(targetSo.add(0x56582+1),{
    onEnter:function(args){

        console.log(" ================ jni Onload");
        hook_all_jni();
    }
});

需要它的时候,就hook,不需要的时候注释掉。

hook_art.js

有了新朋友,也不忘老朋友,yang神 hook_art.js 依然是可以提供jni trace的。

https://github.com/lasting-yang/frida_hook_libart

你可以灵活的增加你需要hook的函数

var addrCallStaticObjectMethodV = null;
...

} else if (symbol.name.indexOf("CallStaticObjectMethodV") >= 0) {
    addrCallStaticObjectMethodV = symbol.address;
    console.log("CallStaticObjectMethodV is at ", symbol.address, symbol.name);
}

...

if (addrCallStaticObjectMethodV != null) {
    Interceptor.attach(addrCallStaticObjectMethodV, {
        onEnter: function (args) {
            if (args[3] != null) {
                    // var argsInt = parseInt(args[4]);
                    console.log("[CallStaticObjectMethodV] >>> args[3] = " + args[3]);
                            }else if (args[4] != null) {
                                    console.log("[CallStaticObjectMethodV] >>> args[4] = " + args[4]);
                            }else if (args[2] != null) {
                                    console.log("[CallStaticObjectMethodV] >>> args[2] = " + args[2]);                    
                            }else{
                        console.log("[CallStaticObjectMethodV]");
                          }


        },
        onLeave: function (retval) {
                            if(retval != null){
                            console.log("[CallStaticObjectMethodV] retval= " + JSON.stringify(retval));
                            }
                    }
    });
}

JNI-Frida-Hook

JNI-Frida-Hook 也是个不错的选择,这个哥哥把函数名都给你定义好了,哪里想看,Hook哪里。

https://github.com/Areizen/JNI-Frida-Hook

art-tracer

https://github.com/oleavr/art-tracer

art-tracer说实话,我还没怎么用过,老铁们可以试试

小小的总结一下JNI_Trace, jnitrace 能跑通的,首推。量大管饱,然后就是 jtrace ,定制方便,信息全。都崩溃的你怀疑人生的时候,老老实实用 hook_art.js ,能hook就行。

三、Native_Trace

trace_natives

做技术,要相信一见钟情,第一次Native Trace用的就是他,层次分明,信息全,结合frida-trace使用,很奇妙的想法。

你不会还在用 IDA 7.0吧?不会吧? 好巧,我也是,把这几行改改就行

so_path, so_name = getSoPathAndName()
# search_result = [f"-a '{so_name}!{offset}'" for offset in search_result]
search_result = ["-a '{}!{}'".format(so_name,offset) for offset in search_result]
search_result = " ".join(search_result)

script_name = so_name.split(".")[0] + "_" + str(int(time.time())) +".txt"

save_path = os.path.join(so_path, script_name)
# with open(save_path, "w", encoding="utf-8")as F:
with open(save_path, "w")as F:
    F.write(search_result)

print("使用方法如下:")
# print(f"frida-trace -UF -O {save_path}")
print("frida-trace -UF -O {} !".format(save_path))

你肯定要问,一见钟情有啥缺点没有?

和jnitrace一样,hook的太多,很容易崩掉。大厂 so木办法玩。

findhash

findhash也是ida插件,他本来是为了检测加解密函数用的,批量hook一大堆可疑的加解密函数,但是它提供了一个超帅的批量hook模板,我们只要按照格式生成 hook_suspected_function 函数中的 const funcs 数组,就可以把他当成一个Native Trace库来用了。

https://github.com/Pr0214/findhash

那const funcs 数组如何生成呢? 这个 改造下之前的 trace_natives.py 是可以的,不过我还没搞。

我改了下龙哥的例子,搞了个Unidbg的

public static Map<Integer, Integer> subTraceMap = new HashMap<Integer, Integer>();
public static Map<Integer, Integer> calcMap = new HashMap<Integer, Integer>();

public static void PrintHookSubInfo(){
        //*
        System.out.println("subTrace len = " + subTraceMap.size());
        String strOut = "";
        for (Map.Entry<Integer, Integer> entry : subTraceMap.entrySet()) {
            int iShow = entry.getKey();
            // 为了和unidbg显示一致这里处理下
            if(iShow % 2 != 0){
                iShow = iShow -1;
            }
            strOut = strOut + "  ,['sub_" + Integer.toHexString(iShow ) + "','0x" + Integer.toHexString((int)entry.getKey() ) + "']";
        }
        System.out.println(strOut);
        // */
}


private void traceFn(final long baseAddr, final long starAddr, final long endAddr) {
    // 这个代码是没法trace 导入函数的
    PrintStream traceStream = null;
    try {
        // 保存文件
        String traceFile = "/Users/fenfei/Desktop/money/ffFunctions.txt";
        traceStream = new PrintStream(new FileOutputStream(traceFile), true);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    final PrintStream finalTraceStream = traceStream;

    emulator.getBackend().hook_add_new(new BlockHook() {
        @Override
        public void hookBlock(Backend backend, long address, int size, Object user){
            // 块太小 影响 ida的 hook , 这里也可以改成 8  10 之类的
            if (size > 20)
            {
                Instruction[] insns = emulator.disassemble(address, 4, 0);
                // 只搞函数 就开启这个 
                // if (insns[0].getMnemonic().equals("push"))
                {
                    int level = emulator.getUnwinder().depth();
                    assert finalTraceStream != null;
                    for (int i = 0; i < level; i++) {
                        finalTraceStream.print("    |    ");
                    }

                    // 为了和 frida-trace 对应,所以加 1
                    //  Thumb 模式切换导致 hook失败
                    // Frida-trace显示函数地址的方式是“sub_Hook地址”,因为Thumb模式下要+1的缘故,所以Frida trace中“sub_123C”在IDA中显示是“sub_123B”,对照ida分析时要注意一下。
                    // finalTraceStream.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr) +1 ) + " ");

                    // 给 traceNative 函数用的, 显示的时候就不加 1
                    // finalTraceStream.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr)  ) + " ");

                    int iSize = insns[0].getSize();
                    int iUseAddr = 0;
                    if( iSize == 4){
                        // ARM模式 4字节
                        iUseAddr = (int) (address - baseAddr);
                    }else {
                        // THUMB模式 2 字节 ,hook的时候 + 1 ,
                        iUseAddr = (int) (address - baseAddr) + 1;

                    }


                    if(calcMap.containsKey(iUseAddr)){
                        int iValue = calcMap.get(iUseAddr);
                        calcMap.put(iUseAddr,iValue + 1);

                        // 4次以上的调用就不显示了, 也不用Native Trace了
                        if(iValue > 3){
                            subTraceMap.remove(iUseAddr);
                        }else{
                            System.out.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr)  ) + " ");
                            finalTraceStream.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr)  ) + " ");
                        }

                    }else{
                        calcMap.put(iUseAddr,1);
                        subTraceMap.put(iUseAddr,1);

                        System.out.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr)  ) + " ");
                        finalTraceStream.println("  " + "sub_" + Integer.toHexString((int) (address - baseAddr)  ) + " ");
                    }

                }
            }
        }

        @Override
        public void onAttach(UnHook unHook){

        }

        @Override
        public void detach(){

        }

    }, starAddr, endAddr, 0);
}

// unidbg/unidbg-api/src/main/java/com/github/unidbg/unwind/Unwinder.java
public final int depth(){
    int count = 0;
    Frame frame = null;
    while((frame = unw_step(emulator, frame)) != null) {
        if(frame.isFinish()){
            return count;
        }
        count++;
    }
    return count;
}

Native Trace有这两个就差不多了,崩溃的话就少hook点,把引发崩溃的hook点删除。

Stalker

大胡子还搞了个Stalker,也是很有野心的,但是对于国内App的内卷现状,效果只能说差强人意吧。

https://frida.re/docs/stalker/

fridaMemoryAccessTrace

没有内存断点的ida没有灵魂。一个替代方案是从 findhash 里面扣出来的。

MemoryAccessMonitor.enable({
    base: this.addr,
    size: 0x8 // qword
}, {
    onAccess: function (details) {
        console.log("\n");
        console.log("B >>>监控到内存访问\n");
        console.log(details.from);
        console.log("B >>> 访问来自:"+details.from.sub(targetSo)+"(可能有误差)");
    }
});

缺点是 只会触发一次

plus版本就是fridaMemoryAccessTrace了,你值得拥有。 https://github.com/asmjmp0/fridaMemoryAccessTrace

四、总结

不要去期望完美的Trace工具,适合你的,你自己能动手调校的才是好工具。

决定战争胜负的是人,而不是一两件新式武器。这一论断永远都不过时。

TIP: 本文的目的只有一个就是学习更多的逆向技巧和思路,如果有人利用本文技术去进行非法商业获取利益带来的法律责任都是操作者自己承担,和本文以及作者没关系,本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。

关注微信公众号: 奋飞安全,最新技术干货实时推送

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
小程序逆向分析 (一)
一、目标李老板:奋飞呀,最近耍小程序的比较多,而且貌似js好耍一点?要不咱们也试试?奋飞:你是老板,你说了算喽。第一次搞小程序,得找个软柿子捏,就找个以前分析过的某段子App的小程序吧。反编译静态分析动态调试二、步骤春天在哪里?app下载回来就是apk包,那么小程序在哪里?小程序是一个以wxapkg为后缀的文件,在android手机的/da
不能Hook的人生不值得 jsHook和模拟执行
一、目标李老板:奋飞呀,上次分析的那个App光能Debug还不够呀,网页中的js也用不了Frida,我还想Hook它的函数,咋搞呀?再有App可以RPC去执行签名,这个js我如何去利用呀?总不能代码都改成js去做请求吧?奋飞:老板呀,你一下提这么多要求,不是明摆着要我们加班吗?这次加班费可得加倍。二、步骤最简单易行的jsHookcon
手把手教你在夜神、雷电上搭frida+Xposed
一、目标李老板:奋飞呀,你天天手把手教这样不好吧?奋飞:老板你想多了,就咱们行业这种男女比例,有啥不好的?二、步骤夜神Frida先adb连上夜神,看看它是嘛系统fenfeiMac:Desktopff$adbconnect127.0.0.1:62001alreadyconnectedto127.0.0.1:62001fenfeiMac:D
小程序逆向分析 (二) 跑起来
一、目标李老板:奋飞呀,光Debug一下没啥意思,有没有可能把这个反编译出来的小程序跑起来?还是说反编译的代码有缺漏,不好使?奋飞:一城一策,具体App具体分析,具体到这个App还是可以玩的。二、步骤先跑一下我们先跑一下上篇教程反编译出来的代码。很可惜,白屏,木有我们期待的内容。不过还好有提示:登录失败搜索一下:/Users/fenfei/Downl
某音乐App 抓包和signature签名分析
一、目标李老板:奋飞呀,最近我想下个歌,现在听歌软件都这么顽固了,包都抓不到?奋飞:抓不到包的原因太多了,咱们得用排除法分析下。某音乐App10.8.4二、步骤排查协议李老板也跟我们混了这么多期,所以基本排除抓包环境的问题。那么另一个可能就是像某手使用的quic协议或者某鱼使用的spdy协议了。上jadx搜一下"quic",如果搜不到还可
某NFT交易平台App wtoken分析
一、目标李老板:奋飞呀,啥叫NFT?奋飞:老板,你已经老了,子曾经曰过,我出生时已经有的科技都是陈旧老土不值一提的;在我1030岁之间诞生的科技都是无法复制的经典;在我30岁之后诞生的科技都是愚蠢肤浅,幼稚可笑的。所以nft不适合你。这App不愧是流行科技,升级太快了,目前的版本是1.1.5我们今天的目标是他wtoken:二、步骤FRIDADEXD
手把手教你从Apk中取出算法
一、目标李老板:奋飞呀,我最近从Apk里面跟踪到一个算法,代码清晰,但是我不会java,把他翻译成python貌似挺费劲的,有没有轻松省力的方法呀?奋飞:有的呀,给我加工资,我来翻译。某电商Appv10.4.5,升级之后老有小伙伴说他的sign算法变了,其实他就是做了点小动作。sign参数没有动,uuid是明文去做签名,但是抓包请求里面找不到明文uu
某小说App返回数据 解密分析
一、目标李老板:奋飞呀,最近被隔离在小区里,没啥可干的呀。奋飞:看小说呀,量大管饱。我们今天的目标就是某小说Appv20210953二、步骤搜索url字符串App请求小说内容的时候没有加签名,但是返回的数据是加密的。那么我们先去jadx搜索一下这个url(novelcontent),看看有没有发现。结果是没有收获。那么很有可能这个url不是在apk中写
Stella981 Stella981
2年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
公众号:  奋飞安全
公众号: 奋飞安全
Lv1
奋飞,国家高级信息系统项目管理师,独立安全研究员。 http://91fans.com.cn/
文章
59
粉丝
4
获赞
44