安卓逆向小案例——阿里系某电影票务APP加密参数还原-Unidbg篇

本文通过unidbg详细解析了阿里系某电影票务APP的加密参数还原过程,涉及安卓逆向、初始化判断、unidbg流程、so文件分析和测试验证。在unidbg中模拟执行native方法,逐步揭示加密参数的生成逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

安卓逆向小案例——阿里系某电影票务APP加密参数还原-Unidbg篇

一、前期准备

使用unidbg还原参数时,首先需要找到指定的native方法和对应的so文件。而锁定生成加密参数的native方法和相应的so文件可以通过frida相关hook找到,这里不做解释。

1. app版本

dt专业版app_6.3.0.apk

2. so文件

libsgmainso-6.5.21.so

3. native方法
```
.class public Lcom/taobao/wireless/security/adapter/JNICLibrary;  
.super Ljava/lang/Object;  
.source ""  
  
  
# direct methods  
.method public static varargs native doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;  
.end method
```
4. 待生成的加密参数

x-sign、x-sgext、x-umt、x-mini-wua

二、unidbg流程
1. 第一步——判断是否存在初始化

native方法是否需要先初始化后才能获取数据,怎么判断?

a:hook产生加密参数的native和该JAVA类中所有native函数
使用spwan模式启动app,查看已经hook的native函数的执行顺序
b:编写目标native的执行代码(这里的传参就使用刚才hook到的具体数据)
在每一个hook的native方法调用前先执行这段目标native的执行代码,再次使用spwan模式启动APP,再次查看输出
这里的逻辑是:如果目标代码在最初就可以成功执行,那就说明不需要初始化,若目标代码在第n-1个native方法前都是报错的,到第n个方法前能够执行出正确结果,那说明第n个方法前的所有hook的native方法都可能是初始化。(这里用可能,是因为有少部分函数不是初始化,但是确实在目标函数前执行,这类函数明显特征是他的执行不固定,可能这次在a方法前面,下一次在a方法后面,而且方法的传参没有实际意义或者没有传参。)

当前案例:当前案例只有一个native方法,但是不代表他没有初始化方法,该是得按照上述的两步操作来观察。

  • hook代码
  var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");  
  if (android_dlopen_ext != null) {	
	  Interceptor.attach(android_dlopen_ext, {  
	 	onEnter: function (args) {  
	 		this.hook = false;   
			var soName = args[0].readCString();  
			if (soName.indexOf("libsgmainso-6.5.21.so") != -1) {  
				this.hook = true;  
			}  
 		},  
		onLeave: function (retval) {  
			if (this.hook) {  
				console.log('找到so,开始加载JNI_OnLoad');  
				var JNIOnload = Module.findExportByName("libsgmainso-6.5.21.so", "JNI_OnLoad");  
				Interceptor.attach(JNIOnload, {  
					onEnter: function (args) {  
						console.log("进到 JNI_OnLoad");  
					},  
					onLeave: function (retval){  
						console.log("JNI_Onload 执行完成");  
						// 这里开始hook  
						hookSignNative();  
					}  
				})  
			} 
		} 
	})
}  
function hookSignNative() {  
	var base_address = Module.findBaseAddress('libsgmainso-6.5.21.so')  
	if (base_address != null) {  
		Java.perform(function () {  
			var obj = Java.use("java.lang.Object");  
			Interceptor.attach(base_address.add(0xc049), {  
				onEnter: function (args) {  
					console.log("hook success");  
					console.log("arg0:" + args[0]); //JNIENV  
					console.log("arg1:" + Java.cast(args[1], obj)); //jclazz  
					console.log("arg2:" + args[2].toInt32()); // 参数1  
					console.log("arg3:" + args[3]); // 参数1  
					var arg3 = Java.cast(args[3], obj);  
					console.log("arg3:" + arg3); // 参数1  
					printArray(arg3);  
				},  
				onLeave: function (retval) {  
					console.log("result ==> :" + retval);  
				}  
			});  
		});  
	}  
}  
			
