快手新版本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了个对象传了进去,很快出现了报错:快手新版本sig3参数算法还原_第1张图片

对应汇编

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

快手新版本sig3参数算法还原_第2张图片 

这是一个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 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 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 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 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 编排地址:快手新版本sig3参数算法还原_第3张图片
  • sub_2BD20
  • sub_25938
  • sub_120C4

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

拼接过程

  • 固定字符串
  • 随机字符串
    • 猜测是随机,进一步验证,追踪,在JNI_OnLoad里:快手新版本sig3参数算法还原_第4张图片

 

你可能感兴趣的:(逆向开发,python,开发语言)