首页
社区
课程
招聘
[原创]某手新版本sig3参数算法还原
2023-9-19 17:14 7910

[原创]某手新版本sig3参数算法还原

2023-9-19 17:14
7910

某手新版本sig3参数算法还原

免责声明:本文章仅供纯技术讨论和研究,本文提供的可操作性不得用于任何商业用途和违法违规场景。本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。

从星球学到很多,分享给更多人

图片描述

Frida Native层主动调用

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
export function callDoCommandNative(){ // jni方法复习
    Java.perform(function() {
        var base_addr = Module.findBaseAddress("libkwsgmain.so") || ptr(0x0);
        var real_addr = base_addr.add(0x41680)
        var docommand = new NativeFunction(real_addr, "pointer", ["pointer", "pointer", "int", "pointer"]);
 
        var JNIEnv = Java.vm.getEnv();
        var Intger = Java.use("java.lang.Integer");
        var jstring = Java.use("java.lang.String");
        var Boolean = Java.use("java.lang.Boolean");
        var cla = JNIEnv.findClass("java/lang/Object");
 
        // args1:,d7b7d042-d4f2-4012-be60-d97ff2429c17,,,com.yxcorp.gifshow.App@27101bb,,'} data: None
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        var context = currentApplication.getApplicationContext();
        log("context"+context)
 
        var input_1 = JNIEnv.newStringUtf('d7b7d042-d4f2-4012-be60-d97ff2429c17');
        var argList_0 = JNIEnv.newObjectArray(7, cla, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList_0, 0, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList_0, 1, input_1);
        JNIEnv.setObjectArrayElement(argList_0, 2, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList_0, 3, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList_0, 4, context.$h);
        JNIEnv.setObjectArrayElement(argList_0, 5, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList_0, 6, ptr(0x0));
 
        var point_0 = docommand(JNIEnv, ptr(0x0), 10412, argList_0); // 返回的是指针,通过cast转成java对象,从而读出来
        console.log("point_0: " + point_0);
        var s_0 = Java.cast(point_0, Java.use("java.lang.Object"));
        console.log("result: " + s_0);
 
        var argList = JNIEnv.newObjectArray(8, cla, ptr(0x0));
        var argList_1 = JNIEnv.newObjectArray(1, cla, ptr(0x0));
        var input0 = JNIEnv.newStringUtf('/rest/n/feed/selectionbb9caf23ee1fda57a6c167198aba919f');
        var input1 = JNIEnv.newStringUtf('d7b7d042-d4f2-4012-be60-d97ff2429c17');
        var input2 = Boolean.$new(false);
        var input2_2 = Boolean.$new(false);
        var input3 = Intger.$new(-1);
        var input5 = JNIEnv.newStringUtf("010a11c6-f2cb-4016-887d-0d958aef1534");
         
        JNIEnv.setObjectArrayElement(argList_1, 0, input0);
        JNIEnv.setObjectArrayElement(argList, 0, argList_1);
        JNIEnv.setObjectArrayElement(argList, 1, input1);
        JNIEnv.setObjectArrayElement(argList, 2, input3.$h);
        JNIEnv.setObjectArrayElement(argList, 3, input2.$h);
        JNIEnv.setObjectArrayElement(argList, 4, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList, 5, ptr(0x0));
        JNIEnv.setObjectArrayElement(argList, 6, input2_2.$h);
        JNIEnv.setObjectArrayElement(argList, 7, input5);
        var point = docommand(JNIEnv, ptr(0x0), 10418, argList); // 返回的是指针,通过cast转成java对象,从而读出来
        var s = Java.cast(point, Java.use("java.lang.Object")); // $className : java.lang.String
        console.log("result: " + s);
        console.log("result: " + Java.vm.tryGetEnv().getStringUtfChars(point).readCString());
    })
}
     
