某航空app的协议逆向分析

两只羊 Lv2

前言

本案例分析仅用于学习交流,禁止用于非法用途

这个app是我在最近的逆向学习中,对自己来说比较困难的一个案例,其中涉及到frida检测,unidbg补环境,ollvm分析等一些之前不太熟悉的操作,在此记录一下自己的学习过程。

抓包

我们第一步对该app进行抓包分析,可以看到上传的参数全部被加密,看不出任何有效参数

image-20250516174228564

frida检测与脱壳

在查看app的信息的时候,显示是360加固

image-20250516173602123

尝试使用frida-dexdump,直接报错,应该是有frida检测

image-20250515163530965

查看是加载哪个so时退出,发现是libmsaoaidsec.so,应该有多线程检测

image-20250515164103977

hook一波clone函数后直接绕过

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
function nop_64(addr) {
Memory.protect(addr, 4 , 'rwx');
var w = new Arm64Writer(addr);
w.putRet();
w.flush();
w.dispose();
}

function hook_clone(soname)
{
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function(args) {
// args[3] 子线程的栈地址。如果这个值为 0,可能意味着没有指定栈地址
if(args[3] != 0){
var addr = args[3].add(96).readPointer()
var so_name = Process.findModuleByAddress(addr).name;
var so_base = Module.getBaseAddress(so_name);
var offset = (addr - so_base);
//console.log("===============>", so_name, addr,offset, offset.toString(16));
if(so_name.indexOf(soname) >= 0) {
console.log("===============>", so_name, addr,offset, offset.toString(16));
nop_64(addr)
}
}
},
onLeave: function(retval) {

}
});

}
setImmediate(hook_clone, "libmsaoaidsec.so")

image-20250515164337223

将上述代码添加到frida-dexdump的源码中即可成功脱壳

image-20250515165311357

算法分析

算法定位

对java层的代码分析,不难找到参数的加密方法

image-20250511133935841

hook该函数,可以看到加密前后的代码已经实现了加密

image-20250511134729104

接下来定位该方法在哪个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
function find_function() {
console.log("==== 0")

Java.perform(function () {

var process_Obj_Module_Arr = Process.enumerateModules();
for(var i = 0; i < process_Obj_Module_Arr.length; i++) {
//包含"lib"字符串的
if(process_Obj_Module_Arr[i].path.indexOf("lib")!=-1)
{
console.log("模块名称:",process_Obj_Module_Arr[i].name);
// console.log("模块地址:",process_Obj_Module_Arr[i].base);
// console.log("大小:",process_Obj_Module_Arr[i].size);
// console.log("文件系统路径",process_Obj_Module_Arr[i].path);

var libname = process_Obj_Module_Arr[i].name
frida_Module_import(libname)
}
}
})
}

function frida_Module_import(libname) {
Java.perform(function () {
const hooks = Module.load(libname);
var Imports = hooks.enumerateExports();
for(var i = 0; i < Imports.length; i++) {
if (Imports[i].name.indexOf('sub_0515') != -1) {
//函数类型
console.log("type:",Imports[i].type);
//函数名称
console.log("name:",Imports[i].name);
//属于的模块
console.log("module:",Imports[i].module);
//函数地址
console.log("address:",Imports[i].address);
}
}
});
}

最后发现是在libumejni.so中

image-20250511135004990

ida打开发现是经过了ollvm混淆,非常难看

image-20250516180326432

unidbg补环境

unidbg去执行这个函数,不出意外程序进行了一些反射调用,需要进行补环境

image-20250516180543347

这里需要补的环境并不算多,一些敏感参数还要配合frida hook来查找

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
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "hanglvzongheng/MainActivity->getPackageManager()Landroid/content/pm/PackageManager;":
return vm.resolveClass("android/content/pm/PackageManager").newObject(null);
case "hanglvzongheng/MainActivity->getPackageName()Ljava/lang/String;":
return new StringObject(vm, "com.umetrip.android.msky.app");
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}

@Override
public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
switch (signature){
case "com/umetrip/android/umehttp/security/SignUtil->getFingerprint(Landroid/content/Context;)Ljava/lang/String;":
{
return new StringObject(vm, "---------------------------------------");
}
}
return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
}



@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
switch (signature) {
case "com/umetrip/android/msky/app/BuildConfig->umSElga4:Ljava/lang/String;":
return new StringObject(vm, "------");
}
return super.getStaticObjectField(vm, dvmClass, signature);
}

最终成功执行,并与抓包和hook出来的结果一致

image-20250516180911772

算法分析

先随便构造一个简单的输入,然后开始trace,最终是60w行的trace代码

image-20250516181116191

接下来就是慢慢调试分析了,最终分析得到的是算法是一个黑盒aes ecb加密与rc4加密,这里就不详细展开了

最终效果

image-20250516181640737

  • 标题: 某航空app的协议逆向分析
  • 作者: 两只羊
  • 创建于 : 2025-05-16 17:34:05
  • 更新于 : 2025-05-16 19:43:05
  • 链接: https://twogoat.github.io/2025/05/16/某航空app的协议逆向分析/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论