【CTF】看雪CTF第一题 流浪者

下载题目,从图标初步判断是一个MFC程序,其它也没什么好看的,直接拖入虚拟机双击运行看下显示效果。

双击后程序显示一个输入框,提示输入passwod,并验证。

首先什么都不输入,直接验证,程序弹窗输出提示字符串:“请输入pass!”。

而随意输入pass,则弹窗输出提示字符串:“错了! 加油!”,并且程序退出。

从以上信息我们猜测:

1、如果pass正确,程序应该会和失败时一样弹窗,并且提示相关字符串,我们可以从搜索错误或者正确时提示的字符串入手,也可以从弹窗API入手。

2、既然程序需要用户输入,那我们就从输入控件,或者获取控件字符串的API入手。

3、假如程序是MFC程序,并且用户输入后需要点击按钮验证,我们可以从OD搜索MFC按钮事件代码入手。

4、输入pass错误后,程序会退出,搜索退出函数或API下断,然后代码回溯。

有了以上4个破解思路,我们就可以打开IDA开始分析了。

待IDA分析完代码后,按快捷键Shift+F12查看字符串。

双击可疑字符串,查看代码交叉引用。

F5查看伪代码。

 1 int __thiscall sub_401890(CWnd *this)
 2 {
 3   struct CString *v1; 
 4   CWnd *v2; 
 5   int v3; 
 6   int result; 
 7   int v5[26]; 
 8   int i;
 9   char *Str; 
10   CWnd *v8; 
11 
12   v8 = this;
13   v1 = (CWnd *)((char *)this + 100);
14   v2 = CWnd::GetDlgItem(this, 1002);
15   CWnd::GetWindowTextA(v2, v1);
16   v3 = sub_401A30((char *)v8 + 100);
17   Str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v3);
18   if ( strlen(Str) )
19   {
20     for ( i = 0; Str[i]; ++i )
21     {
22       if ( Str[i] > 57 || Str[i] < 48 )
23       {
24         if ( Str[i] > 122 || Str[i] < 97 )
25         {
26           if ( Str[i] > 90 || Str[i] < 65 )
27             sub_4017B0();
28           else
29             v5[i] = Str[i] - 29;
30         }
31         else
32         {
33           v5[i] = Str[i] - 87;
34         }
35       }
36       else
37       {
38         v5[i] = Str[i] - 48;
39       }
40     }
41     result = sub_4017F0(v5);
42   }
43   else
44   {
45     result = CWnd::MessageBoxA(v8, "请输入pass!", 0, 0);
46   }
47   return result;
48 }

第15、17行代码将用户输入的pass保存到Str。

第18行代码使用strlen函数检测pass长度,如果为空,则第45行代码弹窗提示重新输入。

第22、24、26行代码通过循环比较来判断pass每个字符是否在0-9,a-z,A-Z范围之间。通过IDA快捷键“R”可以将比较数值转换为字符显示。

第29、33、38行代码则对不同范围内的字符进行减法操作:

0-9:Str[i] -= 48

a-z:Str[i] -= 87

A-Z:Str[i] -= 29

第41行程序将处理后的pass交由“sub_4017F0”函数进行验证处理。

 1 int __cdecl sub_4017F0(int a1)
 2 {
 3   int result; 
 4   char Str1[28]; 
 5   int v3; 
 6   int v4; 
 7 
 8   v4 = 0;
 9   v3 = 0;
10   while ( *(_DWORD *)(a1 + 4 * v4) < 62 && *(_DWORD *)(a1 + 4 * v4) >= 0 )
11   {
12     Str1[v4] = aAbcdefghiabcde[*(_DWORD *)(a1 + 4 * v4)];
13     ++v4;
14   }
15   Str1[v4] = 0;
16   if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") )
17     result = sub_401770();
18   else
19     result = sub_4017B0();
20   return result;
21 }

第10行代码循环遍历pass字符,判断pass[i]是否>=0并且<62。

第12行代码如果以上条件满足,程序将pass[i]的值当做下标索引来获取“aAbcdefghiabcde”字符数组对应索引位置的字符,保存到Str1。

第16行代码使用“strcmp”函数比较Str1是否等于“KanXueCTF2019JustForhappy”字符串,如果等于则调用第17行函数,否则调用第19行函数。

第17行代码:pass正确!

第19行代码:pass错误!

假设:

字符串1 = "KanXueCTF2019JustForhappy"。

字符串2 = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQR"。

通过以上分析我们知道,正确的pass在经过一系列减法操作后,最终应该等于字符串1。

而字符串1是通过字符串2得到的。所以通过反向思维,我们首先获取字符串1每个字符在字符串2中的下标索引。

然后根据每一个索引值的范围加上不同的数,最终就是输入时正确的pass!

 1 void deCode()
 2 {
 3     char* key1 = "KanXueCTF2019JustForhappy";
 4     char* key2 = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ";
 5 
 6     for (int i = 0; i < strlen(key1); i++)
 7     {
 8         char* s = strchr(key2, key1[i]);
 9         char c = s - key2;
10         if ((c + 48 >= '0') && (c + 48 <= '9'))
11         {
12             printf("%c", c + 48);
13         }
14         else if ((c + 87 >= 'a') && (c + 87 <= 'z'))
15         {
16             printf("%c", c + 87);
17         }
18         else if ((c+29 >= 'A') && (c+29 <= 'Z'))
19         {
20             printf("%c", c + 29);
21         }
22     }
23     printf("\n");
24 }

 

posted @ 2019-07-24 18:42  SunsetR  阅读(1039)  评论(4编辑  收藏  举报