export function jniOnload(){
    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("libkwsgmain.so") !== -1) {
                    this.hook = true;
                }
            },
 
            onLeave: function (retval) {
                if (this.hook) {
                    var jniOnload = Module.findExportByName("libkwsgmain.so", "JNI_OnLoad") || ptr(0x0);
                    Interceptor.attach(jniOnload, {
                        onEnter: function (args) {
                            console.log("Enter Mtguard JNI OnLoad");
                        },
                        onLeave: function (retval) {
                            console.log("After Mtguard JNI OnLoad");
                            callDoCommandNative();
                            // hook_ks();
                        }
                    });
                }
            }
        });
    }
}

这里踩了一个坑,传入的对象数组里有个元素是context类型,打印出来是com.yxcorp.gifshow.App@b97f2c,所以想当然的就按这个类直接new了个对象传了进去,很快出现了报错:
图片描述
这个报错让我百思不得其解,压根就没往context这块想,以为哪怕填个空指针都没问题的。

最后,用笨法frida hooknative层地址,打印定位,找到了如此地方:

图片描述
对应汇编

图片描述

此处判断X23的值是否为0,正常打印出来是/data/app/com.smile.gifmaker-q14Fo0PSb77vTIOM1-iEqQ==/base.apk,调用了getPackageCodePath 方法。

图片描述

这是一个context方法,那必须传入有效的context:

1
2
var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
var context = currentApplication.getApplicationContext();

解决方法就是如此简单,基础,却让我绕了一大圈弯路,不得不感叹,基础真重要啊!

IDA静态分析

  • 花指令
    • 这块看龙哥的分析就好了,非常清晰

Unidbg模拟执行

  • 搭架子 & 补环境
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package com.smile.gifmaker3;
 
import com.github.unidbg.*;
import com.github.unidbg.Module;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.UnicornBackend;
import com.github.unidbg.arm.context.Arm32RegisterContext;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.file.linux.AndroidFileIO;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.api.AssetManager;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.wrapper.DvmBoolean;
import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.spi.SyscallHandler;
import com.github.unidbg.utils.Inspector;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.virtualmodule.android.JniGraphics;
import com.sun.jna.Pointer;
import king.trace.GlobalData;
import king.trace.KingTrace;
import unicorn.Unicorn;
import unicorn.UnicornConst;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
 
public class kswgmain11420 extends AbstractJni implements IOResolver {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;
 
    kswgmain11420() throws FileNotFoundException {
        // 创建模拟器实例,要模拟32位或者64位,在这里区分
        EmulatorBuilder<AndroidEmulator> builder = AndroidEmulatorBuilder.for64Bit().setProcessName("com.smile.gifmaker");
        emulator = builder.build();
        emulator.getSyscallHandler().setEnableThreadDispatcher(true);
        // 模拟器的内存操作接口
        final Memory memory = emulator.getMemory();
        // 设置系统类库解析
        memory.setLibraryResolver(new AndroidResolver(23));
 
        // 创建Android虚拟机
        // vm = emulator.createDalvikVM();
        vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\1142064wei.apk"));
        // 设置是否打印Jni调用细节
        vm.setVerbose(true);
        new JniGraphics(emulator, vm).register(memory);
        new AndroidModule(emulator, vm).register(memory);
        vm.setJni(this);
        SyscallHandler<AndroidFileIO> handler = emulator.getSyscallHandler();
        handler.addIOResolver(this);
 
        // 加载libttEncrypt.so到unicorn虚拟内存,加载成功以后会默认调用init_array等函数
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\libkwsgmain.so"), true);
        // 加载好的libttEncrypt.so对应为一个模块
        module = dm.getModule();
 
        // trace code
//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_jniOnload.trc";
//        GlobalData.ignoreModuleList.add("libc.so");
//        GlobalData.ignoreModuleList.add("libhookzz.so");
//        GlobalData.ignoreModuleList.add("libc++_shared.so");
//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));
 
        dm.callJNI_OnLoad(emulator);
    }
 
    public static void main(String[] args) throws FileNotFoundException {
        kswgmain11420 kk = new kswgmain11420();
        kk.init_native();
        kk.get_NS_sig3();
    }
 
