首页
论坛
课程
招聘
[原创]某右app sign还原
2020-7-1 00:21 6032

[原创]某右app sign还原

2020-7-1 00:21
6032

某右APP sign-v2算法分析

目录

1、前言

闲来无事,打算巩固一下以前学的知识,偶然间发现某右sign算法变了,特开一贴用来记录。(ps:本篇分析的APP版本为5.4.8)

2、APP简单分析

使用charles对目标app进行抓包,抓包结果如下:

 

图片描述

 

可以发现有个sign的签名字段,jadx反编译app,全局搜索sign=,找到关键类如下:

 

图片描述

 

使用frida hook a方法可知sign方法为加签方法,该方法为native方法位于libnet_crypto.so中。firda 代码如下:

var NetCrypto = Java.use("com.izuiyou.network.NetCrypto");

​    var String = Java.use("java.lang.String");

​    NetCrypto.sign.implementation = function(arg1, arg2){

​      var result = this.sign(arg1, arg2);

​      console.log(printBytes(arg2));

​      console.log(arg1, result);

​      return result;

​    }

3、SO层分析

使用ida打开libnet_crypto.so, 找到Jni_OnLoad函数,经过处理发现动态注册的函数位off_17D010处。

 

图片描述

 

经确认该so对字符串进行了ollvm默认的加密。有以下3种处理方式:

3.1 hook register打印动态注册的地址。

可以通过hook art.so 的register函数,打印动态注册的地址。frida 代码如下:

///<reference path='frida-gum.d.ts' />

// hook register 打印动态注册的函数地址
function hook_register(){
    // libart.so 所有导出函数表
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addr_register = null;
    for(var i = 0; i < symbols.length; i++){
        var symbol = symbols[i];
        var method_name = symbol.name;
        if(method_name.indexOf("art") >= 0){

            if(method_name.indexOf("_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi") >= 0){
                addr_register = symbol.address;
            }
        }
    }

    // 开始hook
    if(addr_register){
        Interceptor.attach(addr_register, {
            onEnter: function(args){
                var methods = ptr(args[2]);
                var method_count = args[3];
                console.log("[RegisterNatives] method_count:", method_count);
                for(var i = 0; i < method_count; i++){
                    var fn_ptr = methods.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
                    var find_module = Process.findModuleByAddress(fn_ptr);
                    if(i == 0){
                        console.log("module name", find_module.name);
                        console.log("module base", find_module.base);
                    }
                    console.log("\t method_name:", methods.add(i * Process.pointerSize * 3).readPointer().readCString(), "method_sign:", methods.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString(), "method_fnPtr:", fn_ptr, "method offset:", fn_ptr.sub(find_module.base));
                }
            }, onLeave(retval){

            }
        })
    }

}

function main(){
    hook_register();
}

setImmediate(main);

使用方式

frida -U --no-pause -f package_name -l hook.js

3.2 使用frida打印程序运行时已解密的字符串

APP 在运行时,会自动解密字符串。可以使用frida 对解密后的字符串进行打印,frida代码如下:

function print_string(addr){
    var base_addr = Module.findBaseAddress("libnet_crypto.so");
    if(base_addr){
        console.log(base_addr.add(addr).readCString());
    }
}

使用frida 附加到目标APP,调用 print_string(0x17D0C1) 可得到如下结果:

 

图片描述

 

那么与其对应的sub_49864+1 就是sign方法的地址。

3.3 使用unicorn还原字符串

被“孤挺花”混淆的字符串可以用unicorn仿真器进行修复,具体原理可参考大佬的博客:https://www.leadroyal.cn/?p=972

4、定位关键函数

sub_49864 f5 结果如下:

 

图片描述

 

鄙人定位关键函数的方法是使用frida hook 输入和输出对比结果来进行定位。最终定位的关键函数为:

sub_63CB8->sub_63C1C->sub_61A70->sub_61DBA

5、还原sign

函数sub_61DBA的cfg图如下:

 

图片描述

 

看到这个cfg图, 当时觉得这不白给吗,什么混淆都没有。 脑子一热一边动态调试,一遍翻译arm指令。 花了点时间,翻译了500多行arm指令瞬间觉得不对劲了,这尼玛大部分都是指令替换膨胀代码。 粗略计算了下,该函数大概有5000条指令,这尼玛什么时候是头,顶不住顶不住,为了头发着想,偷了个懒,直接f5 copy伪代码,修改了一下,发现结果对上了,果断放弃翻译arm汇编,有兴趣的可以试下翻译。

 

