一
分析总结
到最后才发现他的整个基本都是这个样子比如一开始的JNI_OnLoad方法,有空还想把那个指令的执行也分析一下,看一下他是如何入参出参以及执行方法!
二
目标选取
既然目标已经确定了,那就开始吧!
三
开发环境
四
模拟执行
五
算法移植
算法入口
Find native function Java_com_bytedance_frameworks_encryptor_EncryptorUtil_ttEncrypt => RX@0x40007a38[libEncryptor.so]0x7a38
算法分析
so方法修正
JNI方法的修正
traceWrite
// 0x10为传进来的byte[]大小,这个是已知的
emulator.traceWrite(0x40358000, 0x40358000 + 0x10 + 118);
74 63 05 10 ==>0x10056374
00 00 ==> 0x2be0
// 0x1c184
A0 BB 10 FA EC 38 50 03 EA 42 C6 94 15 ED CD 30(0x35038ecfa10bba0、0x30cded1594c642ea)
B6 5F F5 E8 25 43 A3 66 D4 B8 E7 FB D7 F3 89 77 (0x66a34325e8f55fb6、0x7789f3d7fbe7b8d4)
// 0x2a58
[01:48:41 773] Memory WRITE at 0x40358026, data size = 8, data value = 0xeb12c44a6b02c09, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x4035802e, data size = 8, data value = 0x632b08e36313439e, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x40358036, data size = 8, data value = 0x241e2a664df9f031, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x4035803e, data size = 8, data value = 0x2bee5c58693bd0fc, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x40358046, data size = 8, data value = 0x468e9bebbf9629eb, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x4035804e, data size = 8, data value = 0x2a698ed9942a3431, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x40358056, data size = 8, data value = 0x713bbf110c38b4, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x4035805e, data size = 8, data value = 0x217a8beac056e0c6, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x40358066, data size = 8, data value = 0xdc330f68f180331e, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x4035806e, data size = 8, data value = 0xe121d61a37fcf7dc, PC=RX@0x40002a58[libEncryptor.so]0x2a58, LR=RX@0x40002a4c[libEncryptor.so]0x2a4c
[01:48:41 773] Memory WRITE at 0x40358076, data size = 8, data value = 0x77f78e1f1dd196d1, PC=RX@0x40002a88[libEncryptor.so]0x2a88, LR=RX@0x40002a84[libEncryptor.so]0x2a84
[01:48:41 773] Memory WRITE at 0x4035807e, data size = 8, data value = 0x2e7d43264681b810, PC=RX@0x40002a88[libEncryptor.so]0x2a88, LR=RX@0x40002a84[libEncryptor.so]0x2a84
09 2C B0 A6 44 2C B1 0E 9E 43 .....w.,..D,...C
0030: 13 63 E3 08 2B 63 31 F0 F9 4D 66 2A 1E 24 FC D0 .c..+c1..Mf*.$..
0040: 3B 69 58 5C EE 2B EB 29 96 BF EB 9B 8E 46 31 34 ;iX.+.).....F14
0050: 2A 94 D9 8E 69 2A B4 38 0C 11 BF 3B 71 00 C6 E0 *...i*.8...;q...
0060: 56 C0 EA 8B 7A 21 1E 33 80 F1 68 0F 33 DC DC F7 V...z!.3..h.3...
0070: FC 37 1A D6 21 E1 D1 96 D1 1D 1F 8E F7 77 10 B8 .7..!........w..
0080: 81 46 26 43 7D 2E .F&C}.
0x2be0
0x3c2c
debugger.addBreakPoint(module, 0x3C28);
backend.hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == module.base + 0x3C28){
Number x0 = backend.reg_read(Arm64Const.UC_ARM64_REG_X0);
Number x20 = backend.reg_read(Arm64Const.UC_ARM64_REG_X20);
long m0 = x20.longValue()-module.base;
long m1 = x0.longValue()-module.base;
System.out.println("直接调用的是-->" + Long.toHexString(m0));
System.out.println("间接调用的是-->" + Long.toHexString(m1));
}
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base, module.base + module.size, null);
直接调用的是-->2c0c
间接调用的是-->2b40
直接调用的是-->2c0c
间接调用的是-->2b5c
直接调用的是-->2c0c
间接调用的是-->2b40
直接调用的是-->2c0c
间接调用的是-->2b40
直接调用的是-->2c0c
间接调用的是-->2b94
直接调用的是-->2c0c
间接调用的是-->2b40
直接调用的是-->2c0c
间接调用的是-->2ba8
直接调用的是-->2c0c
间接调用的是-->2bb8
直接调用的是-->2c0c
间接调用的是-->2bb8
直接调用的是-->2c0c
间接调用的是-->2bc8
直接调用的是-->2c0c
间接调用的是-->2be8
直接调用的是-->2c0c
间接调用的是-->2c04
直接调用的是-->2c0c
间接调用的是-->2c04
直接调用的是-->2c0c
间接调用的是-->2c04
Map<Integer, String> map = new HashMap<>();
map.put(0x2c0c, "跳板函数,x0为要执行的函数,x1为参数(指针)");
map.put(0x2b40, "malloc");
map.put(0x2b5c, "time、srand、rand随机数函数");
map.put(0x2b94, "又跳到另外一个大的函数过去了(跟入口的0x2C18是一致)");
map.put(0x2ba8, "散列家族算法(6A58)");
map.put(0x2bb8, "memcpy1");
map.put(0x2bc8, "memcpy2");
map.put(0x2be8, "aes相关函数");
map.put(0x2c04, "free");
Backend backend = emulator.getBackend();
backend.hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == module.base + 0x3C28){
Number x0 = backend.reg_read(Arm64Const.UC_ARM64_REG_X0);
Number x20 = backend.reg_read(Arm64Const.UC_ARM64_REG_X20);
long m0 = x20.longValue()-module.base;
long m1 = x0.longValue()-module.base;
System.out.println("直接调用的是-->" + Long.toHexString(m0)+ "t" + map.get((int)m0));
System.out.println("间接调用的是-->" + Long.toHexString(m1)+ "t" + map.get((int)m1));
}
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base, module.base + module.size, null);
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b40malloc
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b5ctime、srand、rand随机数函数
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b40malloc
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b40malloc
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b94又跳到另外一个大的函数过去了(跟入口的0x2C18是一致)
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2b40malloc
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2ba8散列家族算法(6A58)
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2bb8memcpy1
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2bb8memcpy1
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2bc8memcpy2
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2be8aes相关函数
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2c04free
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2c04free
直接调用的是-->2c0c跳板函数,x0为要执行的函数,x1为参数(指针)
间接调用的是-->2c04free
Map<Integer, String> map = new HashMap<>();
map.put(0x2c0c, "跳板函数,x0为要执行的函数,x1为参数(指针)");
map.put(0x2b40, "malloc");
map.put(0x2b5c, "time、srand、rand随机数函数");
map.put(0x2b94, "又跳到另外一个大的函数过去了(跟入口的0x2C18是一致)");
map.put(0x2ba8, "散列家族算法(6A58)");
map.put(0x2bb8, "memcpy1");
map.put(0x2bc8, "memcpy2");
map.put(0x2be8, "aes相关函数");
map.put(0x2c04, "free");
map.put(0x45bc, "跳板函数,x0为要执行的函数,x1为参数(指针)");
map.put(0x44c4, "malloc");
map.put(0x44e0, "散列家族算法(6A58)");
map.put(0x44f0, "memcpy");
map.put(0x4500, "查表");
map.put(0x45B4, "free");
Backend backend = emulator.getBackend();
backend.hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == module.base + 0x3C28 || address == module.base + 0x55D8){
Number x0 = backend.reg_read(Arm64Const.UC_ARM64_REG_X0);
Number x20 = backend.reg_read(Arm64Const.UC_ARM64_REG_X20);
long m0 = x20.longValue()-module.base;
long m1 = x0.longValue()-module.base;
System.out.println("直接调用的是-->" + Long.toHexString(m0)+ "t" + map.get((int)m0));
System.out.println("间接调用的是-->" + Long.toHexString(m1)+ "t" + map.get((int)m1));
}
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base, module.base + module.size, null);
2ba8、44e0(6A58)
// 这里使用Dobby会异常,别问我为啥知道。嘿嘿。想知道这个又可以水一篇文章了,哈哈哈哈
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz,支持inline hook,文档看https://github.com/jmpews/HookZz
hookZz.wrap(module.base + 0x6A58, new WrapCallback<HookZzArm64RegisterContext>() { // inline wrap导出函数
@Override
public void preCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x0 = ctx.getPointerArg(0);
int x1 = ctx.getIntArg(1);
UnidbgPointer x2 = ctx.getPointerArg(2);
byte[] input = x0.getByteArray(0, x1);
Inspector.inspect(input, "input(sha512)");
ctx.push(x2);
emulator.getUnwinder().unwind();
}
@Override
public void postCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x2 = ctx.pop();
int length = 0x40;
Inspector.inspect(x2.getByteArray(0, length), "正式结果");
}
});
>-----------------------------------------------------------------------------<
[02:55:46 216]input(sha512), md5=7e6de8344f55d460143710510155b657, hex=2ad949a94f9867924645bdfc952302dea3193a95cbe9913e3bbea28a659eaa90
size: 32
0000: 2A D9 49 A9 4F 98 67 92 46 45 BD FC 95 23 02 DE *.I.O.g.FE...#..
0010: A3 19 3A 95 CB E9 91 3E 3B BE A2 8A 65 9E AA 90 ..:....>;...e...
>-----------------------------------------------------------------------------<
[02:55:46 234]正式结果, md5=bed9304b171af26ae94b5fcaccd1fd6b, hex=17b4b1b1094010ca669a8c2116ad14a25b40302b693684f23686cad7817f5295064c06ab34f5f55a8dfceee08bd2ae3a8cb12d52fbc7981e0b4ed5f995b921eb
size: 64
0000: 17 B4 B1 B1 09 40 10 CA 66 9A 8C 21 16 AD 14 A2 [email protected]..!....
0010: 5B 40 30 2B 69 36 84 F2 36 86 CA D7 81 7F 52 95 [@0+i6..6.....R.
0020: 06 4C 06 AB 34 F5 F5 5A 8D FC EE E0 8B D2 AE 3A .L..4..Z.......:
0030: 8C B1 2D 52 FB C7 98 1E 0B 4E D5 F9 95 B9 21 EB ..-R.....N....!.
^-----------------------------------------------------------------------------^
2b5c
hookZz.wrap(module.base + 0x2B5C, new WrapCallback<HookZzArm64RegisterContext>() { // inline wrap导出函数
@Override
public void preCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x0 = ctx.getPointerArg(0);
ctx.push(x0);
}
@Override
public void postCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x0 = ctx.pop();
Inspector.inspect(x0.getPointer(0).getByteArray(0, 0x100), "测试哦");
}
});
2be8
debugger.addBreakPoint(module, 0x2948);
hookZz.wrap(module.base + 0x2948, new WrapCallback<HookZzArm64RegisterContext>() {
@Override
public void preCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x0 = ctx.getPointerArg(0);
int x1 = ctx.getIntArg(1);
UnidbgPointer x2 = ctx.getPointerArg(2);
UnidbgPointer x3 = ctx.getPointerArg(3);
int x4 = ctx.getIntArg(4);
byte[] key = x0.getByteArray(0, x1);
byte[] iv = x2.getByteArray(0, x1);
byte[] input = x3.getByteArray(0, x4);
Inspector.inspect(key, "key");
Inspector.inspect(iv, "iv");
Inspector.inspect(input, "input(aes)");
UnidbgPointer x5 = ctx.getPointerArg(5);
int x6 = ctx.getPointerArg(6).getInt(0);
ctx.push(x5, x6);
}
@Override
public void postCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
UnidbgPointer x5 = ctx.pop();
int x6 = ctx.pop();
Inspector.inspect(x5.getByteArray(0, x6), "aes加密结果");
}
});
分析整理
最后一段:
中间段
到这里我们就剩下最后一个问题了了,aes的key/iv如何生成?
0040: 4D D4 C2 E6 B8 31 62 09 0E 52 B3 C7 A6 73 3B A4 M....1b..R...s;.
0050: 1C B2 46 2B 82 9A B5 8A 19 6B 39 DB 57 17 75 24 ..F+.....k9.W.u$
0060: F4 9B AF 7F 08 E8 D6 8D 26 A7 2E 37 C1 A9 5A 2F ........&..7..Z/
0070: 1F 05 A5 18 92 AE F2 94 97 32 B6 2A 38 AA DD 58 .........2.*8..X
六
最后总结
七
2023年12月20日10点34分更新
debugger.addBreakPoint(module, 0x4500);
0-->80 60 35 40
1-->00 00 00 00
2-->40 00 00 00
v1 = result[2];
v2 = *result;
v3 = result[1];
就可以直到v1=0x40,v2为0x40356080,v3为0
v1 =
b0x4560
>>> q0=0xa43b73a6c7b3520e096231b8e6c2d44d(1.8056244479338115E-263, -3.7768713601031674E-134) q1=0x24751757db396b198ab59a822b46b21c(-4.496232995265719E-257, 4.642831181428702E-133)
q0=0x2f5aa9c1372ea7268dd6e8087faf9bf4(-5.367597597728704E-242, 1.4054386263309782E-80) q1=0x58ddaa382ab6329794f2ae9218a5051f(-9.09210480120694E-208, 1.1969099196948154E120)
0040: 4D D4 C2 E6 B8 31 62 09 0E 52 B3 C7 A6 73 3B A4 M....1b..R...s;.
0050: 1C B2 46 2B 82 9A B5 8A 19 6B 39 DB 57 17 75 24 ..F+.....k9.W.u$
0060: F4 9B AF 7F 08 E8 D6 8D 26 A7 2E 37 C1 A9 5A 2F ........&..7..Z/
0070: 1F 05 A5 18 92 AE F2 94 97 32 B6 2A 38 AA DD 58 .........2.*8..X
看雪ID:陈可牛
https://bbs.kanxue.com/user-home-794149.htm
#
原文始发于微信公众号(看雪学苑):ttEncrypt算法的简单分析
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论