function printArray(objects) { 
	// 这里是输出Object数组
	var ArrayClz = Java.use("java.lang.reflect.Array");  
	var len = ArrayClz.getLength(objects);  
	console.log("Array ==> length = "+ len);  
	for(let i=0;i!=len;i++) {  
		var nowData = ArrayClz.get(objects, i);  
		if (nowData != null) {  
			console.log("Array ==> " + nowData.toString());  
		}  
	}  
}
  • spwan启动APP,输出结果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 这里正常输出结果,接下来执行下一步。执行目标函数(这里也是同一个native)。
function docommandnative2 () {  
    Java.perform(  
        function() {  
            var integerclass= Java.use("java.lang.Integer");  
            var stringclass= Java.use("java.lang.String");  
            var booleanclass= Java.use("java.lang.Boolean");  
            var str1 = stringclass.$new('23632979');  
            var data = stringclass.$new('Yl7Bnq3bwrgDACsiIRXKipG+&&&23632979&202309ce8a8fdfa2a155b604f7c65d9d&1650634740&mtop.alipictures.gravitywave.global.search.list&1.2&&10005894&AvH8ID-LlSsyBuvgEtWSp0OGnvcpTMm66qRu16fKJFxl&&&&27&&&&&&&');  
            var z = booleanclass.$new(false);  
            var i = integerclass.$new(0);  
            var api = stringclass.$new('mtop.alipictures.gravitywave.global.search.list');  
            var str2 = stringclass.$new('pageId=&pageName=');  
            var str3 = stringclass.$new('');  
            var str4 = stringclass.$new('');  
            var str5 = stringclass.$new('');  
            var objArr =Java.array('Ljava.lang.Object;',[str1, data, z, i, api, str2, str3, str4, str5]);  
  
            Java.enumerateClassLoaders({  
                "onMatch": function(loader) {  
                    if (loader.toString().indexOf("libsgmain.so") >= 0 ) {  
                        Java.classFactory.loader = loader; // 将当前class factory中的loader指定为我们需要的  
                        console.log("loader = ",loader.toString());  
                    }  
                },  
                "onComplete": function() {  
                    console.log("success");  
                }  
            });  
  
            var  JNICLibrary =  Java.classFactory.use('com.taobao.wireless.security.adapter.JNICLibrary');  
  
            var resultMap = JNICLibrary.doCommandNative(70102, objArr)  
            if (resultMap != null) {  
                console.log('result == ' + resultMap)  
            }  
        }  
    )  
}
  • 将执行目标函数的代码添加到hook代码之前,spwan模式启动APP,可以发现在执行1个10101和3个10102之后,目标函数可以成功执行。

在这里插入图片描述

2. 搭建unidbg

第一步初始化判断已经完成,且通过hook获取到具体的参数。如下:

  • 10101: 参数0 和 1可以不管。

参数0:0xf21a7230 :这是JNIEnv
参数1:class com.taobao.wireless.security.adapter.JNICLibrary对象
参数2:10101
参数3:[“com.alipictures.moviepro.MovieproApplication@3ca0131”, 3, “”, “/data/user/0/com.alipictures.moviepro/app_SGLib”, “”] :对象数组

  • 10102-第一次

参数2:10102
参数3:[“main”, “6.5.21”, “/data/app/com.alipictures.moviepro-lYhmlkDhtgvVNT1zH6qZIQ==/lib/arm/libsgmainso-6.5.21.so”]

  • 10102-第二次

参数2:10102
参数3:[“securitybody”, “6.5.26”, “/data/app/com.alipictures.moviepro-lYhmlkDhtgvVNT1zH6qZIQ==/lib/arm/libsgsecuritybodyso-6.5.26.so”]

  • 10102-第三次

参数2:10102
参数3:[“middletier”, “6.5.23”, “/data/app/com.alipictures.moviepro-lYhmlkDhtgvVNT1zH6qZIQ==/lib/arm/libsgmiddletierso-6.5.23.so”]

  • 接下来搭建unidbg模板
package com.qking.al.taopiaopiao;  
  
