拖进模拟器是一个验证框,我们直接上jeb:
调用本地方法public static native int CheckString(String arg0),若验证一致返回1,否则返回0.
将apk重命名为zip后解压,在lib目录将.so文件拖进IDA,找到函数CheckString,代码如下:
_BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)
{
const char *v3; // ST1C_4
size_t v4; // edi
char *v5; // esi
size_t v6; // edi
char v7; // al
char v8; // al
size_t v9; // edi
char v10; // al
v3 = (const char *)(*(int (__cdecl **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
v4 = strlen(v3);
v5 = (char *)malloc(v4 + 1);
memset(&v5[v4], 0, v4 != -1);
memcpy(v5, v3, v4);
if ( strlen(v5) >= 2 )
{
v6 = 0;
do
{
v7 = v5[v6];
v5[v6] = v5[v6 + 16];
v5[v6++ + 16] = v7;
}
while ( v6 < strlen(v5) >> 1 );
}
v8 = *v5;
if ( *v5 )
{
*v5 = v5[1];
v5[1] = v8;
if ( strlen(v5) >= 3 )
{
v9 = 2;
do
{
v10 = v5[v9];
v5[v9] = v5[v9 + 1];
v5[v9 + 1] = v10;
v9 += 2;
}
while ( v9 < strlen(v5) );
}
}
return strcmp(v5, "f72c5a36569418a20907b55be5bf95ad") == 0;
}
我们大胆推测const char * v3是传入的字符串,接下来逐个分析代码逻辑:
v4 = strlen(v3); //取变量v4=v3的字符串长度,假设v3="abcd",v4=4
v5 = (char *)malloc(v4 + 1); //为字符指针v5请求一块长度为v4+1的内存空间
memset(&v5[v4], 0, v4 != -1); //将v5扩增一倍并后面扩增的部分初始化为0,此行代码结束,v5=----0000
memcpy(v5, v3, v4); //将v3的内容复制到v5中
if ( strlen(v5) >= 2 ) //若v5的长度大于等于2则执行花括号内的内容
{
v6 = 0; //初始化v6=0
do //执行循环
{
v7 = v5[v6]; //从第0个开始读取v5的每个字符
v5[v6] = v5[v6 + 16]; //逐个将v5的第v6个字符与第v6+16个字符交换位置
v5[v6++ + 16] = v7; //v6自增1
}
while ( v6 < strlen(v5) >> 1 );
}
假设传入字符串为abcd,则上述代码执行完之后的v5为cdab
继续分析接下来的代码:
v8 = *v5; //指针v8指向v5
if ( *v5 ) //v5存在,执行花括号内的逻辑
{
*v5 = v5[1];
v5[1] = v8;
if ( strlen(v5) >= 3 ) //v5的长度大于等于3
{
v9 = 2; //初始化v9=2
do
{
v10 = v5[v9];
v5[v9] = v5[v9 + 1];
v5[v9 + 1] = v10;
v9 += 2;
}
while ( v9 < strlen(v5) );
}
}
这段代码很简单,就是两两交换。
根据上述我们直接手动得到flag的code:
1.将f72c5a36569418a20907b55be5bf95ad两两交换得到7fc2a5636549812a90705bb55efb59da
2.将7fc2a5636549812a90705bb55efb59da从中间砍断,头拼接到尾,得到90705bb55efb59da7fc2a5636549812a
3.加上flag{}就是flag。