-
-
[原创] 第四题 英雄救美题解
-
发表于: 2021-5-15 01:44 6831
-
使用ida打开CrackMe.exe, 跳转到主函数F5, 先做初步分析:
可以看到输入首先经过check_1, 再经过check_2, 然后进行 md5 -> sub_401ed0 -> 申请内存 -> 从4181A0拷贝再做某些处理 > 执行该内存.
后面的部分先放一下, 首先分析check_1:
通过从地址0x416260乱序拷贝数据来初始化somekey, 长度是5*0x10+1
共81字节. 这里选择在动态调试 somekey赋值完成后读取somekey的值.
使用frida代码:
输出结果是
共81字节.
然后:
主体逻辑是在循环中从somekey中找到输入字符的位置, 模9加1后填入input2中. 同时在输入是数字字符时会判断 该字符等于9减去临时已输入的字符数量, 符合时会将临时已输入的字符数量置0, 然后将 从somekey中找输入字符时的起始位置 加9.
然后看check_2:
在循环中将input2填入地址0x4187C4到0x418908之间, 如果该地址内的值非0就会跳过. 注意到是9个为一轮且数据大小为9*9*4
.使用frida按照9*9
打印该地址的数据:
在onEnter时的输出为:
九乘九, 1到9填空, 后面还有一坨验证逻辑, DNA告诉我它是数独. 于是找了个在线解数独的网站:
数独数据为546719238921834657837625419718463925453291786692587143284956371365172894179348562
, 要填入的数据是5619238183457621978469254539786692871328563617281793452
.
然而填入数据来源是输入字符在81字节长的somekey中位置的模9加1, 此时每个字符仍有9种可能, 分析到这时的我还没搞懂check_1中输入是数字字符时的判断逻辑是要我输入什么, 先继续分析接下来的流程.
回到主函数:
注意sub_401ed0的定义, ida认为它是int __cdecl sub_401ED0(int a1, unsigned __int8 *a2)
其中a1和a2都来自栈, 但是在函数中其实使用的是ecx和栈上一个值作为参数, 需要更改该函数的定义为int __fastcall sub_401ED0(int a1, unsigned __int8 *a2, char *a3, char *a4)
, a1, a2来自ecx和edx, 其它来自栈, 函数没有用到a2和a3.
改完后再看sub_401ed0:
查看byte_415960处的数据, 搜索可发现该数据是AES算法的s_box, 可以判断该函数功能是初始化AES密钥. 结合上面的对输入进行md5hash的操作来看,很可能是将输入的md5值作为密钥, 解密接下来的数据. 使用frida调试配合本地nodejs脚本测试解密可以验证算法正确:
可以看出解密后的数据是匹配的.
在没有找到更多对输入的限制的情况下, 似乎只能爆破? 然而输入的可能性仍比9^55还要多, 爆破是不可能爆破的.
这时想到对输入包含0到9字符时的处理逻辑, 看来要猜一猜出题人想要我们输入什么.
当输入了i <= 9个字符后输入数字9-i, 它会将somekey前面截短9个字符. 只输入数独的话用55个字符, 如果再输入9个字符, 长度就会达到64, 刚好满足输入长度限制64. 可以猜测是不是想要选手分9行输入数独, 每行输入完成后输入该行已有的的数字数量, 同时somekey进入下一节.
于是构造输入的逻辑就要变成:
验证发现该输入就是正确的输入, 也是最终的flag.
Interceptor.attach(PE.base.add(
0x12A8
), function() {
console.log((this.context as Ia32CpuContext).ebp.sub(
0x58
).readCString(
81
));
});
Interceptor.attach(PE.base.add(
0x12A8
), function() {
console.log((this.context as Ia32CpuContext).ebp.sub(
0x58
).readCString(
81
));
});
int
__fastcall check_2(
int
*
input2,
int
inplen)
{
ipos
=
0
;
data_p
=
(
int
*
)&unk_4187C4;
do
{
if
( !
*
(data_p
-
1
) )
{
v4
=
input2[ipos
+
+
];
*
(data_p
-
1
)
=
v4;
}
if
( !
*
data_p )
{
v5
=
input2[ipos
+
+
];
*
data_p
=
v5;
}
if
( !data_p[
1
] )
{
v6
=
input2[ipos
+
+
];
data_p[
1
]
=
v6;
}
if
( !data_p[
2
] )
{
v7
=
input2[ipos
+
+
];
data_p[
2
]
=
v7;
}
if
( !data_p[
3
] )
{
v8
=
input2[ipos
+
+
];
data_p[
3
]
=
v8;
}
if
( !data_p[
4
] )
{
v9
=
input2[ipos
+
+
];
data_p[
4
]
=
v9;
}
if
( !data_p[
5
] )
{
v10
=
input2[ipos
+
+
];
data_p[
5
]
=
v10;
}
if
( !data_p[
6
] )
{
v11
=
input2[ipos
+
+
];
data_p[
6
]
=
v11;
}
if
( !data_p[
7
] )
{
v12
=
input2[ipos
+
+
];
data_p[
7
]
=
v12;
}
if
( ipos >
=
inplen )
break
;
data_p
+
=
9
;
}
while
( (
int
)data_p < (
int
)&unk_418908 );
int
__fastcall check_2(
int
*
input2,
int
inplen)
{
ipos
=
0
;
data_p
=
(
int
*
)&unk_4187C4;
do
{
if
( !
*
(data_p
-
1
) )
{
v4
=
input2[ipos
+
+
];
*
(data_p
-
1
)
=
v4;
}
if
( !
*
data_p )
{
v5
=
input2[ipos
+
+
];
*
data_p
=
v5;
}
if
( !data_p[
1
] )
{
v6
=
input2[ipos
+
+
];
data_p[
1
]
=
v6;
}
if
( !data_p[
2
] )
{
v7
=
input2[ipos
+
+
];
data_p[
2
]
=
v7;
}
if
( !data_p[
3
] )
{
v8
=
input2[ipos
+
+
];
data_p[
3
]
=
v8;
}
if
( !data_p[
4
] )
{
v9
=
input2[ipos
+
+
];
data_p[
4
]
=
v9;
}
if
( !data_p[
5
] )
{
v10
=
input2[ipos
+
+
];
data_p[
5
]
=
v10;
}
if
( !data_p[
6
] )
{
v11
=
input2[ipos
+
+
];
data_p[
6
]
=
v11;
}
if
( !data_p[
7
] )
{
v12
=
input2[ipos
+
+
];
data_p[
7
]
=
v12;
}
if
( ipos >
=
inplen )
break
;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!