import com.github.unidbg.AndroidEmulator;  
import com.github.unidbg.Emulator;  
import com.github.unidbg.file.FileResult;  
import com.github.unidbg.file.IOResolver;  
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;  
import com.github.unidbg.linux.android.AndroidResolver;  
import com.github.unidbg.linux.android.dvm.AbstractJni;  
import com.github.unidbg.linux.android.dvm.DvmClass;  
import com.github.unidbg.linux.android.dvm.DvmObject;  
import com.github.unidbg.linux.android.dvm.VM;  
import com.github.unidbg.memory.Memory;  
import com.github.unidbg.virtualmodule.android.AndroidModule;  
  
import java.io.File;  
  
public class TppSign extends AbstractJni implements IOResolver {  
    private final AndroidEmulator emulator;  
    private final VM vm;  
  
    final String PACKAGE_NAME = "com.alipictures.moviepro";  
  
    final String APK_NAME = "unidbg-android/src/test/resources/ali/taopiaopiao/dt630.apk";  
    final String SO_SMAIN = "unidbg-android/src/test/resources/ali/taopiaopiao/libsgmainso-6.5.21.so";  
  
    public DvmClass JNICLibrary;  
  
    TppSign() {  
        // 创建模拟器实例,进程名填写实际的进程名,避免针对进程名的校验  
        emulator = AndroidEmulatorBuilder.for32Bit()  
                .setProcessName(PACKAGE_NAME)  
                .build();  
  
        // 绑定IO重定向接口,没有这句,下面的resolve方法不会被调用  
        emulator.getSyscallHandler().addIOResolver(this);  
  
        // 获取模拟器的内存操作接口  
        final Memory memory = emulator.getMemory();  
        // 设置系统类库解析  
        memory.setLibraryResolver(new AndroidResolver(23));  
        // 创建虚拟机,传入apk,让unidbg为我们做部分签名校验的工作(最好填绝对路径)  
        vm = emulator.createDalvikVM(new File(APK_NAME));  
  
        // 设置JNI  
        vm.setJni(this);  
        // 打印日志  
        vm.setVerbose(true);  
  
        new AndroidModule(emulator, vm).register(memory);  
        JNICLibrary = vm.resolveClass("com/taobao/wireless/security/adapter/JNICLibrary");  
  
    }  
  
    public static void main(String[] args) {  
        TppSign tppSign = new TppSign();  
    }  
  
    @Override  
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {  
        System.out.println("QKING ==> " + pathname);  
        return null;    
    }  
}
3. 开始第一次初始化
// 这个方法重复调用,写成成员变量方便调用
public String methodSign = "doCommandNative(I[Ljava/lang/Object;)Ljava/lang/Object;";  
  
public void initMain() {  
    // 加载so  
    DalvikModule dm = vm.loadLibrary(new File(SO_SMAIN), true);  
    dm.callJNI_OnLoad(emulator);  
  
    DvmObject<?> context = vm.resolveClass("com/alipictures/moviepro/MovieproApplication", vm.resolveClass("android/content/Context")).newObject("taobao");  
  
    DvmObject<?> ret = JNICLibrary.callStaticJniMethodObject(  
            emulator, methodSign, 10101,  
            new ArrayObject(  
                    context,  
                    DvmInteger.valueOf(vm, 3),  
                    new StringObject(vm, ""),  
                    new StringObject(vm, "/data/user/0/" + PACKAGE_NAME + "/app_SGLib"),  
                    new StringObject(vm, "")  
            ));  
    System.out.println("QKING, initMain.ret-10101: " + ret.getValue().toString());   
}
  • 接下来就是认真补环境了

在这里插入图片描述

  • 这里getPackageCodePath方法是获取/data/app/包名下的base.apk的绝对路径(不懂可以百度、谷歌)
@Override  
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {  
	switch (signature) {  
		case "com/alipictures/moviepro/MovieproApplication->getPackageCodePath()Ljava/lang/String;": {  
			// APK_INSTALL_PATH : /data/app/com.alipictures.moviepro-lYhmlkDhtgvVNT1zH6qZIQ\==/base.apk  
			return new StringObject(vm, APK_INSTALL_PATH);  
		}  
	}  
	return super.callObjectMethod(vm, dvmObject, signature, varArg);  
}
  • 继续运行

