App逆向百例|06|某App mfsig分析

简介: App逆向百例|06|某App mfsig分析

观前提示:

本文章仅供学习交流,切勿用于非法通途,如有侵犯贵司请及时联系删除

样本:aHR0cHM6Ly9wYW4uYmFpZHUuY29tL3MvMUFrMms4M1BjTlRjLVRSbWJDQWlRRkE/cHdkPTgyd3Y=

0x1 准备工作

  • 下载并安装样本App
  • 脱壳
  • 跳转至加密位置

0x2 Unidbg实现

  • 初始化

通过使用Frida hooknativeSignnativeInit发现在加载完so后会调用一次nativeInit 接着才是正常调用nativeSign方法 所以在unidbg中也需要主动初始化

  • 补环境

复制粘贴好基础框架后运行

报了下面的错误

INFO [com.github.unidbg.linux.AndroidElfLoader] (AndroidElfLoader:464) - libsign.so load dependency libandroid.so failed
exit status=0

按照报错给出的信息就是缺少libandroid.so这个东东 那我们给上就好了

new AndroidModule(emulator,vm).register(memory);

继续执行 发现已经不提示缺少libandroid.so了 但还是退出了 也没给出具体原因

那我们改改src/test/resources/log4j.properties

将里面的INFO改成DEBUG 接着再次运行

能够看到里面读取了/proc/self/maps这个文件

虽然unidbg会自动补上一个maps 但是这个maps和真机环境上的maps差距甚远 所以我们还得从真机上copy一个正确的maps文件 并且重定向到我们自己的maps上

@Override
public FileResult resolve(Emulator emulator, String pathname, int oflags) {
    System.out.println(pathname);
    if (pathname.equals("/proc/self/maps")){
        return  FileResult.success(new SimpleFileIO(oflags,new File("unidbg-android/src/test/java/com/meiriyouxian/maps"),pathname));
    }
    return null;
}

此时此刻 这个so文件已经能正常加载了

  • 主动调用

由之前hook的结果可知需要先初始化 所以在unidbg中同样需要进行初始化

并且由java层代码可知传入了俩个参数

private static native int nativeInit(Context context, String str);

unidbg主动调用

public void CallnativeInit(){
    List<Object> args = new ArrayList<>(10);
    args.add(vm.getJNIEnv());
    args.add(0);
    DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);
    args.add(vm.addLocalObject(context));
    args.add(vm.addLocalObject(new StringObject(vm,"01000002")));
    module.callFunction(emulator,0x38bb4+1,args.toArray());
}

此时出现报错

补上即可

@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
    switch (signature){
        case "android/content/Context->getAssets()Landroid/content/res/AssetManager;":{
            return new AssetManager(vm,signature);
        }
    }
    return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

初始化完后根据java代码实现一下nativeSign

private static native String nativeSign(Context context, long j, byte[] bArr);

unidbg主动调用

public void CallnativeSign(){
    List<Object> args = new ArrayList<>(10);
    args.add(vm.getJNIEnv());
    args.add(0);
    DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);
    args.add(vm.addLocalObject(context));
    args.add(1648631301174L);
    byte[] input = "1234567890".getBytes(StandardCharsets.UTF_8);
    args.add(vm.addLocalObject(new ByteArray(vm,input)));
    Number number = module.callFunction(emulator, 0x38bf4 + 1, args.toArray());
    System.out.printf(vm.getObject(number.intValue()).getValue().toString());
}

看看效果 这个时候已经能够正常出值了 并且多次主动调用发现返回值没有发生改变

0x3 算法还原

使用IDA Pro打开so文件发现so文件被加壳 这里使用yang神的dump_so脚本

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

IDA Pro打开dump出来的so文件 进来后可以在导出表中找到nativeSignnativeInit都是静态注册的

直接打开nativeSign进行分析

从伪代码的逻辑上看 最终指向了sub_36FC0 而且返回值为v18

这里用unidbg来hook辅助分析so还原算法

emulator.attach().addBreakPoint(module.base+0x36FC0+1);

hook住后 记住mr0的地址 输入blr下断 接着跳到下一个断点

输入刚刚mr0的地址

看到00 70 2E 40 也就是存放数据的地址 通过读取验证确定就是返回的加密值了

traceWrite

emulator.traceWrite(0x402e7000L, 0x402e7000L + 32L);

通过hook可知在0x37f64存在写操作

上ida跳转过去 突然看到了熟悉的东西 这不就是base64吗

双击一下 发现 这码表好像不对 可以确定是改了码表的base64

先hook一下sub_37F3C看看输入和输出结果 输入值

输出值