    public void init_native() throws FileNotFoundException {
        // trace code
//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_init_native.trc";
//        GlobalData.ignoreModuleList.add("libc.so");
//        GlobalData.ignoreModuleList.add("libhookzz.so");
//        GlobalData.ignoreModuleList.add("libc++_shared.so");
//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));
 
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv()); // 第一个参数是env
        DvmObject<?> thiz = vm.resolveClass("com/kuaishou/android/security/internal/dispatch/JNICLibrary").newObject(null);
        list.add(vm.addLocalObject(thiz)); // 第二个参数,实例方法是jobject,静态方法是jclass,直接填0,一般用不到。
        DvmObject<?> context = vm.resolveClass("com/yxcorp/gifshow/App").newObject(null); // context
        vm.addLocalObject(context);
        list.add(10412); //参数1
        StringObject appkey = new StringObject(vm,"d7b7d042-d4f2-4012-be60-d97ff2429c17"); // SO文件有校验
        vm.addLocalObject(appkey);
        DvmInteger intergetobj = DvmInteger.valueOf(vm, 0);
        vm.addLocalObject(intergetobj);
        list.add(vm.addLocalObject(new ArrayObject(intergetobj,appkey,intergetobj,intergetobj,context,intergetobj,intergetobj)));
        // 直接通过地址调用
        Number numbers = module.callFunction(emulator, 0x41680, list.toArray());
        System.out.println("numbers:"+numbers);
        DvmObject<?> object = vm.getObject(numbers.intValue());
        String result = (String) object.getValue();
        System.out.println("result:"+result);
    }
 
    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature) {
            case "com/yxcorp/gifshow/App->getPackageCodePath()Ljava/lang/String;": {
                return new StringObject(vm, "/data/app/com.smile.gifmaker-q14Fo0PSb77vTIOM1-iEqQ==/base.apk");
            }
            case "com/yxcorp/gifshow/App->getAssets()Landroid/content/res/AssetManager;": {
//                return new Long(vm, "3817726272");
                return new AssetManager(vm, signature);
            }
            case "com/yxcorp/gifshow/App->getPackageName()Ljava/lang/String;": {
                return new StringObject(vm, "com.smile.gifmaker");
            }
            case "com/yxcorp/gifshow/App->getPackageManager()Landroid/content/pm/PackageManager;": {
                DvmClass clazz = vm.resolveClass("android/content/pm/PackageManager");
                return clazz.newObject(signature);
            }
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }
 
    @Override
    public boolean callBooleanMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature) {
            case "java/lang/Boolean->booleanValue()Z":
                DvmBoolean dvmBoolean = (DvmBoolean) dvmObject;
                return dvmBoolean.getValue();
        }
        return super.callBooleanMethodV(vm, dvmObject, signature, vaList);
    }
 
    public String get_NS_sig3() throws FileNotFoundException {
        // trace code
//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_new.trc";
//        GlobalData.ignoreModuleList.add("libc.so");
//        GlobalData.ignoreModuleList.add("libhookzz.so");
//        GlobalData.ignoreModuleList.add("libc++_shared.so");
//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));
 
        System.out.println("_NS_sig3 start");
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv()); // 第一个参数是env
        DvmObject<?> thiz = vm.resolveClass("com/kuaishou/android/security/internal/dispatch/JNICLibrary").newObject(null);
        list.add(vm.addLocalObject(thiz)); // 第二个参数,实例方法是jobject,静态方法是jclass,直接填0,一般用不到。
        DvmObject<?> context = vm.resolveClass("com/yxcorp/gifshow/App").newObject(null); // context
        vm.addLocalObject(context);
        list.add(10418); //参数1
        StringObject urlObj = new StringObject(vm, "/rest/app/eshop/ks/live/item/byGuest6bcab0543b7433b6d0771892528ef686");
        vm.addLocalObject(urlObj);
        ArrayObject arrayObject = new ArrayObject(urlObj);
        StringObject appkey = new StringObject(vm,"d7b7d042-d4f2-4012-be60-d97ff2429c17");
        vm.addLocalObject(appkey);
        DvmInteger intergetobj = DvmInteger.valueOf(vm, -1);
        vm.addLocalObject(intergetobj);
        DvmBoolean boolobj = DvmBoolean.valueOf(vm, false);
        vm.addLocalObject(boolobj);
        StringObject appkey2 = new StringObject(vm,"7e46b28a-8c93-4940-8238-4c60e64e3c81");
        vm.addLocalObject(appkey2);
        list.add(vm.addLocalObject(new ArrayObject(arrayObject,appkey,intergetobj,boolobj,context,null,boolobj,appkey2)));
        // 直接通过地址调用
        Number numbers = module.callFunction(emulator, 0x41680, list.toArray());
        System.out.println("numbers:"+numbers);
        DvmObject<?> object = vm.getObject(numbers.intValue());
        String result = (String) object.getValue();
        System.out.println("result:"+result);
        return result;
    }
 
    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        System.out.println("fuck:"+pathname);
        return null;
    }
 
    public String readStdString(Pointer strptr){
        Boolean isTiny = (strptr.getByte(0) & 1) == 0;
        if(isTiny){
            return strptr.getString(1);
        }
        return strptr.getPointer(emulator.getPointerSize()* 2L).getString(0);
    }
 
    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "com/kuaishou/android/security/internal/common/ExceptionProxy->getProcessName(Landroid/content/Context;)Ljava/lang/String;":
                return new StringObject(vm, "com.smile.gifmaker");
            case "com/meituan/android/common/mtguard/NBridge->getSecName()Ljava/lang/String;":
                return new StringObject(vm, "ppd_com.sankuai.meituan.xbt");
            case "com/meituan/android/common/mtguard/NBridge->getAppContext()Landroid/content/Context;":
                return vm.resolveClass("android/content/Context").newObject(null);
            case "com/meituan/android/common/mtguard/NBridge->getMtgVN()Ljava/lang/String;":
                return new StringObject(vm, "4.4.7.3");
            case "com/meituan/android/common/mtguard/NBridge->getDfpId()Ljava/lang/String;":
                return new StringObject(vm, "");
        }
        return super.callStaticObjectMethodV(vm, dvmClass, signature,vaList);
    }
}

