文章目录
一、easy-so
题目链接:https://adworld.xctf.org.cn/task/task_list?type=mobile&number=6&grade=0
二、答题步骤
1.运行app
点击check提示
2.jadx反编译apk文件
搜索验证失败
字符串
找到源码
发现核查字符串函数:CheckString
3.IDA修改apk逻辑实现破解
上IDA,搜索刚才的方法名check,只有一个结果,双击跳过去,F5转伪代码,大概看一下,就是TestDec输出的字符串和f72c5a36569418a20907b55be5bf95ad
进行对比,如果相等则返回1,也就是验证通过。
_BOOL8 __fastcall Java_com_testjava_jack_pingan2_cyberpeace_CheckString(__int64 a1, __int64 a2, __int64 a3)
{
const char *v3; // r14
size_t v4; // rax
int v5; // er15
unsigned __int64 v6; // r12
char *v7; // rax
char *v8; // r13
bool v9; // cc
size_t v10; // r12
size_t v11; // rbx
char v12; // al
char v13; // al
size_t v14; // rbx
char v15; // al
v3 = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL);
v4 = strlen(v3);
v5 = v4;
v6 = (__int64)((v4 << 32) + 0x100000000LL) >> 32;
v7 = (char *)malloc(v6);
v8 = v7;
v9 = v6 <= v5;
v10 = v6 - v5;
if ( v9 )
v10 = 0LL;
memset(&v7[v5], 0, v10);
memcpy(v8, v3, v5);
if ( strlen(v8) >= 2 )
{
v11 = 0LL;
do
{
v12 = v8[v11];
v8[v11] = v8[v11 + 16];
v8[v11++ + 16] = v12;
}
while ( strlen(v8) >> 1 > v11 );
}
v13 = *v8;
if ( *v8 )
{
*v8 = v8[1];
v8[1] = v13;
if ( strlen(v8) >= 3 )
{
v14 = 2LL;
do
{
v15 = v8[v14];
v8[v14] = v8[v14 + 1];
v8[v14 + 1] = v15;
v14 += 2LL;
}
while ( strlen(v8) > v14 );
}
}
return strcmp(v8, "f72c5a36569418a20907b55be5bf95ad") == 0;
}
const char * v3是传入的字符串,接下来逐个分析代码逻辑:
第一步:两两交换
v4 = strlen(v3);//取变量v4=v3的字符串长度,假设v3="abcd",v4=4
v5 = v4;
v6 = (__int64)((v4 << 32) + 0x100000000LL) >> 32;
v7 = (char *)malloc(v6);//为字符指针v7请求一块长度为v6的内存空间
v8 = v7;
v9 = v6 <= v5;
v10 = v6 - v5;
if ( v9 )
v10 = 0LL;
memset(&v7[v5], 0, v10);//将v7扩增一倍并后面扩增的部分初始化为0,此行代码结束,v7=----0000
memcpy(v8, v3, v5);//将v3的内容复制到v8中
if ( strlen(v8) >= 2 )//若v8的长度大于等于2则执行花括号内的内容
{
v11 = 0LL;//初始化v11=0
do//执行循环
{
v12 = v8[v11];//从第0个开始读取v8的每个字符
v8[v11] = v8[v11 + 16];//逐个将v8的第v11个字符与第v11+16个字符交换位置
v8[v11++ + 16] = v12;//v6自增1
}
while ( strlen(v8) >> 1 > v11 );
}
假设传入字符串为abcd,则上述代码执行完之后的v8为cdab
继续分析接下来的代码:
第二步:头尾互换
v13 = *v8;
if ( *v8 )
{
*v8 = v8[1];
v8[1] = v13;
if ( strlen(v8) >= 3 )
{
v14 = 2LL;
do
{
v15 = v8[v14];
v8[v14] = v8[v14 + 1];
v8[v14 + 1] = v15;
v14 += 2LL;
}
while ( strlen(v8) > v14 );
}
}
根据上述我们直接手动得到flag的code:
1.将f7 2c 5a 36 56 94 18 a2 09 07 b5 5b e5 bf 95 ad两两交换
得到7f c2 a5 63 65 49 81 2a 90 70 5b b5 5e fb 59 da
2.将7fc2a5636549812a 90705bb55efb59da从中间砍断,头尾互换得到90705bb55efb59da 7fc2a5636549812a
得到flag:flag{90705bb55efb59da7fc2a5636549812a}
4.脚本解题和Java源码
python脚本
data = list('f72c5a36569418a20907b55be5bf95ad') //把字符串传入,并转为列表
for i in range(len(data)//2): //第一个循环体
a = data[i]
data[i] = data[i+16]
data[i+16] = a
for i in range(0,len(data),2): //第二个循环体
a2 = data[i]
data[i]=data[i+1]
data[i+1]=a2
key=''.join(data) //拼接字符串
print("flag{"+key+"}") //把题目中的flag{}和最终字符串拼接并输出
java源码解析
@Test
public void demo2() {
String str = "f72c5a36569418a20907b55be5bf95ad";
char[] data = str.toCharArray();
//伪代码中的第二次循环
int index2 = 0;
int result;
int v9;
do {
char v8 = data[index2 + 2];
data[index2 + 2] = data[index2 + 3];
data[index2 + 3] = v8;
result = data.length;
v9 = index2 + 4;
index2 += 2;
}
while (v9 < result);
//循环后进行的简单的互换
result = data[0];
data[0] = data[1];
data[1] = (char) result;
result = data.length;
//伪代码中第一次循环
int index = 0;
do
{
char v4 = data[index];
data[index] = data[index + 16];
data[index+16] = v4;
++index;
}
while ( index < data.length >> 1 );
System.out.println(data);
//输出结果 -> 90705bb55efb59da7fc2a5636549812a
}
总结
- APK
- JADX
- IDA