看代码是要先注册,先调doRegister注册,判断已注册再调work
doRegister跳到RegActivity
输入内容传到saveSN
saveSN是个native方法,这里还看到了work也是native方法,就是核心代码都在so里
下面来分析so
直接看导出函数没找到saveSN和work,那应该是动态注册的
直接看JNI_OnLoad,确实是动态注册的,n1就是initSN,n2是saveSN,n3是work
n2是saveSN,那直接看n2,这里的加密逻辑大概就是第1、4、7、10、13位是w,2、5、8、11位是_,剩下的3、6、9是a,组合起来的字符串就是“w_aw_aw_aw_aw”,
这里也可以直接动态调试得到解密字符串“w_aw_aw_aw_aw”
这里就是异或,R3是我们输入的,R2就是解密的结果,这里会循环执行;动态调式看R2的值就是循环的0x77、0x5f、0x61,转换就是“w_aw_aw_aw_aw”;
然后拿“w_aw_aw_aw_aw”和用户输入的字符串进行逐位异或,结果保存到/sdcard/reg.dat
int __fastcall n2(_DWORD *a1, int a2, int a3)
{
JNIEnv **v3; // r6
int string; // r9
FILE *v5; // r7
int *v7; // r4
const char *v8; // r3
int v9; // r0
int v10; // r1
_WORD *v11; // r5
JNIEnv **v12; // r0
int v13; // r4
JNIEnv *v14; // r3
signed int v15; // r6
const char *v16; // r9
const char *v17; // r5
signed int v18; // r10
char v19; // r2
char v20; // r3
int v21; // [sp+0h] [bp-38h]
int v22; // [sp+14h] [bp-24h]
char v23; // [sp+18h] [bp-20h]
v3 = a1;
string = a3;
v5 = fopen("/sdcard/reg.dat", "w+");
if ( v5 )
{
if ( v22 == _stack_chk_guard )
return j___android_log_print(3, "com.gdufs.xman", &unk_2DCA);
}
else
{
v7 = &v21;
v8 = "W3_arE_whO_we_ARE";
do
{
v9 = *v8;
v8 += 8;
v10 = *(v8 - 1);
*v7 = v9;
v7[1] = v10;
v11 = v7 + 2;
v7 += 2;
}
while ( v8 != "E" );
v12 = v3;
v13 = 2016;
*v11 = *v8;
v14 = *v3;
v15 = 0;
v16 = (v14[169])(v12, string, 0);
v17 = v16;
v18 = strlen(v16);
while ( v15 < v18 )
{
if ( v15 % 3 == 1 ) //1、4、7、10、13
{
v13 = (v13 + 5) % 16; // 5
v19 = *(&v23 + v13 - 23); // w
}
else if ( v15 % 3 == 2 ) //2、5、8、11
{
v13 = (v13 + 7) % 15; // 13
v19 = *(&v23 + v13 - 22); // _
}
else //剩下的就是3、6、9
{
v13 = (v13 + 3) % 13; // 4
v19 = *(&v23 + v13 - 21); // a
}
v20 = *v17;
++v15;
*(++v17 - 1) = v20 ^ v19; // 拿输入的字符和这里加密的字符异或然后保存到/sdcard/reg.dat
}
fputs(v16, 0);
}
return j_fclose(v5);
}
到这里都没看到验证的点,不急我们继续看work,也就是n3,这个也啥都没有就调了n1,那验证的代码肯定在n1了
int __fastcall n3(int a1)
{
int v1; // r4
int v2; // r0
JNIEnv *v3; // r0
void *v4; // r1
bool v5; // zf
v1 = a1;
n1(a1);
v2 = getValue(v1);
if ( v2 )
{
v5 = v2 == 1;
v3 = v1;
if ( v5 )
v4 = &unk_2E6B; //验证通过之后的toast
else
v4 = &unk_2E95;
}
else
{
v3 = v1;
v4 = &unk_2E5B;
}
return callWork(v3, v4);
}
最后看n1,直接从/sdcard/reg.dat取值和"EoPAoY62@ElRD"比较,到这里就已经很明了了,就是拿用户输入^“w_aw_aw_aw_aw”判断是否等于"EoPAoY62@ElRD",那直接“w_aw_aw_aw_aw”逐位异或"EoPAoY62@ElRD"就得到了用户输入也就是flag了;
int __fastcall n1(int a1)
{
int v1; // r6
FILE *v2; // r0
FILE *v3; // r4
int v4; // r0
FILE *v5; // r1
int v6; // r7
void *v7; // r5
int v9; // r0
signed int v10; // r1
v1 = a1;
v2 = fopen("/sdcard/reg.dat", "r+");
v3 = v2;
if ( !v2 )
{
v4 = v1;
v5 = v3;
return setValue(v4, v5);
}
fseek(v2, 0, 2);
v6 = ftell(v3);
v7 = malloc(v6 + 1);
if ( !v7 )
{
fclose(v3);
v4 = v1;
v5 = 0;
return setValue(v4, v5);
}
fseek(v3, 0, 0);
fread(v7, v6, 1u, v3);
*(v7 + v6) = 0;
if ( !strcmp(v7, "EoPAoY62@ElRD") )
{
v9 = v1;
v10 = 1;
}
else
{
v9 = v1;
v10 = 0;
}
setValue(v9, v10);
return j_fclose(v3);
}
最后得到flag:“201608Am!2333”,看到toast还强行给加个格式,那flag就是:xman{201608Am!2333}
解题思路没啥问题,但其实还可以更简单一些,不用解密“w_aw_aw_aw_aw”也可以。
它的逻辑是这样的:
输入的flag与一个解密的字符串异或得到"EoPAoY62@ElRD",输入的flag就是正确的,它会把异或的结果保存,那么反过来我们输入"EoPAoY62@ElRD"异或后的结果就是flag了,直接从保存的文件中拿到异或的结果就是正确的flag了;
flag^? =“EoPAoY62@ElRD”
那么
"EoPAoY62@ElRD“^?=flag
这里的?就是解密得到的“w_aw_aw_aw_aw”,所以其实根本不用解它,不管它是个啥,我们直接输入"EoPAoY62@ElRD“,那么异或之后的结果就是flag,
前面说过这个结果会保存到/sdcard/reg.dat,所以直接从/sdcard/reg.dat就可以拿到flag了;