在这里插入图片描述

  • 这里返回值是一个File对象,直接new一个对象返回,在后续再做处理
case "com/alipictures/moviepro/MovieproApplication->getFilesDir()Ljava/io/File;": {  
    return vm.resolveClass("java/io/File").newObject(signature);  
}
  • 继续运行

![[Pasted image 20220918155142.png]]

  • 这里就是对刚才我们直接返回的File对象的处理
case "java/io/File->getAbsolutePath()Ljava/lang/String;": {  
    String sig = dvmObject.getValue().toString();  
    System.out.println("qking sig:" + sig);  
    if(sig.equals("com/alipictures/moviepro/MovieproApplication->getFilesDir()Ljava/io/File;")){  
        return new StringObject(vm, "/data/user/0/com.alipictures.moviepro/files");  
    }  
    break;  
}
  • 继续运行,这需要一个ApplicationInfo对象。

![[Pasted image 20220918155436.png]]

  • 直接new一个ApplicationInfo对象返回
case "com/alipictures/moviepro/MovieproApplication->getApplicationInfo()Landroid/content/pm/ApplicationInfo;": {  
    return new ApplicationInfo(vm);  
}
  • 继续向下

在这里插入图片描述

  • nativeLibraryDir变量是app的lib文件夹目录,为/data/app/包名下的lib/arm文件夹
case "android/content/pm/ApplicationInfo->nativeLibraryDir:Ljava/lang/String;": {  
    // dataAppPath:/data/app/com.alipictures.moviepro-lYhmlkDhtgvVNT1zH6qZIQ==  
    return new StringObject(vm, dataAppPath + "/lib/arm");  
}
  • 继续运行, registerAppLifeCyCleCallBack是一个没有传参没有返回值的方法,这里直接返回空

在这里插入图片描述

  • 继续运行,这里通过方法名readFromSPUnified可以看出,这里是去读一个什么东西。这里直接去看源码

在这里插入图片描述

  • jadx查看源码,发现这个类没有反编译出来,直接换jeb来看。

在这里插入图片描述

  • jeb打开,找到对应的方法。这里他去调用了SPUtility2.a(arg3, arg4, arg5)方法,之间进去看。

在这里插入图片描述

  • 发现这里他去调用SPUtility2.a(“SGMANAGER_DATA2”),再进去看。

在这里插入图片描述

  • 发现这里是去读文件:SPUtility2.a.getFilesDir().getAbsolutePath() + 刚才传进来的文件名

在这里插入图片描述

  • 直接去手机里查找:

在这里插入图片描述

  • 发现SGMANAGER_DATA2文件中就是一个key-value的对象

在这里插入图片描述

  • 然后获取数据是用 arg1 + “_” + arg2去取
case "com/taobao/wireless/security/adapter/common/SPUtility2->readFromSPUnified(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;": {
                String arg1 = varArg.getObjectArg(0).getValue().toString();
                String arg2 = varArg.getObjectArg(1).getValue().toString();
                String key = arg1 + "_" + arg2;
                System.out.println("KEY==> "+ key);
                JSONObject data = JSONObject.parseObject("{\"dynamicreid_dynamicreid\":\"8d8ae5b64573be6\",\"dynamicrsid_dynamicrs这里还有很多,直接省略了\"}");
                String result = data.getString(key);
                System.out.println("data ==> " + result);
                if (result != null) {
                    return new StringObject(vm, result);
                }
                return null;
            }
  • 下一步

在这里插入图片描述

  • 直接看源码,这里做了一个判断,如果f==0,去赋值e = 1,f = 1。如果f!= 0,直接返回e,这里就明显了,e==1,所以直接返回1即可

在这里插入图片描述

  • 继续向下

![[Pasted image 20220918173932.png]]

  • 看源码,这里返回值v9=v3=r17(第二个参数)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 直接返回第二个参数

在这里插入图片描述

  • 把int数据格式转换一下返回
case "java/lang/Integer-><init>(I)V": {  
    int input = varArg.getIntArg(0);  
    return DvmInteger.valueOf(vm, input);  
}
  • 跑出结果了

在这里插入图片描述

