unidbg 算法还原术· 某民宿app 篇· 下卷
本文由 简悦
SimpRead 转码, 原文地址 mp.weixin.qq.com
int __fastcall bodyEncrypt(JNIEnv *a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10)
{
JNIEnv *v10; // r4
int v11; // r6
int v12; // r5
int v13; // r11
char *v14; // r9
int v15; // ST14_4
int v16; // r6
int v17; // r10
size_t v18; // r0
int v19; // r8
void *v20; // r5
int v21; // r4
int result; // r0
int v23; // [sp+1Ch] [bp-24h]
int v24; // [sp+20h] [bp-20h]
v10 = a1;
v11 = a9;
v12 = a3;
v13 = a7;
if ( !a9 )
v11 = ((*a1)->NewStringUTF)(a1, &unk_13ECB);
if ( !a7 )
v13 = ((*v10)->NewStringUTF)(v10, &unk_13ECB);
if ( !v12 )
v12 = ((*v10)->NewStringUTF)(v10, &unk_13ECB);
v14 = ((*v10)->GetStringUTFChars)(v10, v12, 0);
v15 = v11;
v16 = ((*v10)->GetStringUTFChars)(v10, v11, 0);
v17 = ((*v10)->GetStringUTFChars)(v10, v13, 0);
v23 = 0;
v18 = strlen(v14);
v19 = j_tj_crypt(v14, v18, a5, a6, v17, a8, v16, a10, &v23);
((*v10)->ReleaseStringUTFChars)(v10, v12, v14);
((*v10)->ReleaseStringUTFChars)(v10, v13, v17);
((*v10)->ReleaseStringUTFChars)(v10, v15, v16);
if ( v19 )
{
v20 = j_tjtxtutf8(v19, v23, 10);
v21 = ((*v10)->NewStringUTF)(v10, v20);
free(v20);
}
else
{
v21 = ((*v10)->NewStringUTF)(v10, &unk_13ECB);
}
result = _stack_chk_guard - v24;
if ( _stack_chk_guard == v24 )
result = v21;
return result;
}
流程如下
v19 = j_tj_crypt(v14, v18, a5, a6, v17, a8, v16, a10, &v23);
v20 = j_tjtxtutf8(v19, v23, 10);
v21 = ((*v10)->NewStringUTF)(v10, v20);
result = v21;
return result;
可以打个断点验证下
j_tjtxtutf8:
打印
mr0 0x31
逆向之友验证
https://gchq.github.io/CyberChef
没毛病,接下来看
进到
看到熟悉的
然后这些 “1”,“2”,“3” 是代表加密模式
也就是根据第一个参数的值才决定用哪种加密
我们可以在
public void get_bodyencrypt() throws FileNotFoundException {
List<Object> list = new ArrayList<>();
DvmClass Native=vm.resolveClass("com/tujia/gundam/Gundam");
DvmObject<?> jclass =Native.newObject(null);
String arg0_str = "1";
Object arg0 = vm.addLocalObject(new StringObject(vm, arg0_str));
long arg1 = 1630808396L;
String arg2_str = "YM0A2TMAIEWA3xMAMM1xTjQEUYGD0wZAYAh32AdgQATDzAZNZA33WAEIZAzzhAMMNAzyTDAkZMzTizYOYN11TTgMRcDThyNMZO44GTIAVQGDi5OONN2wWGMMUVWj1iMNOYxxGmUU";
Object arg2 = vm.addLocalObject(new StringObject(vm, arg2_str));
int arg3 = arg2_str.getBytes(StandardCharsets.UTF_8).length;
String arg4_str = "{\"code\":null,\"parameter\":{\"activityTask\":{},\"defa";//"{\"code\":null,\"parameter\":{\"activityTask\":{},\"defaultKeyword\":\"\",\"abTest\":{\"AppSearchHouseList\":\"B\",\"listabtest8\":\"B\"},\"returnNavigations\":true,\"returnFilterConditions\":true,\"specialKeyType\":0,\"returnGeoConditions\":true,\"abTests\":{\"T_login_831\":{\"s\":true,\"v\":\"A\"},\"searchhuojiatujia\":{\"s\":true,\"v\":\"D\"},\"listfilter_227\":{\"s\":true,\"v\":\"D\"},\"T_renshu_292\":{\"s\":true,\"v\":\"D\"},\"Tlisttest_45664\":{\"s\":true,\"v\":\"C\"},\"Tlist_168\":{\"s\":true,\"v\":\"C\"},\"T_LIST27620\":{\"s\":false,\"v\":\"A\"}},\"pageSize\":10,\"excludeUnitIdSet\":null,\"historyConditions\":[],\"searchKeyword\":\"\",\"url\":\"\",\"isDirectSearch\":false,\"sceneCondition\":null,\"returnAllConditions\":true,\"searchId\":null,\"pageIndex\":0,\"onlyReturnTotalCount\":false,\"conditions\":[{\"type\":2,\"value\":\"2021-09-05\"},{\"type\":3,\"value\":\"2021-09-06\"},{\"label\":\"大理州\",\"type\":1,\"value\":\"36\"}]},\"client\":{\"abTest\":{},\"abTests\":{},\"adTest\":{\"m1\":\"Color OS V5.2.1\",\"m2\":\"ade40419318f085ff21b4776f2eef21f\",\"m3\":\"armeabi-v7a\",\"m4\":\"armeabi\",\"m5\":\"100\",\"m6\":\"2\",\"m7\":\"5\"},\"api_level\":260,\"appFP\":\"qA/Ch2zqjORBz90YV34sUZpcFXFV6vzhmAISdTjYAFeqMBTtMUukzQFXqkDokr+sMau0bWClwjtk36nbrVBWVrjmrPTCkXFIraNHdgRVW/QT6g4eLWuM3hhP8qsWgGnrErk2KA+GFxr/OBRMYfV4l0v+TYUDZ5k4bUCUawafdLY5b3aC02SuOrqjW3jjrXiB/dt6ErjrDv44vY4Y8/1r5Z6ut/2BmcErxM37MniKpW6EZc8F4CjJ9S1KRTtEPJ2Kkd2Sd8602jqdgtssJ6QKXyx2+qsKvybydVe+zSTXQGn/T86A6uW0oC+mJHwOLnP8HKN0q2Fu3rTcKZ+Prbs/dcBHaWJi1C1tHZFza2O+1gUQTgvg+Kq57BvE6IjEhveT\",\"appId\":\"com.tujia.hotel\",\"appVersion\":\"260_260\",\"appVersionUpdate\":\"rtag-20210803-183436-zhengyuan\",\"batteryStatus\":\"full\",\"buildTag\":\"rtag-20210803-183436-zhengyuan\",\"buildVersion\":\"8.38.0\",\"ccid\":\"51742042410923060391\",\"channelCode\":\"qq\",\"crnVersion\":\"254\",\"devModel\":\"OPPO R11st\",\"devToken\":\"\",\"devType\":2,\"dtt\":\"\",\"electricity\":\"100\",\"flutterPkgId\":\"277\",\"gps\":null,\"kaTest\":{\"k1\":\"2_1_2\",\"k2\":\"sdm660\",\"k3\":\"ubuntu-16\",\"k4\":\"R11st_11_A.43_200402\",\"k5\":\"OPPO/R11st/R11s:8.1.0/OPM1.171019.011/1577198226:user/release-keys\",\"k6\":\"R11st\",\"k7\":\"OPM1.171019.011\"},\"latitude\":\"23.105714\",\"locale\":\"zh-CN\",\"longitude\":\"113.470271\",\"networkType\":\"1\",\"osVersion\":\"8.1.0\",\"platform\":\"1\",\"salt\":\"ZMmADTBAMIzAzxMAYMkxWjVEFY2TkwNMZAh2WAdAFATzmAZMYA3wTAEckAzT2AMMMAz52DAIZMzThzYMMN1xWTgYMcDD0yNNMO41zTIQUQGDx5OOZN2wDGMMEVWjziMNZYxxDmUA\",\"screenInfo\":\"\",\"sessionId\":\"a3e57f1c-03a8-3bc4-8709-fb36e7698ae4_1630844995950\",\"tId\":\"21090509553913313246\",\"tbTest\":{\"j1\":\"11d890e2\",\"j2\":\"R11s\",\"j3\":\"OPPO R11st\",\"j4\":\"OPPO\",\"j5\":\"OPPO\",\"j6\":\"unknown\",\"j7\":\"qcom\",\"j8\":\"2.1.0 (ART)\"},\"traceid\":\"1630845462612_1630845462444_1630845358749\",\"uID\":\"a3e57f1c-03a8-3bc4-8709-fb36e7698ae4\",\"version\":\"260\",\"wifi\":null,\"wifimac\":\"i5ZQ9aI14FDr9VMJ/ECIg7KAOE+Xev1/CrFoa53WLbE=\"},\"psid\":\"76938405-a463-464a-9c49-752022daf516\",\"type\":null,\"user\":null,\"usid\":null}";
Object arg4 = vm.addLocalObject(new StringObject(vm, arg4_str));
int arg5 = arg4_str.getBytes(StandardCharsets.UTF_8).length;
list.add(vm.getJNIEnv());
list.add(vm.addLocalObject(jclass));
list.add(arg0);
list.add(arg1);
list.add(arg2);
list.add(arg3);
list.add(arg4);
list.add(arg5);
Number number = module.callFunction(emulator, 0x380c+1, list.toArray())[0];
String result = vm.getObject(number.intValue()).getValue().toString();
System.out.println("result: " + result);
}
当
结果为
2uEIVLrtvmHQ72DYG1+fFcCSo5obEd62UMKP+O7zFRx+K6u3FhLY0f3gYdZds/BMRA==
当
结果为
NO3dVW0Fr4yk5Xxoc64Nn903e6sSZK3Uif1TB8dHmVVlXXqzMuteseCb4hfd1/WUQFyHPmxIIRM86pCkymjlgQ==
当
结果为
HAT5BvN81B8PjW69r1sqCvf0cwN/Pey9XoAWBnkuGYEeVpqWTDBuyei8kaZ00XTJig/SKP4qThHwuGgWmlr4fg==
开始逐一击破
加密模式
strncmp “1” 和
所以跳到
有三个函数,分别是
显而易见,加密逻辑在
hook CCCrypt:
public void hook_CCCrypt(){
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz
hookZz.wrap(module.base + 0x4480 + 1, new WrapCallback<HookZzArm32RegisterContext>() {
@Override
// 方法执行前
public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
int arg0 = ctx.getIntArg(0);
int arg1 = ctx.getIntArg(1);
int arg2 = ctx.getIntArg(2);
Pointer arg3 = ctx.getPointerArg(3);
byte[] arg3_ = arg3.getByteArray(0, ctx.getIntArg(4));
int arg4 = ctx.getIntArg(4);
int arg5 = ctx.getIntArg(5);
String arg6 = ctx.getPointerArg(6).getString(0);
int arg7 = ctx.getIntArg(7);
Pointer arg8 = ctx.getPointerArg(8);
int arg9 = ctx.getIntArg(9);
System.out.println("arg0:"+arg0);
System.out.println("arg1:"+arg1);
System.out.println("arg2:"+arg2);
Inspector.inspect(arg3_, "arg3");
System.out.println("arg4:"+arg4);
System.out.println("arg5:"+arg5);
System.out.println("arg6:"+arg6);
System.out.println("arg7:"+arg7);
ctx.push(arg8);
ctx.push(arg9);
};
@Override
// 方法执行后
public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
int length=ctx.pop();
Pointer output = ctx.pop();
byte[] outputhex = output.getByteArray(0, length);
Inspector.inspect(outputhex, "CCCrypt ret");
}
});
hookZz.disable_arm_arm64_b_branch();
}
大胆一点猜,
这时候是先看密钥怎么生成的还是先找出是什么加密算法呢?
都可以,我喜欢倒着推,所以先看
显而易见
双击进去
int __fastcall CCCryptorCreate(int a1, unsigned int a2, void *a3, int a4, int a5, int a6, _DWORD *a7)
{
unsigned int v7; // r5
void **v8; // r8
int v9; // r9
void *v10; // r11
int v11; // r10
int v12; // r4
size_t v14; // r4
_BYTE *v15; // r0
int v16; // r0
void *v17; // [sp+10h] [bp-28h]
size_t v18; // [sp+14h] [bp-24h]
int v19; // [sp+18h] [bp-20h]
v11 = a1;
v12 = 0xFFFFEF34;
if ( a7 )
{
v7 = a2;
if ( a2 <= 5 )
{
v9 = a4;
v10 = a3;
v8 = off_1EB14[a2];
v12 = (*v8)(a1);
if ( !v12 )
goto LABEL_6;
}
}
while ( _stack_chk_guard != v19 )
{
LABEL_6:
v17 = v10;
v14 = v18 + 20;
v18 = v14;
v15 = malloc(v14);
if ( v15 )
{
v10 = v15;
*(v15 + 1) = v14;
*(v15 + 2) = v11;
*(v15 + 3) = v7;
*(v15 + 4) = v8;
*v15 = 1;
v16 = (v8[1])(v15 + 20, v11, v7, v17, v9, a5, a6);
if ( v16 )
{
v12 = v16;
free(v10);
}
else
{
v12 = 0;
*a7 = v10;
}
}
else
{
v12 = -4302;
}
}
return v12;
}
这里伪代码看着有点懵?看上去好像没函数
这里
那
按
.text:00004274 EXPORT CCCryptorCreate
.text:00004274 CCCryptorCreate ; CODE XREF: j_CCCryptorCreate+8↑j
.text:00004274 ; DATA XREF: LOAD:00000450↑o ...
.text:00004274
.text:00004274 anonymous_0 = -0x34
.text:00004274 var_28 = -0x28
.text:00004274 var_24 = -0x24
.text:00004274 var_20 = -0x20
.text:00004274 anonymous_1 = 8
.text:00004274 arg_8 = 0x10
.text:00004274
.text:00004274 ; __unwind {
.text:00004274 PUSH {R4-R7,LR}
.text:00004276 ADD R7, SP, #0xC
.text:00004278 PUSH.W {R1-R11}
.text:0000427C MOV R10, R0
.text:0000427E LDR R0, =(__stack_chk_guard_ptr - 0x4284)
.text:00004280 ADD R0, PC ; __stack_chk_guard_ptr
.text:00004282 LDR R6, [R0] ; __stack_chk_guard
.text:00004284 LDR R0, [R6]
.text:00004286 STR R0, [SP,#0x38+var_20]
.text:00004288 LDR R0, =0xFFFFEF32
.text:0000428A ADDS R4, R0, #2
.text:0000428C LDR R0, [R7,#arg_8]
.text:0000428E CBZ R0, loc_42B2
.text:00004290 MOV R5, R1
.text:00004292 CMP R1, #5
.text:00004294 BHI loc_42B2
.text:00004296 LDR R0, =(off_1EB14 - 0x42A2) ; r0=0x1EB14 - 0x42A2 = 0x1A872
.text:00004298 MOV R9, R3
.text:0000429A MOV R11, R2
.text:0000429C ADD R2, SP, #0x38+var_24
.text:0000429E ADD R0, PC ; off_1EB14
.text:000042A0 MOV R1, R5
.text:000042A2 LDR.W R8, [R0,R5,LSL#2]
.text:000042A6 MOV R0, R10
.text:000042A8 LDR.W R3, [R8]
.text:000042AC BLX R3
.text:000042AE MOV R4, R0
.text:000042B0 CBZ R0, loc_42C8
.text:000042B2
.text:000042B2 loc_42B2 ; CODE XREF: CCCryptorCreate+1A↑j
.text:000042B2 ; CCCryptorCreate+20↑j ...
.text:000042B2 LDR R0, [R6]
.text:000042B4 LDR R1, [SP,#0x38+var_20]
.text:000042B6 SUBS R0, R0, R1
.text:000042B8 ITTTT EQ
.text:000042BA MOVEQ R0, R4
.text:000042BC ADDEQ SP, SP, #0x1C
.text:000042BE POPEQ.W {R8-R11}
.text:000042C2 POPEQ {R4-R7,PC}
.text:000042C4 BLX __stack_chk_fail
.text:000042C8 ; ---------------------------------------------------------------------------
.text:000042C8
.text:000042C8 loc_42C8 ; CODE XREF: CCCryptorCreate+3C↑j
.text:000042C8 LDR R0, [SP,#0x38+var_24]
.text:000042CA STR.W R11, [SP,#0x38+var_28]
.text:000042CE ADD.W R4, R0, #0x14
.text:000042D2 STR R4, [SP,#0x38+var_24]
.text:000042D4 MOV R0, R4 ; size
.text:000042D6 BLX malloc
.text:000042DA CBZ R0, loc_4312
.text:000042DC MOV R11, R0
.text:000042DE LDRD.W R1, R0, [R7,#8]
.text:000042E2 MOVS R2, #1
.text:000042E4 STRD.W R4, R10, [R11,#4]
.text:000042E8 STRD.W R5, R8, [R11,#0xC]
.text:000042EC STRB.W R2, [R11]
.text:000042F0 MOV R2, R5
.text:000042F2 LDR.W R4, [R8,#4]
.text:000042F6 LDR R3, [SP,#0x38+var_28]
.text:000042F8 STRD.W R9, R1, [SP]
.text:000042FC MOV R1, R10
.text:000042FE STR R0, [SP,#0x38+anonymous_0+4]
.text:00004300 ADD.W R0, R11, #0x14
.text:00004304 BLX R4
.text:00004306 CBZ R0, loc_4316
.text:00004308 MOV R4, R0
.text:0000430A MOV R0, R11 ; ptr
.text:0000430C BLX free
.text:00004310 B loc_42B2
.text:00004312 ; ---------------------------------------------------------------------------
按
汇编
指令BLX
指令的格式为:BLX 目标地址BLX
指令从ARM 指令集跳转到指令中所指定的目标地址,
并将处理器的工作状态有ARM 状态切换到Thumb 状态。
显而易见,这里是动态跳转的,也就是说
这里有两种方法获得
第一种方法很简单,用
第二种方法就是静态分析,手动计算出来不就行了嘛?
先挑战一下第二种方法看看能出来不?
.text:000042F2 LDR.W R4, [R8,#4]
即:R4 = [R8 + 0x4] []代表取R8+R4的指针
.text:000042A2 LDR.W R8, [R0,R5,LSL#2]
即:R8 = [R0 + (R5<<2)] ,[]代表取R0+(R5<<2)的指针
.text:0000429E ADD R0, PC ; off_1EB14
即:R0 = R0+PC 这里ida已经算出R0=0x1EB14
.text:00004290 MOV R5, R1
R5=R1 也就是函数的第二个参数,最后追到tj_crypt函数里的v25=v17 = 4
if ( !strncmp("1", v10, n) )
{
v16 = v9;
v17 = 4;
LABEL_15:
v25 = v17;
v9 = 0;
goto LABEL_17;
}
流程:
.text:00004290 MOV R5, R1
.text:0000429E ADD R0, PC ; off_1EB14
.text:000042A2 LDR.W R8, [R0,R5,LSL#2]
.text:000042F2 LDR.W R4, [R8,#4]
R5 = 4
R0 = 0x1EB14
R8 = [R0+(4«2)] = [0x1EB24]
R4 = [R8+4]
双击
所以
最后
第二种获取
hook 0x4304
debugger.addBreakPoint(module.base+0x4304);
笑死,直接就出来了,还静态分析个毛
所以
咦,显而易见这是
打个断点
debugger.addBreakPoint(module.base+0xC244);
查看
密钥是:f4c7fc5d61bdff1914c383b340958ef1
没毛病吧!就是上面说的
然后最终加密的函数呢?
回到
v12 = (*(*(v21 + 16) + 12))(v21 + 20);
有经验了,这一行一看跟上面差不多嘛,看看汇编
BLX R4
debugger.addBreakPoint(module.base+0x453A);
运行
二话不说
public void hook_CC_RC4(){
//rc4密钥
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz
hookZz.wrap(module.base + 0xBEC8 + 1, new WrapCallback<HookZzArm32RegisterContext>() {
@Override
// 方法执行前
public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
Pointer arg0 = ctx.getPointerArg(3);
int arg1 = ctx.getIntArg(1);
Pointer arg2 = ctx.getPointerArg(2);
System.out.println("arg1:"+arg1);
System.out.println("arg2:"+arg2.getString(0));
ctx.push(arg0);
ctx.push(arg1);
};
@Override
// 方法执行后
public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
int length=ctx.pop();
// System.out.println(length);
Pointer output = ctx.pop();
byte[] outputhex = output.getByteArray(0, length);
Inspector.inspect(outputhex, "CC_RC4 ret");
}
});
hookZz.disable_arm_arm64_b_branch();
}
看来前面猜的都没错嘛
现在就用逆向之友看看这个
没毛病
现在明文知道了,也就是
加密方式也知道了,对称算法
最后就要解决密钥是怎么生成的
rc4 key
回到
显而易见!
我们要进入
char *__fastcall sub_302C(int a1, int a2, int a3, int a4)
{
int v4; // r8
char *result; // r0
int v6; // r4
int v7; // r9
char *v8; // r6
v4 = a1;
result = 0;
if ( a2 )
{
v6 = a4;
if ( a4 )
{
v7 = a3;
v8 = malloc(0x15u);
result = 0;
*(v8 + 13) = 0;
*v8 = 0LL;
*(v8 + 1) = 0LL;
*(v8 + 17) = 0;
if ( v8 )
{
j_CCHmac(0, v7, v6, v4);
sub_33E4(v8, 20);
result = v8;
}
}
}
return result;
}
先
public void hook_sub_302C(){
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz
hookZz.wrap(module.base + 0x302C + 1, new WrapCallback<HookZzArm32RegisterContext>() {
@Override
// 方法执行前
public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
Pointer arg0 = ctx.getPointerArg(0);
int arg1 = ctx.getIntArg(1);
int arg3 = ctx.getIntArg(3);
Pointer arg2 = ctx.getPointerArg(2);
System.out.println("arg0:"+arg0.getString(0));
System.out.println("arg1:"+arg1);
System.out.println("arg2:"+arg2.getString(0));
System.out.println("arg3:"+arg3);
};
@Override
// 方法执行后
public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
Pointer output = ctx.getR0Pointer();
byte[] outputhex = output.getByteArray(0, 128);
Inspector.inspect(outputhex, "sub_302C ret");
}
});
hookZz.disable_arm_arm64_b_branch();
}
显而易见是个
哪个函数拼接的?
咦,不对呢,但是可以发现
应该是
这次没错了
如果猜不出是
int __fastcall CCHmac(int a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // r11
int v7; // r4
char v9; // [sp+4h] [bp-194h]
int v10; // [sp+Ch] [bp-18Ch]
void (__fastcall *v11)(int *, int, int); // [sp+160h] [bp-38h]
int v12; // [sp+184h] [bp-14h]
int v13; // [sp+188h] [bp-10h]
v13 = v6;
v7 = a4;
j_CCHmacInit(&v9, a1, a2, a3);
v11(&v10, v7, a5);
j_CCHmacFinal(&v9, a6);
return _stack_chk_guard - v12;
}
显而易见,又是个
进入
看到有个控制流,根据
这里
所以进到这里
可以看到初始化常量
回到上面
这里的
再回到上面,解决这个函数
进入
const char *__fastcall sub_33E4(const char *result, unsigned int a2)
{
unsigned int v2; // r6
const char *v3; // r9
_BYTE *v4; // r4
int v5; // r2
int v6; // r3
signed int v7; // r2
const char *v8; // r0
const char *v9; // r1
int v10; // r3
char v11; // t1
char v12; // t1
int v13; // r2
_BYTE *v14; // r0
int v15; // r1
_BYTE *v16; // r3
char v17; // r5
if ( a2 >= 2 )
{
v2 = a2;
v3 = result;
result = strlen(result);
if ( result >= v2 )
{
v4 = malloc(v2 + 1);
_aeabi_memclr(v4, v2 + 1, v5, v6);
v7 = 1;
v8 = &v3[v2 - 1];
v9 = v3;
while ( 1 )
{
v10 = &v4[v7];
if ( v9 >= v8 )
break;
v11 = *v9++;
*(v10 - 1) = v11;
v12 = *v8--;
v4[v7] = v12;
v7 += 2;
}
if ( v8 == v9 )
*(v10 - 1) = *v9;
v13 = v2 - 1;
v14 = v3 + 1;
v15 = 0;
while ( 1 )
{
v16 = &v4[v15];
if ( &v4[v15] >= &v4[v13] )
break;
v17 = v4[v13--];
*(v14 - 1) = v17;
++v15;
*v14 = *v16;
v14 += 2;
}
if ( v13 == v15 )
*(v14 - 1) = v4[v15];
result = j_free(v4);
}
}
return result;
}
就是对
看着
也可以汇编为主,伪代码和
都不会的还有个更简单的,只需要多搞几组数据对比下,找出规律自己实现就行了;
放出伪代码为主,汇编辅助的代码
import hashlib
import hmac
from hexdump import hexdump
def CCHmac(key:str,input_str:str)->bytes:
hmacsha1_ret = hmac.new(key.encode(), input_str.encode(), hashlib.sha1).digest()
return hmacsha1_ret
def sub_33E4(hmacsha1_ret:bytes,size=20)->bytes:
v4 = [0 for i in range(size)]
v5 = 1
v6 = size
v7 = 0
while (1):
if (v7 >= v6):
break
v9 = hmacsha1_ret[v7]
v7 += 1
v4[v5 - 1] = v9
v6 -= 1
v10 = v6
v4[v5] = hmacsha1_ret[v10]
v5 += 2
if v6 == v7:
hmacsha1_ret = v4
v11 = size
v13 = 0
v12 = 1
v4 = [0 for i in range(size)]
while (1):
if (v13 >= v11):
break
v11 -= 1
v10 = v11
v4[v12 - 1] = hmacsha1_ret[v10]
v9 = hmacsha1_ret[v13]
v13 += 1
v4[v12] = v9
v12 += 2
return bytes(v4)[:16]
if __name__ == '__main__':
input_str = "YM0A2TMAIEWA3xMAMM1xTjQEUYGD0wZAYAh32AdgQATDzAZNZA33WAEIZAzzhAMMNAzyTDAkZMzTizYOYN11TTgMRcDThyNMZO44GTIAVQGDi5OONN2wWGMMUVWj1iMNOYxxGmUU1630808396"
Hmackey = "dGp******dG8K"
v9 = CCHmac(Hmackey,input_str)
key = sub_33E4(v9,20)
hexdump(key)
加密模式
其实后面两种加密模式流程都差不多,简单说一下吧。。文章已经太长了
这里改成
然后根据上面模式
R5=v25=v17=0
R8, [R0,R5,LSL#2]
即:R8=[R0+(0«2)]=[0x1EB14]
同理可得
R4=[R8+4]
最后
不信的话用
还是这个地方
debugger.addBreakPoint(module.base+0x4304);
废话不多说,直接
signed int __fastcall sub_4828(int a1, int a2, int a3, char a4, int a5, unsigned int a6, int a7)
{
int v7; // r5
char v8; // r8
int v9; // r6
char *v10; // r0
signed int v11; // r4
int *v12; // r6
char v13; // r3
int v14; // r12
v7 = a1;
v8 = a4;
v9 = a2;
v10 = sub_4DE0(a3);
v11 = -4300;
if ( a5 && v10 && *((_DWORD *)v10 + 2) <= a6 && *((_DWORD *)v10 + 3) >= a6 )
{
*(_DWORD *)v7 = v10;
if ( v9 == 1 )
{
v12 = (int *)(v10 + 40);
*(_DWORD *)(v7 + 4) = *((_DWORD *)v10 + 8);
v13 = 0;
}
else
{
if ( v9 )
return v11;
v12 = (int *)(v10 + 36);
*(_DWORD *)(v7 + 4) = *((_DWORD *)v10 + 7);
v13 = 1;
}
v14 = *v12;
*(_BYTE *)(v7 + 49) = v8 & 1;
*(_BYTE *)(v7 + 48) = v13;
*(_DWORD *)(v7 + 8) = v14;
if ( v8 & 2 )
{
*(_WORD *)(v7 + 50) = 0;
}
else
{
*(_BYTE *)(v7 + 50) = 1;
if ( v10[16] )
*(_BYTE *)(v7 + 51) = 0;
else
*(_BYTE *)(v7 + 51) = 1;
}
*(_DWORD *)(v7 + 28) = 0;
if ( !(*((int (__fastcall **)(int))v10 + 5))(v7 + 52) )
{
if ( *(_BYTE *)(v7 + 50) )
sub_4E0C((unsigned __int8 *)v7, (int *)a7);
v11 = 0;
}
}
return v11;
}
这函数里只有
那肯定就再
进去
int __fastcall sub_4E0C(unsigned __int8 *a1, int *a2)
{
int *v2; // r3
JNIEnv *v3; // r2
int v4; // r1
int v5; // r2
int v7; // [sp+0h] [bp-30h]
__int64 v8; // [sp+8h] [bp-28h]
__int64 v9; // [sp+10h] [bp-20h]
__int64 v10; // [sp+18h] [bp-18h]
int v11; // [sp+24h] [bp-Ch]
v2 = a2;
v3 = *a1;
if ( *(*a1 + 16) )
{
if ( !a2 )
{
v2 = &v7;
*&v7 = 0LL;
v8 = 0LL;
v9 = 0LL;
v10 = 0LL;
}
(v3[6])(a1 + 52, a1[48], v2);
}
else
{
v4 = (a1 + 12);
if ( !a1[48] )
v4 = (a1 + 32);
v5 = *(v3 + 4);
if ( v2 )
_aeabi_memmove(v4, v2, v5);
else
_aeabi_memclr(v4, v5, v5, 0);
}
return _stack_chk_guard - v11;
}
(v3[6])(a1 + 52, a1[48], v2);
这一行很熟悉了吧,看汇编
直接
debugger.addBreakPoint(module.base+0x4E40);
g 0x11a51
显而易见,这是
hook
debugger.addBreakPoint(module.base+0x11A50);
打印
那
噢这里还有这
ida g 0x119e9
unidbg b 0x119e9
c
进去
b0xE398
c
打印
可以看到这个
加密方式:aes_cbc
明文:{"code":null,"parameter":{"activityTask":{},"defa
aes_key:f4c7fc5d61bdff1914c383b340958ef1
aes_iv:00000000000000000000000000000000
逆向之友验证
没毛病
还想知道
拿到
再
拿到
就是了
加密模式
跟模式
改成
看样子就是比模式二多走了一个
public void hook_sub_302C(){
IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz
hookZz.wrap(module.base + 0x302C + 1, new WrapCallback<HookZzArm32RegisterContext>() {
@Override
// 方法执行前
public void preCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
Pointer arg0 = ctx.getPointerArg(0);
int arg1 = ctx.getIntArg(1);
int arg3 = ctx.getIntArg(3);
Pointer arg2 = ctx.getPointerArg(2);
System.out.println("arg0:"+arg0.getString(0));
System.out.println("arg1:"+arg1);
System.out.println("arg2:"+arg2.getString(0));
System.out.println("arg3:"+arg3);
};
@Override
// 方法执行后
public void postCall(Emulator<?> emulator, HookZzArm32RegisterContext ctx, HookEntryInfo info) {
Pointer output = ctx.getR0Pointer();
byte[] outputhex = output.getByteArray(0, 128);
Inspector.inspect(outputhex, "sub_302C ret");
}
});
hookZz.disable_arm_arm64_b_branch();
}
然后后边的流程就跟模式二就没区别了,就不说了
hook aes_cc_set_iv
debugger.addBreakPoint(module.base+0x11A50);
可以看到这个
这个
加密方式:aes_cbc
明文:{"code":null,"parameter":{"activityTask":{},"defa
aes_key:f4c7fc5d61bdff1914c383b340958ef1
aes_iv:7b53e03485ddbbcf07bd9698718abc8a
逆向之友验证
这些都是标准的算法,遇到魔改算法的时候,就需要对算法的原理非常熟悉了,这时就需要看这个男人的密码学专栏
完结!
饮茶去了