f5的伪代码,有大量类似于:

(~v5 & 0xF92342E3 | v5 & 0x6DCBD1C) ^ (HIDWORD(v4) & 0x6DCBD1C | ~HIDWORD(v4) & 0xF92342E3);

的运算,可以替换成

v5 ^ (HIDWORD(v4)

等形式。

(注:Z为常量)
(~X & Z | X & ~Z) ^ (Y & ~Z| ~Y & Z);  x ^ y
(~X & Z | X & ~Z) = X ^ ~Z = ~X ^ Z
推理过程:
    (~X & Z | X & ~Z) ^ (Y & ~Z| ~Y & Z)
  = X ^ Z ^ Y ^ Z
  = X ^ Y

6、结果验证

hook sign方法,获取加密参数:

 

图片描述

 

c运行结果图:

 

图片描述

7、杂谈

逆过老版本的某右APP,当时的只有个sign加签,post提交参数和返回结果还没有aes加解密(怀念以前不穿衣服的你),整体难度应该算是容易的吧。老版本的sign没记错的话就是md5(待加签的字符串进行变形),新版本的话,有点像是自己魔改的md5,不太确定,但整体感觉上是像。另外,aes加解密这块好像是比sign难度高点,还没去看。还发现个hook点可以让app使用老接口,frida代码:

    var nd3 = Java.use("nd3");
    var ia = Java.use("ia$a");
    nd3.a.overload('java.lang.String').implementation = function(arg1){
        return false;
    }
    ia.c.implementation = function(){
        var result = this.c();
        console.log(result);
        return 1;
    }

感觉该so是练习反混淆的好样本,cfg不太复杂,想练习的朋友可以试试先trace,然后根据trace手动修复,再写脚本还原。强烈建议各位胆大信息的大佬,去翻译核心算法的arm汇编,试过的都说爽(逃)。


2020 KCTF秋季赛【攻击篇】正在火热进行中!

收藏
点赞1
打赏
分享
最新回复 (11)
雪    币: 2331
活跃值: 活跃值 (7614)
能力值: (RANK:65 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-7-1 09:27
2
0
图片没传上,麻烦再重新上传一下哦
雪    币: 1317
活跃值: 活跃值 (2051)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2020-7-1 09:55
3
0
Editor 图片没传上,麻烦再重新上传一下哦
图片已修复了
雪    币: 280
活跃值: 活跃值 (268)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
darbra 活跃值 2020-7-1 12:37
4
0
大佬优秀6666
雪    币: 152
活跃值: 活跃值 (7959)
能力值: ( LV9,RANK:166 )
在线值:
发帖
回帖
粉丝
0x指纹 活跃值 3 2020-7-1 13:41
5
0
感谢分享!
雪    币: 236
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
吉他jita 活跃值 2020-7-2 11:41
6
0
雪    币: 236
活跃值: 活跃值 (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
吉他jita 活跃值 2020-7-2 15:46
7
0
期待楼主继续把aes加密那部分分析一下
雪    币: 86
活跃值: 活跃值 (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kongfubull 活跃值 2020-7-2 16:33
8
0
说个题外话,frida 这种东西涉及到的技术门槛我相信对国内很多大神来说那都不算事,为什么这个玩意儿,不是国产的呢?
雪    币: 955
活跃值: 活跃值 (258)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
hczhong 活跃值 2020-7-3 15:17
9
0
吉他jita 期待楼主继续把aes加密那部分分析一下
等有时间 等有时间
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_exesqobt 活跃值 2020-7-15 22:22
10
0

网页版的api:http://izuiyou.com/api/index/webrecommend  参数知道含义吗?

能否通过添加参数去获取某话题的最新帖子?


雪    币: 202
活跃值: 活跃值 (58)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
pkiot 活跃值 2020-8-10 10:46
11
0
kongfubull 说个题外话,frida 这种东西涉及到的技术门槛我相信对国内很多大神来说那都不算事,为什么这个玩意儿,不是国产的呢?
国内大牛都在忙着挣钱
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_vduvyota 活跃值 2020-8-13 19:42
12
0
一般看CFG 是一长串这样的流程那是变型的MD5无疑了
游客
登录 | 注册 方可回帖
返回