4. 第二个初始化函数
  • 10102第一次,这个函数运行时没有加载别的so文件,直接可以运行成功,所以可以加到刚才问方法里顺序执行。

在这里插入图片描述

5. 第三个初始化函数
  • 10102第二次,这里会去加载libsgsecuritybodyso-6.5.26.so文件
final String SO_BODY = "unidbg-android/src/test/resources/ali/taopiaopiao/libsgsecuritybodyso-6.5.26.so";
public void initSecurityBody() {  
    DalvikModule sb = vm.loadLibrary(new File(SO_BODY), true);  
    sb.callJNI_OnLoad(emulator);  
    DvmObject<?> ret = JNICLibrary.callStaticJniMethodObject(  
            emulator, methodSign, 10102,  
            new ArrayObject(  
                    new StringObject(vm, "securitybody"),  
                    new StringObject(vm, "6.5.26"),  
					new StringObject(vm, dataAppPath + "/lib/arm/libsgsecuritybodyso-6.5.26.so") 
            ));  
    System.out.println("QKING, initMain.ret-10102-2: " + ret.getValue().toString());  
}
  • 直接运行

在这里插入图片描述

  • 直接new一个ClassLoader对象并返回
case "com/alibaba/wireless/security/securitybody/SecurityGuardSecurityBodyPlugin->getPluginClassLoader()Ljava/lang/ClassLoader;":{  
    return new ClassLoader(vm, signature);  
}
  • 继续

![[Pasted image 20220918184000.png]]

  • 直接newObject
case "com/taobao/dp/util/CallbackHelper->getInstance()Lcom/taobao/dp/util/CallbackHelper;": {  
    return dvmClass.newObject(signature);  
}
  • 这里设置slot的值,这里添加一个成员变量slot

![[Pasted image 20220918184142.png]]

  • 添加代码
private long slot;  
@Override  
public void setStaticLongField(BaseVM vm, DvmClass dvmClass, String signature, long value) {  
    switch (signature) {  
        case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J": {  
            this.slot = value;  
            return;        
        }  
    }  
    super.setStaticLongField(vm, dvmClass, signature, value);  
}
  • 这里是获取刚才设置的值,直接返回

在这里插入图片描述

@Override  
public long getStaticLongField(BaseVM vm, DvmClass dvmClass, String signature) {  
    switch (signature) {  
        case "com/alibaba/wireless/security/framework/SGPluginExtras->slot:J": {  
            return this.slot;  
        }  
    }  
    return super.getStaticLongField(vm, dvmClass, signature);  
}
  • 继续运行

![[Pasted image 20220918185415.png]]

  • 直接返回类名
case "dalvik/system/PathClassLoader->findClass(Ljava/lang/String;)Ljava/lang/Class;": {  
    String clazzName = varArg.getObjectArg(0).getValue().toString();  
    System.out.println("findclass:" + clazzName);  
    return vm.resolveClass(clazzName);  
}
  • 继续, 这里去输出他的传参。传参为:accessibility,这里的意思是去获取AccessibilityManager对象,用来管理无障碍模式的。这里直接返回空,正常人谁开无障碍呀。。

![[Pasted image 20220918185816.png]]

  • 继续,这里调用callStaticVoidMethod无传参无返回值,直接返回null

![[Pasted image 20220918190448.png]]

![[Pasted image 20220918190609.png]]

  • 至此,第三次初始化完成
6. 最后一次初始化
  • 这里还是回去加载一个so文件
final String SO_MIDD = "unidbg-android/src/test/resources/ali/taopiaopiao/libsgmiddletierso-6.5.23.so";  
public void initMidd() {  
    DalvikModule midd = vm.loadLibrary(new File(SO_MIDD), true);  
    midd.callJNI_OnLoad(emulator);  
    DvmObject<?> ret = JNICLibrary.callStaticJniMethodObject(  
            emulator, methodSign, 10102,  
            new ArrayObject(  
                    new StringObject(vm, "middletier"),  
                    new StringObject(vm, "6.5.23"),  
                    new StringObject(vm, dataAppPath + "/lib/arm/libsgmiddletierso-6.5.23.so")  
            ));  
    System.out.println("QKING, initMain.ret-10102-3: " + ret.getValue().toString());  
}
  • 运行,SDK_INT直接返回23