上CyberChef验证 可以见到结果一模一样

接着找找是谁调用了sub_37F3C

跳转过来后看到v7a2有关

继续找是谁调用了sub_37E5C

跳过来看到a2对应v116

v116sub_2F8F6有关  所以hook一下sub_2F8F6这里注意一下方法一共被调用了3次 后2次才是我们需要的 第2次输出值和之前base64传入值对上了 但是还差了点

第3次输出值就和之前base64传入值一模一样了

看一下传入值 第2次

第3次

和输出值对比了一下可以发现 就是取我们传入的时间戳1648631301174分成了俩半1648631301174中间插入了第2次的传入值

接着找找这段值的由来

还是使用traceWrite

emulator.traceWrite(0x402a10f0L, 0x402a10f0L + 32L);

得到结果 跳转过去0x36488

先hooksub_363DC的传入值

emulator.attach().addBreakPoint(module.base+0x363DC+1);

a2

a3

知道了传入值后看看代码流程

根据代码不难看出 主要操作就是循环累加

for ( i = 0; i != v11; ++i )
      {
        sub_815D8(i, v28);
        v14 = v3 + 1;
        if ( *v3 << 31 )
          v14 = *(v3 + 2);
        v15 = v14[v13];                         
        sub_815D8(i, v27);
        v17 = v26 + 1;
        if ( *v26 << 31 )
          v17 = *(v3 + 5);
        v18 = v31;
        v19 = *(a3 + 2);
        v20 = v17[v16];                         
        if ( (v29 & 1) == 0 )
          v18 = &v29 + 1;
        if ( (*a3 & 1) == 0 )
          v19 = a3 + 1;
        v18[i] = v20 + v15 + v19[i];
      }

v19就是a3 v20是v17[v16]对应a2+12+1 v15是v14[v13]对应a2+1

而v16和v13在ida中并没有正确识别到 那就继续hook看是什么情况

先在0x3647C处下个断点看v13

r0=0xbfffee85(-1073746299) r1=0x0
ldrb.w fp, [r0, r1]" [0xbfffee85] => 0x31 //1
r0=0xbfffee85(-1073746299) r1=0x1
ldrb.w fp, [r0, r1]" [0xbfffee86] => 0x31 //1
r0=0xbfffee85(-1073746299) r1=0x2
ldrb.w fp, [r0, r1]" [0xbfffee87] => 0x37 //7
r0=0xbfffee85(-1073746299) r1=0x3
ldrb.w fp, [r0, r1]" [0xbfffee87] => 0x34 //4
r0=0xbfffee85(-1073746299) r1=0x4
ldrb.w fp, [r0, r1]" [0xbfffee85] => 0x31 //1
r0=0xbfffee85(-1073746299) r1=0x5
ldrb.w fp, [r0, r1]" [0xbfffee86] => 0x31 //1
r0=0xbfffee85(-1073746299) r1=0x6
ldrb.w fp, [r0, r1]" [0xbfffee87] => 0x37 //7
r0=0xbfffee85(-1073746299) r1=0x7
ldrb.w fp, [r0, r1]" [0xbfffee87] => 0x34 //4
.......

然后在0x364A4处下个断点看v16

r0=0xbfffee91(-1073746287) r1=0x0
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x41 //A
r0=0xbfffee91(-1073746287) r1=0x1
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x42 //B
r0=0xbfffee91(-1073746287) r1=0x2
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x43 //C
r0=0xbfffee91(-1073746287) r1=0x3
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x44 //D
r0=0xbfffee91(-1073746287) r1=0x4
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x45 //E
r0=0xbfffee91(-1073746287) r1=0x5
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x46 //F
r0=0xbfffee91(-1073746287) r1=0x6
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x47 //G
r0=0xbfffee91(-1073746287) r1=0x7
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x47 //H
r0=0xbfffee91(-1073746287) r1=0x8
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x41 //A
r0=0xbfffee91(-1073746287) r1=0x9
0x400364a4:*"ldrb r0, [r0, r1]" [0xbfffee91] => 0x42 //B
.......

能看到v13和v16其实就是i

相当于1174ABCDEFGH在循环往复取值

所以这段代码主要的作用就加加加

用python还原就是

def sub_363DC(v14,v19):
    v17="ABCDEFGH".encode()
    result=[]
    for i in range(len(v19)):
        result.append(v17[i%8]+v14[i%4]+v19[i])
    return bytes(result).hex()

查找谁调用了sub_363DC

找找v162的由来

跳转过来后看到下面的报错信息出现了protobuf

带着猜想 复制数据测试 发现能成功反序列化