这里也有一个小tips,让unidbg加载指定SO文件,如果这个SO有依赖其他的SO库,那么很有可能会加载失败。这个情况不同于直接加载apk文件里的so文件,那种情况下unidbg会帮我们自动去寻找需要的SO文件。这个情况下,我们只要把需要的SO文件提取出来,放在同一目录下即可。

  • trace code

    用去花后的so文件,trace关键函数,很快的。

SHA256还原

结合ida伪代码和sha256的伪代码,还原

  • 思路:先看iv和table,然后看明文编排,最后比对具体运算 ———— 龙哥语录
  • sha256算法基础

确定调用栈

  • [确定调用堆栈]
  • 找到sig3最先出现的地方
  • sub_2636c 编排地址:

图片描述

  • sub_2BD20
  • sub_25938
  • sub_120C4

至此,明文输入字符串的加密结果拿到

拼接过程

  • 固定字符串
  • 随机字符串
    • 猜测是随机,进一步验证,追踪,在JNI_OnLoad里:

图片描述

  • 明文字符加密结果
  • 时间戳
  • 固定字符串

END


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

上传的附件:
收藏
点赞7
打赏
分享
最新回复 (5)
雪    币: 19389
活跃值: (29037)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-9-20 10:36
2
1
感谢分享
雪    币: 225
活跃值: (141)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mb_ggnlrzcs 2023-9-20 16:08
3
0
大神666
雪    币: 510
活跃值: (3811)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
codeoooo 2023-10-11 17:17
4
0
666
雪    币: 166
活跃值: (181)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
新手村1号 2023-10-11 17:46
5
0
您好,我要做软件开发,方便给我一个联系方式吗?或者您加我维新appkfz
雪    币: 21
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huxianwei 2024-2-4 18:03
6
0
多少位了?说了半天了
游客
登录 | 注册 方可回帖
返回