![[Pasted image 20220918190913.png]]

  • 直接结束

![[Pasted image 20220918191027.png]]

7. 正式执行目标函数
  • 这里不需要再加载so,直接把hook到的数据传入进行调用
public void getXSignAll() {  
    DvmObject<?> ret = JNICLibrary.callStaticJniMethodObject(  
            emulator, methodSign, 70102,  
            new ArrayObject(  
                    new StringObject(vm, "23632979"),  
                    new StringObject(vm, "Yl7Bnq3bwrgDACsiIRXKipG+&&&23632979&88bb23b6258b4ec22f9a4779b2b1c83c&1661343561&mtop.common.gettimestamp&*&&10005894&AvH8ID-LlSsyBuvgEtWSp0OGnvcpTMm66qRu16fKJFxl&&&&27&&&&&&&"),  
                    DvmBoolean.valueOf(vm, false),  
                    DvmInteger.valueOf(vm, 0),  
                    new StringObject(vm, "mtop.common.gettimestamp"),  
                    new StringObject(vm, "pageId=&pageName="),  
                    null, null, null            ));  
  
    System.out.println("QKING, getXSign.ret-70102: " + ret.getValue().toString());  
}
  • 运行报错,百度了一堆,说的是环境没有补全,最后发现其实是文件加载没有补

在这里插入图片描述

  • 补一个apk

![[Pasted image 20220918191731.png]]

  • 继续运行

![[Pasted image 20220918191758.png]]

  • 这里和之前一样,先直接返回一个File对象

![[Pasted image 20220918191916.png]]

  • 继续补一个文件路径

![[Pasted image 20220918192116.png]]

  • 继续往下

![[Pasted image 20220918192140.png]]

  • 这里需要网络接口的列表,返回值是一个枚举对象,这里列出常见的名称
case "java/net/NetworkInterface->getNetworkInterfaces()Ljava/util/Enumeration;":{  
    String[] NetworkInterfaceNameList = new String[]{"dummy0","r_rmnet_data2","r_rmnet_data3","ip_vti0","wlan0","wlan1"};  
    int length = NetworkInterfaceNameList.length;  
    List<DvmObject<?>> NetworkInterfacelist = new ArrayList<>();  
    for (int i = 0; i < length; i++) {  
        NetworkInterfacelist.add(vm.resolveClass("java/net/NetworkInterface").newObject(NetworkInterfaceNameList[i]));  
    }  
    return new Enumeration(vm,  NetworkInterfacelist);  
}
  • 继续运行

![[Pasted image 20220918192540.png]]

  • 补一个Enumeration的方法调用
@Override  
public boolean callBooleanMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {  
    switch (signature) {  
        case "java/util/Enumeration->hasMoreElements()Z": {  
            System.out.println("hasMoreElements ===> " + dvmObject);  
            return ((Enumeration)dvmObject).hasMoreElements();  
        }  
    }  
    return super.callBooleanMethod(vm, dvmObject, signature, varArg);  
}	
  • 继续,这里补的方法同上

![[Pasted image 20220918192721.png]]

在这里插入图片描述

  • 直接返回false

在这里插入图片描述

  • 继续运行

![[Pasted image 20220918193002.png]]

  • 这里补base.apk路径
case "android/content/pm/ApplicationInfo->sourceDir:Ljava/lang/String;": {  
    return new StringObject(vm, APK_INSTALL_PATH);  
}
  • doAdapter, 这里最好的方式是通过hook传入具体的参数,返回真实的结果

![[Pasted image 20220918202934.png]]

  • hook代码如下:
function callDoAdapter(i) {  
    Java.perform(  
        function() {  
            Java.enumerateClassLoaders({  
                "onMatch": function (loader) {  
                    if (loader.toString().indexOf("libsgsecuritybody.so") >= 0) {  
                        // 将当前class factory中的loader指定为我们需要的  
                        Java.classFactory.loader = loader; 
                        console.log("loader = ", loader.toString());  
                        let SecurityBodyAdapter = Java.classFactory.use("com.alibaba.wireless.security.securitybody.SecurityBodyAdapter");  
                        var ret = SecurityBodyAdapter.doAdapter(i);  
                        console.log("libsgsecuritybody.so return ==> ", ret);  
                    }  
                },  
                "onComplete": function () {  
                    console.log("success");  
                }  
            });  
  
        })  
}