1: 16777218
2: 1
3: "29FE8A6E707C1697A7DB626B9880CC8DF6BD9AFA6C72BDE90F4CA76D39EA3A77"
6: 1

其中1就是0x1000002 3为未知字符串 2和6不变都是1 验证成功就可以开始写protoc文件了

syntax = "proto3";
message meiriyouxian{
    uint64 i1=1;
    uint64 i2=2;
    string s3=3;
    uint64 i6=6;
}

写完生成python文件就可以在python里面引用生成我们需要的结果

生成结果和hook结果能够对上 现在需要找出里面s3的来源

hooksub_348B4

r0=0x402a1000 r1=0xbffff070 r2=0x1000002 r3=0x1 r4=0xbfffefc8 r5=0x1000002 r6=0x1

看看v144

所以v144也就是我们自己定义的s3

查找一下v144的引用

跳转过来 没发现什么特别的地方 先hook了再说

sub_2E5A4有2处调用 第2次调用才是我们需要的

此时能看到 值已经生成了 所以我们还得找v178的来源

此时重点来到sub_37D8C 进去观摩观摩 每个方法都看看瞧瞧

sub_37D8C->sub_2FB14->sub_367F6->sub_36558

里面发现了v28 = dword_9E030[v14++];

眼尖的朋友们应该发现这是什么东西了吧 不认识不要紧 随便复制一个去百度搜搜看

结果很明显 就是一个sha256的k表 那这就是一个sha256算法吗?

不一定 也有可能是hmac sha256

继续分析 回到sub_37D8C找v201的引用位置 (这里我把v201改成了sha)

这里可以看到上面俩处引用

先看看 sub_37D80(&sha, a3, a4)

通过对比sha256的c代码发现sub_37D80就是SHA256update

接着看sub_37D5C(&sha, v13)

进入sub_37D5C中的sub_2FA30方法发现0x360x5C

这不就是HMAC SHA256中的INNER_PAD和OUTER_PAD吗


HMAC-SHA256 C实现

https://blog.csdn.net/miniphoenix/article/details/110135164


确定完是HMAC SHA256 那就是找key以及查看是否加盐了

hooksub_37D5C

emulator.attach().addBreakPoint(module.base+0x37D5C+1);


所以key就是PwwGKgCqZAc2PPb31TLnnqPNVFAAdq/X

hooksub_37D80

emulator.attach().addBreakPoint(module.base+0x37D80+1);

结果显示没有加盐之类的操作

知道明文,加密方式和KEY后在CyberChef验证结果

结果一模一样

0x4 mfsig加密流程总结

  • HMAC SHA256
  • protobuf
  • sub_363DC
  • base64
  • 头部拼接mfsn


感谢各位大佬观看

感谢大佬们的文章分享

如有错误 还请海涵

共同进步


[完]

相关文章
|
24天前
|
数据采集 小程序 网络安全
云擎技术---分析工信部APP备案的“传闻”
APP备案并非新事物,自2005年起已有非经营性互联网信息服务备案制度。备案针对的是网站主办者,而非用户,不涉及个人用户网络访问。网络接入服务提供者包括ISP和IDC,不限于三大运营商。通知要求不为未备案网站提供接入,但不影响国外软件使用。个人开发者不能涉及经营性内容,备案审核时长1-20个工作日。境内服务器和国内应用商店需备案,境外则无需。手机厂商不会开启白名单制,仅实行黑名单制。APP备案与民营经济发展壮大意见不冲突,工信部有权颁布相关规定。该政策不存在逐步试探底线情况,所有解读均有法律依据。
25 3
云擎技术---分析工信部APP备案的“传闻”
|
2月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
32 4
|
2月前
|
算法
某圈app算法分析
某圈app算法分析
18 0
|
2月前
|
算法 安全 数据安全/隐私保护
某影视APP算法逆向分析
某影视APP算法逆向分析
19 0
|
2月前
|
算法 Java
某江app算法分析
某江app算法分析
17 0
|
2月前
|
算法 数据挖掘 数据安全/隐私保护
某合伙人app算法分析
某合伙人app算法分析
15 0
|
4月前
|
数据可视化 数据挖掘
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
46 0
|
7月前
|
算法 数据安全/隐私保护
App逆向百例|17|某音乐App分析
App逆向百例|17|某音乐App分析
159 0
|
7月前
|
存储 数据安全/隐私保护
App逆向百例|16|某交友App花指令分析
App逆向百例|16|某交友App花指令分析
192 0
|
7月前
|
算法 数据安全/隐私保护
App逆向百例|15|某特惠App参数分析
App逆向百例|15|某特惠App参数分析
91 0
App逆向百例|15|某特惠App参数分析