首页
社区
课程
招聘
1
[原创] 第三题:街机少年解题步骤--连蒙带猜爆破
发表于: 2019-12-6 13:56 4824

[原创] 第三题:街机少年解题步骤--连蒙带猜爆破

2019-12-6 13:56
4824

引用

工具:ida pro7.0
使用ida打开文件,可以看到函数表。其中有个名字较start的,本来想顺着这个start的调用一直跟下去找的处理逻辑,但跟了几层之后觉得有点烦,就决定先找字符串。

往上拖到开头,本来是想看下什么版本的分析之类的,结果第一个函数里就看到命令行输出的字符串,基本可以确认就是程序逻辑,于是分析调用和处理流程。

用于控制台输入输出的函数从上下文可以直接分析出来,比如这个sub_40471D显然就是输出,所以不用跟进去。
得到大致流程:
逐个字符的获取输入的用户名,序列号,以及长度,分别存入全局变量,从这里可以看出数据长度限制。

然后输入的序列号经过一个处理函数,生成真正的密码和长度。经过分析,这个处理函数将输入的四个58-122之间的字节为一组,通过位运算映射为三个字节。。。这就是一个base64算法的变形,58-122刚好也就是64个,'z'相当于标准base64的'=',这可能是我唯一熟悉的加密算法来。至少这一步可以确定是可逆的,有点感动。

接下来通过XREF找到调用getinput的地方,发现接下来过程很直接,就是将第一步得到的用户名和密码通过一个函数处理判断是否正确,因此关键的验证逻辑就是checkpass这个函数了。


解题的过程中顺便看了看题主的发帖记录,找到这样一篇,也是类似的题目。
题目设计非常精巧,反正我是看不懂。并且总是强调多解就女装什么的。。。不知道是自信还是其实想女装(小声)
虽然题目肯定不一样,但是还是能得到一些启发。例如里面的描述方法对理解本题有些帮助,可以把base64得到的密码当成指令来看待。
chekpass代码特别长,毕竟是核心算法,经过分析密码和用户名的调用(这里是只读的)以及如何让函数返回正确,可以将验证逻辑大致可以解释为以下几步:
  1. 将用户名的第一位经过一系列运算得到一个初始值。
  2. 将用户名其余部分调用一个函数经过一系列运算,修改了一个长度15的数组(实际是char[256]但是只有前十五个字符在使用),暂且把这个表当成hash(就是取个名),这个表在后续步骤中使用,而用户名再也没有用过。
  3. 进入循环,每次取出密码中的一项经过一系列复杂操作,分别取高位和低位经过几个循环迭代,得到结果,过程中用到并改变的1中生成的初始值(当成译码看待),最后用这个密码字符得到的指令,修改了hash表中的指定一项(这里里我称为主动修改)。          (这一步里有一个校验指令的过程,可能导致循环退出,具体条件太复杂我也看不懂。只能知道不能走到这个条件)因为代码译码的过程用的变量太多,难以一一分析,所以我暂时没有先研究具体过程,只看这整个步骤对结果的影响。
  4. 取出了密码中的该项和下一项,经过运算,修改了3中译码操作需要用到的一些值,再加上3中也修改了部分变量,也就是改变了下一次译码的规则,这就是说我们不能从数据到指令建立一个一一映射的关系,因此很难倒推回去。
  5. 从hash表中取出所有数据,经过一系列操作作为数组下标,几次迭代,最后从一个大的表中取出一个数据,根据这个数据来进入一个判断,也就是步骤6。
  6. case1:从另一个表中取出数据修改hash,并且是把hash中的每一项都改变,因此既有可能产生0,也有可能把之前已经消为0的值变为非0,造成破坏性后果,具体会发生什么很难分析;case2,有一个指针指向hash中的数据,每次进入这个分支会改变hash中的一个数据,并且重要的是,根据判断条件,只会改变>=1的一个数据(写的时候才突然意识到这一点),而且这里指针指向采用+4%15的方法,可以遍历整个hash表。这一步我称之为被动修改。
