逆向基础学习(二)

偶然知道可以用wine来运行windows下的exe程序,那么俺要转行了嘻嘻~

环境准备

在Linux下可以安装wine来运行exe程序:

可以通过

sudo apt-get install wine

来尝试安装,安装时会提示让你选择wine-stable 3.0-1ubuntu1wine-development 3.6-1其中一个,之后使用:

sudo apt-get install wine-stable

命令安装即可,之后只需用wine 程序.exe即可运行:

反汇编函数中出现的标识含义:

sub_XXXXXX 子程序
loc_xxxxxx 地址
byte_xxxxxx 8位数据
word_xxxxxx 16位数据
dword_xxxxxx 32位数据
unk_xxxxxx 未知的

常见函数

1.strcpy

char *strcpy(char* dest, const char *src);
功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间

2.strcmp

extern int strcmp(const char *s1,const char *s2);
两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止
当s1<s2时,返回为负数;
当s1=s2时,返回值= 0;
当s1>s2时,返回正数。

lucky number题目分析

首先将程序拖到ida64打开,不过发现不能反编译,那么将其拖到ida32打开;可以事先用file命令查看其格式:

找到主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ecx
int v4; // eax
unsigned int v5; // kr04_4
int result; // eax
int i; // ecx
char v8; // al
char v9; // dl
int v10; // [esp-800h] [ebp-800h]
int v11; // [esp-400h] [ebp-400h]

sub_404A50(&unk_414148);
sub_404A50(aPleaseGiveMeWh);
v3 = *(_DWORD *)(dword_417DB0 + 4);
v4 = sub_401180((char *)0xA);
sub_401330(&dword_417DB0, &v10, 1023, v4);
strcpy((char *)&v11, (const char *)&v10);
v5 = strlen((const char *)&v10) + 1;
if ( v5 - 1 != strlen(aH5wg2gMcifT1ou) )
{
sub_404A50(aYouBadGuy);
return -1;
}
for ( i = 0; i <= (signed int)(v5 - 2); ++i )
{
v8 = *((_BYTE *)&v10 + i);
if ( v8 > 90 || v8 < 65 )
{
if ( v8 > 122 || v8 < 97 )
continue;
v9 = (v8 - 83) % 26 + 97;
}
else
{
v9 = (v8 - 51) % 26 + 65;
}
*((_BYTE *)&v10 + i) = v9;
}
if ( !strcmp(aH5wg2gMcifT1ou, (const char *)&v10) )
{
sub_404A50(aIAgreeWithYouD);
system(aPause);
result = 0;
}
else
{
sub_404A50(aYouBaaaaaadGuy);
result = -1;
}
return result;
}

首先进行长度判断:

if ( v5 - 1 != strlen(aH5wg2gMcifT1ou) )
  {
    sub_404A50(aYouBadGuy);
    return -1;
  }

双击aH5wg2gMcifT1ou查看变量值,其对应值为H5wg_2g_MCif_T1ou_v7v7v,长度为23。可大致判断出v10为输入,且输入的长度要等于23;那么v5等于24

最后输出flag的判断条件:

if ( !strcmp(aH5wg2gMcifT1ou, v10) )
  {
    sub_404A50(aIAgreeWithYouD);
    system(aPause);
    result = 0;
  }

即最终v10要等于H5wg_2g_MCif_T1ou_v7v7v。这个判断上面的循环语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for ( i = 0; i <= (signed int)(v5 - 2); ++i )
{
v8 = v10[i];
if ( v8 > 90 || v8 < 65 )
{
if ( v8 > 122 || v8 < 97 )
continue;
v9 = (v8 - 83) % 26 + 97;
}
else
{
v9 = (v8 - 51) % 26 + 65;
}
v10[i] = v9;
}

即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for ( i = 0; i <= 22; ++i )
{
v8 = v10[i];
if ( v8 > 90 || v8 < 65 )
{
if ( v8 > 122 || v8 < 97 )
continue;
v9 = (v8 - 83) % 26 + 97;
}
else
{
v9 = (v8 - 51) % 26 + 65;
}
v10[i] = v9;
}

有两个v9的式子,由第一个v9 = (v8 - 83) % 26 + 97;可知v9范围为[97, 122];第二个v9 = (v8 - 51) % 26 + 65;可知v9范围为[65, 90]

那么我们可以首先判断最后得到的v10的范围,如果v10[97, 122],那么可以倒推求出v8:v10 - 97 + 83,但这个v8可能不是最开始正确的v8,因为有个求余的表达式,因此可根据最开始的v8要在[97, 122]范围内,如果求得的v8小于97,那么可以加上26;下面的分支同理;对应的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
v10 = 'H5wg_2g_MCif_T1ou_v7v7v'
for i in v10:
i = ord(i)
if 97<=i<=122:
res = i - 97 + 83
if res < 97:
for n in range(1, 5):
res = (i - 97) + 26*n + 83
if 97<=res<=122:
break
print(chr(res), end='')
elif 65<=i<=90:
res = i - 65 + 51
if res < 65:
for n in range(1, 5):
res = (i - 65) + 26*n + 51
if 65<=res<=90:
break
print(chr(res), end='')
else:
print(chr(i), end='')

经景同学指点,这道题可以不用逆向的思想,直接通过爆破128个ascii字符,通过比较经过流程运算后的字符是否相同来判断是否为正确的flag字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v10='H5wg_2g_MCif_T1ou_v7v7v'
v11 = [i for i in v10]

for i in range(len(v10)):
for j in range(0,128):
if j > 90 or j< 65 :
if j > 122 or j < 97:
continue
v9 = (j - 83) % 26 + 97
else:
v9 = (j - 51) % 26 + 65
if v10[i]==chr(v9): # 如果运算得到的结果相同,那么该字符为最初的flag字符
v11[i] = chr(j) # 因为有的字符不在上面两个范围内,需要直接输出,因此可以通过“如果满足范围条件,运算后替换列表字符,不满足范围则不改变”的方式得到flag

print(''.join(v11))

flag:T5is_2s_YOur_F1ag_h7h7h