【i春秋】第十届全国大学生信息安全大赛之逆向--溯源
本帖最后由 skywilling 于 2017-12-25 21:59 编辑0x00前言
考研结束了,终于可以有一段闲暇的时间了。在睡了一天后,不忘发个帖子,疏解一下空虚寂寞的心情,再加上今天是圣诞节{:301_973:}。这个题目其实在暑假就已经解出来了,是准备等到考完研再发出来的,也就是现在{:301_987:}。
下面开始切入正题
0x01运行
运行很简单,输入错误即返回fail提示。
0x02静态分析
首先放到IDA中看一下,入口在哪里
三个关键字也很显眼
根据"please input key:"找到输入口,直接F5
int sub_4014B0()
{
int v0; // eax@1
int v1; // ecx@1
int v2; // eax@1
int v3; // ebx@1
signed int v4; // eax@2
unsigned int v5; // ecx@3
int v6; // edx@3
int v7; // eax@5
int v8; // esi@5
int v9; // edx@5
int v10; // eax@5
char *v11; // edi@9
int v12; // ecx@9
int v13; // esi@9
void *v14; // esi@11
int v15; // eax@11
int v16; // ebx@11
const char *v17; // edx@14
int v18; // eax@15
int v19; // eax@17
char *v20; // ecx@19
unsigned int v21; // eax@22
unsigned int v22; // ecx@24
signed int v24; // @3
int v25; // @5
int v26; // @1
void *Memory; // @9
int v28; // @5
int v29; // @5
int v30; // @1
int v31; // @2
void *v32; // @1
int v33; // @1
unsigned int v34; // @1
char v35; // @1
__int128 v36; // @3
__int128 v37; // @5
int v38; // @1
v0 = sub_401A60(std::cout, "please input key:");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v0, sub_401C90);
v33 = 0;
v34 = 15;
LOBYTE(v32) = 0;
v38 = 0;
sub_401DC0(std::cin, (int)&v32);
sub_401300((unsigned int)&v32, (int)&v35);
sub_401120((int)&v26, (int)&v35, v1); // 将堆栈数据复制到数据段
v2 = 999;
LOBYTE(v38) = 1;
v3 = v26;
v30 = 999;
while ( 2 )
{
v4 = (unsigned __int8)byte_403230;
v31 = 0;
do
{
v5 = v4 & 3;
v24 = v4 >> 2;
v6 = 5 - v5;
v36 = xmmword_403660;
if ( v5 < 2 )
v6 = 1 - v5;
v37 = xmmword_403620;
v7 = *((_DWORD *)&v36 + 2 * v6 + 1);
v8 = v28 + *((_DWORD *)&v36 + 2 * v6);
v9 = v29;
v10 = v29 + v7;
v25 = v8;
if ( v8 < 0 || v8 >= v3 || v10 < 0 || v10 >= v3 )// v3=0xA
{
v19 = sub_401A60(std::cout, "fail");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_401C90);
v14 = Memory;
goto LABEL_18;
}
v29 = v10;
v11 = (char *)Memory + 4 * (v9 + v28 * v3);
v12 = v10 + v8 * v3;
v4 = v24;
v13 = *((_DWORD *)Memory + v12);
*((_DWORD *)Memory + v12) = *(_DWORD *)v11;
*(_DWORD *)v11 = v13;
v28 = v25;
++v31;
}
while ( v31 < 4 );
v2 = v30-- - 1;
if ( v30 >= 0 )
continue;
break;
}
v14 = Memory;
v15 = 0;
v16 = v3 * v3;
if ( v16 <= 0 )
{
LABEL_14:
v17 = "success";
}
else
{
while ( *((_DWORD *)Memory + v15) == v15 )
{
if ( ++v15 >= v16 )
goto LABEL_14;
}
v17 = "fail";
}
v18 = sub_401A60(std::cout, v17);
std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_401C90);
LABEL_18:
j_j_free(v14);
if ( v34 >= 0x10 )
{
v20 = (char *)v32;
if ( v34 + 1 >= 0x1000 )
{
if ( (unsigned __int8)v32 & 0x1F )
invalid_parameter_noinfo_noreturn(v32);
v21 = *((_DWORD *)v20 - 1);
if ( v21 >= (unsigned int)v20 )
v21 = invalid_parameter_noinfo_noreturn(v20);
v22 = (unsigned int)&v20[-v21];
if ( v22 < 4 )
v21 = invalid_parameter_noinfo_noreturn(v22);
if ( v22 > 0x23 )
v21 = invalid_parameter_noinfo_noreturn(v22);
v20 = (char *)v21;
}
j_free(v20);
}
return 0;
}
三个关键字都在这里了
逐一分析这段代码功能
这里把输入的数据直接存到v32
然后调用了 sub_401300((unsigned int)&v32, (int)&v35),进去查看,发现函数名是_DWORD *__fastcall sub_401300(unsigned int input, int a2)
这时v32对应到input,v35对应a2,再往下看
这时,a2赋值到temp,input赋值到v3,
这里有个判断是否为200的地方,其实就是看输入的字符长度是否为200
再往下有个循环
这里其实就是i++,再看一下循环结束条件 while ( v14 != v15 ),往上看v14=0,再找v15,
v3=input,算来算去,v15就是200
这时可以还原出C代码:
for(i=0;i<100;i++){
temp=i;
}
for(i=0;i<200;i++){
temp=temp*16+input-65;
if((i+1)%2==1){
temp=0;
}
}
注意这里有个65,也就是A,到这里可以确认的输入格式是长度为200的大写英文字母,而这个循环就是要将长度为200的输入缩合为长度为100的数据,格式就是A,A,B,B,C,C转换为00,11,22
再往下看
sub_401120((int)&v26, (int)&v35, v1); // 将堆栈数据复制到数据段
这里是保存到了数据段v26
这里可以看到一个长度为1000的数组,类似于置换表
再往下看就是关于数据置换的代码了,下面给出还原后的代码
while(1) {
n=table;
num=0;
do {
low = n & 3;//低三位
high = n >> 2; //高五位
j = 5 - low;
if (low < 2) {
j = 1 - low;
}
a = xmm + l;
b = xmm + k;
//printf("j=%X b=%X a=%X low=%X high=%X n=%X k=%X l=%X\n", j,b, a, low, high, n, k, l);
if (a < 0 || a >= c || b < 0 || b >= c) {
printf("fail\n");
return 0;
}
//交换
c = 0xA * l;
c += k;
d = temp;
//printf("c=%X temp[%d]=%X\n",c,c,d);
temp_ = temp + c;
c = 0xA * a;
c += b;
d_ = temp;
temp = d;
//printf("c=%X temp[%d]=%X\n",c,c,d_);
*temp_ = d_;
//
l = a;
k = b;
n = high;
c = 0xA;
num++;
} while (num < 4);
//print(temp,100);
i--;
if(i<0){
break;
}
}
由于是直接按照汇编还原,代码实现比较冗长
//判断
for(i=0;i<100;i++){
if(temp!=i){
printf("fail\n");
return 0;
}
}
置换后的数据与0-99比较,如果相等就成功了,否则失败
解密过程我会在附件中给出
0x03结尾
老规矩,题目和c代码会在文章最后的附件中给出,下面献上成功的截图:
附件:https://pan.baidu.com/s/1o8wvQAY 密码:i46l
版权声明:允许转载,但是一定要注明出处。
90182si 发表于 2017-12-26 19:20
我是一个新手,想问这里的的input+16什么意思
input是指针,16是偏移量 沙发吗? 学习了! 祝考研成功!!!! 顶一个,路过回复。 我是一个新手,想问这里的的input+16什么意思 祝考研成功。 skywilling 发表于 2017-12-26 20:48
input是指针,16是偏移量
哦,知道了,谢谢 看都看不懂...
页:
[1]
2