可是我确实就不会反调试,也不知道断点要下在哪,也不想试,所以只能先假设没有什么变形,试试运气(实际上我觉得这个算法这么复杂,是假的可能性不大,而且各个数据表从ida的xref里也没找到别的读写,但是也有可能在初始化时产生了新的函数修改了部分表)。
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
KCTF2019
Please input your username: ( A to Z, a to z, 0 to 9 )
f3a25f8b29bb9cbc
Please input your KEY: ( : to z )
JXg@vwrpfoLbRcxvdYrvv[kyRSso
hashis:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 6, 7, 14, 15,
 
hashis:5, 1, 6, 3, 3, 7, 7, 10, 9, 10, 7, 6, 6, 10, 11,
ind:0, pass:65
change:id14+=-5
hashis:5, 1, 6, 3, 3, 7, 7, 10, 9, 10, 7, 6, 6, 10, 6,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 10, 7, 6, 6, 10, 6,
 
ind:1, pass:-19
change:id14+=-6
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 10, 7, 6, 6, 10, 0,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 10, 0,
 
ind:2, pass:-122
change:id13+=-3
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 7, 0,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 4, 0,
 
ind:3, pass:-13
change:id12+=-1
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 4, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 4, 0,
 
ind:4, pass:-40
change:id13+=-2
hashis:5, 1, 0, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 2, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 7, 6, 5, 2, 0,
 
ind:5, pass:-10
change:id13+=-1
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 7, 6, 5, 1, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 3, 6, 5, 1, 0,
 
ind:6, pass:-77
change:id12+=-4
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 3, 6, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 10, 9, 7, 3, 6, 1, 1, 0,
 
ind:7, pass:82
change:id11+=-2
hashis:5, 1, 0, 1, 3, 1, 4, 10, 9, 7, 3, 4, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 3, 4, 1, 1, 0,
 
ind:8, pass:104
change:id10+=-3
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 0, 4, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 0, 1, 1, 1, 0,
 
ind:9, pass:98
change:id9+=-5
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 2, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 3, 1, 4, 9, 9, 2, 0, 1, 1, 1, 0,
 
ind:10, pass:-98
change:id6+=-2
hashis:3, 1, 0, 1, 3, 1, 2, 9, 9, 2, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 9, 9, 2, 0, 1, 1, 1, 0,
 
ind:11, pass:-4
change:id9+=-1
hashis:3, 1, 0, 1, 0, 1, 2, 9, 9, 1, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 9, 6, 1, 0, 1, 1, 1, 0,
 
ind:12, pass:-87
change:id7+=-3
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 1, 0,
 
ind:13, pass:-8
change:id13+=-1
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 0, 0,
 
ind:14, pass:-4
change:id11+=-1
hashis:3, 0, 0, 1, 0, 1, 2, 6, 6, 1, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 0, 2, 6, 6, 1, 0, 0, 0, 0, 0,
 
ind:15, pass:-14
change:id9+=-1
hashis:3, 0, 0, 1, 0, 0, 2, 6, 6, 0, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0,
 
ind:16, pass:17
change:id8+=-2
hashis:3, 0, 0, 1, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
 
ind:17, pass:-1
change:id0+=-1
hashis:2, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
case2:
hashis:2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 0, 0,
 
ind:18, pass:97
change:id8+=-3
hashis:2, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0,
case2:
hashis:0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0,
 
ind:19, pass:-103
change:id7+=-2
hashis:0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
case2:
hashis:0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
 
ind:20, pass:-11
change:id7+=-1
hashis:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
You Win! You are very clever!
请按任意键继续. . .

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
53
54
55
56
57
58
int main()
{
  char tempcachetable[256]={0},temphashname[256]={0};
  strcpy(tempcachetable,cachetable);//保存初始表格
  strcpy(temphashname,hashname);
  char *constname="KCTF";
  len_name=4;
  strcpy(uname,constname);
  bool run=true;
  int k=0;
  while(run){
      for(int i=0;i<256;i++){
          strcpy(cachetable,tempcachetable);
          strcpy(hashname,temphashname);
          pass[k]=(char)i;
          passlen=k+1;
          if(checkpass((unsigned __int8 *)uname, len_name))
          {run=false;cout<<"success!!!"<<endl;break;}//最后一次是主动修改完成,不需要进入case2,已经直接得到了答案
          if(iscase2){
              cout<<endl<<k<<"--------------"<<i<<endl<<endl;
              iscase2=false;
              break;
          }
          if(i==255){cout<<"failed"<<endl;return 0;}
      }
      k++;
      if(k>=96){
      cout<<"failed"<<endl;
      break;
      }
      int jj;
    cout<<"pass:";
    for ( jj = 0; jj <passlen; ++jj )
      {
        cout<<(int)pass[jj]<<",  ";
      }
    cout<<"len is:"<<passlen<<endl;
  }
    char result[256]={0};//base64加密结果
    ab64e(pass,result,passlen);
    cout<<result<<endl;
      cout<<endl;
/*
  if ( getinput() )
  {
    if ( checkpass((unsigned __int8 *)uname, len_name) )
      cout<<"You Win! You are very clever!"<<endl;
    else
      cout<<"Game Over"<<endl;
    system("pause");
  }
  else
  {
    cout<<"Game Over"<<endl;
    system("pause");
  }
*/
  return 0;
完整程序见附件。
程序跑起来很快,把多余的输出关了基本就是秒解。
接下来就是从密码解出序列号,需要写一个base64编码的规则,本来以为这是最简单的,结果因为代码太渣,脑子太卡,甚至还写出了栈溢出。。。。主要就是各种左移右移掩码,原程序汇编代码里有一个sar算术右移,被ida翻译成signed类型,其实最高位都是0没什么影响,所以在编码的时候要全部用逻辑左/右移,也就是unsigned,另外比较麻烦的就是长度不能被三整除的情况,这样最后需要特殊处理,补上’z'。

然后把生成的序列号输入原程序,竟然失败了,当时就心里一凉。幸好把序列号输入自己的程序也失败了,虽然我的程序有问题,但至少还有机会。
经过检查,发现是从提供的用户名改为“KCTF”时忘了改长度,然后就得到正确结果。


这时候已经凌晨一点多了。我记得我本来都掏出手机想打游戏的,游戏加载的时候看了看代码,结果就没能停下来,最后发现游戏都已经被杀后台了。
提交的时候发现还没人提交,心情有点激动,差点睡不着。转念一想这题难不难不知道,但是我能做出了确实是凑巧。
其实最主要的原因是花的时间多吧,一下午加一晚上,十二个小时不知道有没有,真正的大佬估计是在忙别的没这么多时间,毕竟是工作日。
本人没什么经验,第一题就做了两小时,一半在MFC打转,第二题在py虚拟机里转了两天还去搞了patch文件然后子进程调试和脱壳之类的,最后终于醒悟过来拿到py代码查出来是rsa后也分解不了。

综上所述,做这道题,靠的就是运气好,蒙对了。不过我觉得这题挺有意思的,虽然看不懂原理但是感觉很精妙。
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
KCTF2019
Please input your username: ( A to Z, a to z, 0 to 9 )
f3a25f8b29bb9cbc
Please input your KEY: ( : to z )
JXg@vwrpfoLbRcxvdYrvv[kyRSso
hashis:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 5, 6, 7, 14, 15,
 
hashis:5, 1, 6, 3, 3, 7, 7, 10, 9, 10, 7, 6, 6, 10, 11,
ind:0, pass:65
change:id14+=-5
hashis:5, 1, 6, 3, 3, 7, 7, 10, 9, 10, 7, 6, 6, 10, 6,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 10, 7, 6, 6, 10, 6,
 
ind:1, pass:-19
change:id14+=-6
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 10, 7, 6, 6, 10, 0,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 10, 0,
 
ind:2, pass:-122
change:id13+=-3
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 7, 0,
case2:
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 6, 4, 0,
 
ind:3, pass:-13
change:id12+=-1
hashis:5, 1, 6, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 4, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 4, 0,
 
ind:4, pass:-40
change:id13+=-2
hashis:5, 1, 0, 3, 3, 1, 7, 10, 9, 7, 7, 6, 5, 2, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 7, 6, 5, 2, 0,
 
ind:5, pass:-10
change:id13+=-1
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 7, 6, 5, 1, 0,
case2:
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 3, 6, 5, 1, 0,
 
ind:6, pass:-77
change:id12+=-4
hashis:5, 1, 0, 3, 3, 1, 4, 10, 9, 7, 3, 6, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 10, 9, 7, 3, 6, 1, 1, 0,
 
ind:7, pass:82
change:id11+=-2
hashis:5, 1, 0, 1, 3, 1, 4, 10, 9, 7, 3, 4, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 3, 4, 1, 1, 0,
 
ind:8, pass:104
change:id10+=-3
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 0, 4, 1, 1, 0,
case2:
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 7, 0, 1, 1, 1, 0,
 
ind:9, pass:98
change:id9+=-5
hashis:5, 1, 0, 1, 3, 1, 4, 9, 9, 2, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 3, 1, 4, 9, 9, 2, 0, 1, 1, 1, 0,
 
ind:10, pass:-98
change:id6+=-2
hashis:3, 1, 0, 1, 3, 1, 2, 9, 9, 2, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 9, 9, 2, 0, 1, 1, 1, 0,
 
ind:11, pass:-4
change:id9+=-1
hashis:3, 1, 0, 1, 0, 1, 2, 9, 9, 1, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 9, 6, 1, 0, 1, 1, 1, 0,
 
ind:12, pass:-87
change:id7+=-3
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 1, 1, 0,
case2:
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 1, 0,
 
ind:13, pass:-8
change:id13+=-1
hashis:3, 1, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 1, 2, 6, 6, 1, 0, 1, 0, 0, 0,
 
ind:14, pass:-4
change:id11+=-1
hashis:3, 0, 0, 1, 0, 1, 2, 6, 6, 1, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 0, 2, 6, 6, 1, 0, 0, 0, 0, 0,
 
ind:15, pass:-14
change:id9+=-1
hashis:3, 0, 0, 1, 0, 0, 2, 6, 6, 0, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 1, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0,
 
ind:16, pass:17
change:id8+=-2
hashis:3, 0, 0, 1, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
case2:
hashis:3, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
 
ind:17, pass:-1
change:id0+=-1
hashis:2, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0,
case2:
hashis:2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, 0, 0,
 
ind:18, pass:97
change:id8+=-3
hashis:2, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0,
case2:
hashis:0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0,
 
ind:19, pass:-103
change:id7+=-2
hashis:0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
case2:
hashis:0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
 
ind:20, pass:-11
change:id7+=-1
hashis:0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
You Win! You are very clever!
请按任意键继续. . .
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
53
54
55
56
57
58
int main()
{
  char tempcachetable[256]={0},temphashname[256]={0};
  strcpy(tempcachetable,cachetable);//保存初始表格
  strcpy(temphashname,hashname);
  char *constname="KCTF";
  len_name=4;
  strcpy(uname,constname);
  bool run=true;
  int k=0;
  while(run){
      for(int i=0;i<256;i++){
          strcpy(cachetable,tempcachetable);
          strcpy(hashname,temphashname);
          pass[k]=(char)i;
          passlen=k+1;
          if(checkpass((unsigned __int8 *)uname, len_name))
          {run=false;cout<<"success!!!"<<endl;break;}//最后一次是主动修改完成,不需要进入case2,已经直接得到了答案
          if(iscase2){
              cout<<endl<<k<<"--------------"<<i<<endl<<endl;
              iscase2=false;
              break;
          }
          if(i==255){cout<<"failed"<<endl;return 0;}
      }
      k++;
      if(k>=96){
      cout<<"failed"<<endl;
      break;
      }
      int jj;
    cout<<"pass:";
    for ( jj = 0; jj <passlen; ++jj )
      {
        cout<<(int)pass[jj]<<",  ";
      }
    cout<<"len is:"<<passlen<<endl;
  }
    char result[256]={0};//base64加密结果
    ab64e(pass,result,passlen);
    cout<<result<<endl;
      cout<<endl;
/*
  if ( getinput() )
  {
    if ( checkpass((unsigned __int8 *)uname, len_name) )
      cout<<"You Win! You are very clever!"<<endl;
    else
      cout<<"Game Over"<<endl;
    system("pause");
  }
  else
  {
    cout<<"Game Over"<<endl;
    system("pause");
  }
*/
  return 0;
工具:ida pro7.0
使用ida打开文件,可以看到函数表。其中有个名字较start的,本来想顺着这个start的调用一直跟下去找的处理逻辑,但跟了几层之后觉得有点烦,就决定先找字符串。

往上拖到开头,本来是想看下什么版本的分析之类的,结果第一个函数里就看到命令行输出的字符串,基本可以确认就是程序逻辑,于是分析调用和处理流程。

用于控制台输入输出的函数从上下文可以直接分析出来,比如这个sub_40471D显然就是输出,所以不用跟进去。
得到大致流程:
逐个字符的获取输入的用户名,序列号,以及长度,分别存入全局变量,从这里可以看出数据长度限制。

然后输入的序列号经过一个处理函数,生成真正的密码和长度。经过分析,这个处理函数将输入的四个58-122之间的字节为一组,通过位运算映射为三个字节。。。这就是一个base64算法的变形,58-122刚好也就是64个,'z'相当于标准base64的'=',这可能是我唯一熟悉的加密算法来。至少这一步可以确定是可逆的,有点感动。

接下来通过XREF找到调用getinput的地方,发现接下来过程很直接,就是将第一步得到的用户名和密码通过一个函数处理判断是否正确,因此关键的验证逻辑就是checkpass这个函数了。

逐个字符的获取输入的用户名,序列号,以及长度,分别存入全局变量,从这里可以看出数据长度限制。

然后输入的序列号经过一个处理函数,生成真正的密码和长度。经过分析,这个处理函数将输入的四个58-122之间的字节为一组,通过位运算映射为三个字节。。。这就是一个base64算法的变形,58-122刚好也就是64个,'z'相当于标准base64的'=',这可能是我唯一熟悉的加密算法来。至少这一步可以确定是可逆的,有点感动。

接下来通过XREF找到调用getinput的地方,发现接下来过程很直接,就是将第一步得到的用户名和密码通过一个函数处理判断是否正确,因此关键的验证逻辑就是checkpass这个函数了。

解题的过程中顺便看了看题主的发帖记录,找到这样一篇,也是类似的题目。
题目设计非常精巧,反正我是看不懂。并且总是强调多解就女装什么的。。。不知道是自信还是其实想女装(小声)
虽然题目肯定不一样,但是还是能得到一些启发。例如里面的描述方法对理解本题有些帮助,可以把base64得到的密码当成指令来看待。
chekpass代码特别长,毕竟是核心算法,经过分析密码和用户名的调用(这里是只读的)以及如何让函数返回正确,可以将验证逻辑大致可以解释为以下几步:
  1. 将用户名的第一位经过一系列运算得到一个初始值。
  2. 将用户名其余部分调用一个函数经过一系列运算,修改了一个长度15的数组(实际是char[256]但是只有前十五个字符在使用),暂且把这个表当成hash(就是取个名),这个表在后续步骤中使用,而用户名再也没有用过。
  3. 进入循环,每次取出密码中的一项经过一系列复杂操作,分别取高位和低位经过几个循环迭代,得到结果,过程中用到并改变的1中生成的初始值(当成译码看待),最后用这个密码字符得到的指令,修改了hash表中的指定一项(这里里我称为主动修改)。          (这一步里有一个校验指令的过程,可能导致循环退出,具体条件太复杂我也看不懂。只能知道不能走到这个条件)因为代码译码的过程用的变量太多,难以一一分析,所以我暂时没有先研究具体过程,只看这整个步骤对结果的影响。
  4. 取出了密码中的该项和下一项,经过运算,修改了3中译码操作需要用到的一些值,再加上3中也修改了部分变量,也就是改变了下一次译码的规则,这就是说我们不能从数据到指令建立一个一一映射的关系,因此很难倒推回去。
  5. 从hash表中取出所有数据,经过一系列操作作为数组下标,几次迭代,最后从一个大的表中取出一个数据,根据这个数据来进入一个判断,也就是步骤6。
  6. case1:从另一个表中取出数据修改hash,并且是把hash中的每一项都改变,因此既有可能产生0,也有可能把之前已经消为0的值变为非0,造成破坏性后果,具体会发生什么很难分析;case2,有一个指针指向hash中的数据,每次进入这个分支会改变hash中的一个数据,并且重要的是,根据判断条件,只会改变>=1的一个数据(写的时候才突然意识到这一点),而且这里指针指向采用+4%15的方法,可以遍历整个hash表。这一步我称之为被动修改。

[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2019-12-6 19:28 被mb_ibocelll编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
赞赏记录
参与人
雪币
留言
时间
PLEBFE
为你点赞~
2023-1-20 02:59
最新回复 (6)
雪    币: 55923
活跃值: (21565)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
建议写详细些
2019-12-6 14:13
0
雪    币: 3239
活跃值: (445)
能力值: ( LV6,RANK:151 )
在线值:
发帖
回帖
粉丝
3
我尽力了,再多已经写不出来了,最多贴点图
2019-12-6 18:56
0
雪    币: 11705
活跃值: (975)
能力值: ( LV12,RANK:779 )
在线值:
发帖
回帖
粉丝
4
大众队黑马车手,太强了
2019-12-8 12:26
0
雪    币: 3239
活跃值: (445)
能力值: ( LV6,RANK:151 )
在线值:
发帖
回帖
粉丝
5
大佬别说了,我害怕[瑟瑟发抖]
2019-12-8 12:58
0
雪    币: 3239
活跃值: (445)
能力值: ( LV6,RANK:151 )
在线值:
发帖
回帖
粉丝
6
我错了,这里不该用strcpy的,要是中间有个0就完了
2019-12-8 12:59
0
雪    币: 200
活跃值: (87)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
牛皮哦
2019-12-9 11:20
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册