![[Pasted image 20220918203207.png]]

  • 补一个Thread.currentThread()

![[Pasted image 20220918203618.png]]

  • 这需要返回当前的堆栈

![[Pasted image 20220918203834.png]]

case "java/lang/Thread->getStackTrace()[Ljava/lang/StackTraceElement;": {  
    StackTraceElement[] elements = ((Thread) dvmObject.getValue()).getStackTrace();  
    DvmObject[] objs = new DvmObject[elements.length];  
    for (int i = 0; i < elements.length; i++) {  
        System.out.println(elements[i]);  
        objs[i] = vm.resolveClass("java/lang/StackTraceElement").newObject(elements[i]);  
    }  
    return new ArrayObject(objs);  
}
  • 这里直接调用StackTraceElement对象的getClassName方法

![[Pasted image 20220918203939.png]]

  • 这里明显是检测一些设备信息,直接hook查看对应的值

![[Pasted image 20220918205545.png]]

  • hook代码
function doCom(i) {  
    Java.perform(function() {  
        Java.enumerateClassLoaders({  
            "onMatch": function(loader) {  
                if (loader.toString().indexOf("libsgmain.so") >= 0 ) {  
                    Java.classFactory.loader = loader; // 将当前class factory中的loader指定为我们需要的  
                    console.log("loader = ",loader.toString());  
                }  
            },  
            "onComplete": function() {  
                console.log("success");  
            }  
        });  
        let DeviceInfoCapturer = Java.classFactory.use("com.taobao.wireless.security.adapter.datacollection.DeviceInfoCapturer");  
        var result = DeviceInfoCapturer.doCommandForString(i);  
        console.log("result ==> " + result)  
    })  
}

![[Pasted image 20220918205903.png]]

  • 设备信息补完后继续运行

![[Pasted image 20220918210039.png]]

  • 这里直接new一个hashMap即可

![[Pasted image 20220918210105.png]]

  • 这里是map的put方法,直接帮他实现一下就行

![[Pasted image 20220918210127.png]]

![[Pasted image 20220918210240.png]]

  • 终于跑出结果了。。。

![[Pasted image 20220918210311.png]]

三、 测试
1. 使用springboot外放接口,共Python调用

![[Pasted image 20220918210854.png]]

2. 使用Python运行代码:

![[Pasted image 20220918210533.png]]

3. 成功返回结果

![[Pasted image 20220918210950.png]]

某flutter-app逆向分析是指对于一个使用flutter框架开发的应用进行逆向工程分析。逆向工程是通过分析应用的代码、二进制文件等来了解其内部实现细节。 首先,我们需要获取该应用的安装包文件(APK或IPA文件),然后进行解包操作,将其转换为可读取的文件目录结构。 接下来,我们可以使用一些工具来提取应用的资源文件、代码文件等。对于flutter-app来说,可以提取出dart文件,这是flutter的主要代码文件,其中包含了应用的逻辑实现。 通过阅读dart文件,我们可以了解应用的代码结构、数据模型、界面设计等。可以分析应用的逻辑实现方法,包括各种函数、类、方法的调用关系。 同时,还可以通过分析相关配置文件、资源文件等来了解应用的各种设置、资源加载方式等。 在逆向过程中,还可以使用一些调试工具来进一步了解应用的运行机制。例如,hook工具可以拦截应用的函数调用,并捕获输入输出数据,用于进一步分析。 逆向分析的目的可以有很多,比如了解应用的工作原理、发现潜在的漏洞或安全问题、提供参考用于自己的开发等。 需要注意的是,逆向分析需要遵守法律规定。未经授权的逆向分析可能侵犯他人的知识产权,涉及到隐私等方面的问题。因此,在进行逆向分析之前,应该了解并遵守当地相关法律法规,避免产生法律纠纷。
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

意识存在感

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值