首页
社区
课程
招聘
[原创] 看雪 2022·KCTF 春季赛 - 第二题 末日邀请 by 心学
2022-5-13 21:39 8870

[原创] 看雪 2022·KCTF 春季赛 - 第二题 末日邀请 by 心学

htg 活跃值
4
2022-5-13 21:39
8870

'''
日期:2022-05-13
CTF:htg
题目:看雪 2022·KCTF 春季赛 > 第二题 末日邀请

'''
**##工具:IDA

 

##分析步骤:

 

##先简略看看程序运行情况**

##【1、找关键代码】

'''
【方法一】
查阅字符串方法,View->Strings,搜索
【方法二】
找 main 方法:
_main
按F5即可转换伪代码
'''

##【2、显示中文字符串】

'''
Options->General->Strings:Default8 8 bits :修改为GBK
如果没有 GBK ,那么点开之后,按住 insert 即可弹出一个新的对话框,直接输入 GBK
设置完成之后,在 View->Strings 里 右键 Rebuild 对字符串进行重新搜索建立
如果还是没有,那么关闭IDA,再打开IDA,重新来一遍
'''

##【3、分析主函数结构】

'''

1
2
3
4
5
6
7
sub_EB100C(
    "%s\n 而你,作为一个操控韩立的人,千万不要让韩立 GAME OVER 了.\n现在,输入你的操作ID吧:",
    asc_EB55C0);
  sub_EB103A("%s", Arglist);【获取用户输入,此处经过测算,长度<= 0x1E 30 位,查看局部变量表】
  sub_EB100C("\n现在,你就是韩立,韩立就是你,如遇绝境,吼:男人至死是少年!");
  v41 = 0;
  lenSN = strlen(Arglist);

//////////////////////////////////////
【SN 构造 一个 resultA (一个字节 byte)】

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
resultA = 0;
 if ( lenSN )
 {
   v7 = Arglist;
   do
   {
     v5 ^= *v7;
     --v6;
     ++v7;
     v8 = 8;
     do
     {
       v9 = 2 * v5;
       v10 = v9 ^ 7;
       if ( v9 >= 0 )
         v10 = v9;
       v5 = v10;
       --v8;
     }
     while ( v8 );
   }
   while ( v6 );
   lenSN = lenSNcopy;
   resultA = v10;                              // resultA是输出的结果
 }

//////////////////////////////////////

1
initArrayA();                                 // 构造:dword_EB5B20 数组。这个是全局变量,与用户输入无关

//////////////////////////////////////
【用SN及arrayA:生成一个 signA(一个双字 DWORD),必须等于 0xF52E0765】

1
2
3
4
v11 = -1;
 for ( i = 0; i < lenSN; ++i )
   v11 = arrayA[(unsigned __int8)(v11 ^ Arglist[i])] ^ (v11 >> 8);// 0x0AD1F89A
 signA = ~v11;                                 // signA == 0xF52E0765

//////////////////////////////////////
【这个之后,Arglist 也就是 SN 是已经处理过了】

1
stringToInt((int)Arglist, lenSN);             // 对用户输入的字符串进行转换:将字符转换成数值,'0123456789ABCDEF'-->0123456789ABCDEF

//////////////////////////////////////
【resultA 构造 一个 arrayB】
【取出 arrayB 里三个字节进行或运算 得到 signB】
【取出 Arglist 里前三个字节进行 异或运算,得到 的结果必须等于 signB】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
v13 = resultA;
 v36 = 1;
 v35 = resultA + 1;
 v14 = v35;
 do
 {
   v15 = v13;
   for ( j = 1; j < 0xC8; ++j )
   {
     if ( (v15 & 1) != 0 )
       v15 = 3 * v15 + 1;
     else
       v15 >>= 1;
     arrayB[j] = v15;
   }
   ++v13;
 }
 while ( v13 < v14 );                          // 构造了一个大表
 signB = arrayB[0xC6] | arrayB[0xC5] | arrayB[0xC4];// 1^2^4=7
 lenSNcopy2 = lenSNcopy;
 if ( signB != (Arglist[2] ^ Arglist[1] ^ Arglist[0]) )// 第一个判断:前三位异或之后结果为7

//////////////////////////////////////
【计算中间字符数量(其实是尾部),他是 signB + 2】【剩余数量为0,题目挖了一个坑】

1
2
midBytes = signB + 2;
restBytes = lenSNcopy2 - midBytes - 7;

//////////////////////////////////////
【四个固定字符:KCTF】【注意此时的字符,减去了 0x37 】

1
2
3
4
if ( Arglist[3] != 0x14 )
if ( Arglist[4] != 0xC )
if ( Arglist[5] != 0x1D )
if ( Arglist[6] != 0xF )

//////////////////////////////////////
【按位取出字符,必须被当前的数字位数进行整除,比如 123 被 3 整除,12345 被 5 整除】
【具体多少位数,看后面】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
v21 = 0;
 lenSNcopy = 0;
 if ( midBytes > 0 )
 {
   v22 = 1;
   do
   {
     v23 = Arglist[v22 + 6] + 10 * v37;        // 十进制算法。十进制数值字符串转换成十进制数值??
     v24 = v23 - 0x37373737;                   // 7777
     if ( v23 <= 0x4B435445 )                  // KCTE
       v24 = v23;
     v37 = v24;
     if ( v24 % v36 )                          // 不能跳进去:需要被整除。
       goto LABEL_50;
     v21 = lenSNcopy + 1;
     v22 = v36 + 1;
     lenSNcopy = v21;
     ++v36;
   }
   while ( v21 < midBytes );
 }

//////////////////////////////////////
【对刚才的判断的字符串,进行升序排序】
【排序结果有参考或要求么?看后面】

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
v25 = midBytes - 1;
if ( midBytes - 1 > 0 )                       // 冒泡排序算法:Arglist【7:7+midBytes-1】即对从7开始的 midBytes 个数进行排序
{
  v26 = midBytes - 1;
  do
  {
    v27 = 0;
    if ( v25 > 0 )
    {
      do
      {
        v28 = Arglist[v27 + 7];
        v29 = Arglist[v27 + 8];
        if ( v28 > v29 )
        {
          Arglist[v27 + 7] = v29;
          Arglist[v27 + 8] = v28;
        }
        ++v27;
      }
      while ( v27 < v25 );
      v3 = 0;
    }
    --v25;
    --v26;
  }
  while ( v26 );
}

//////////////////////////////////////
【对全局常量进行转换,结果就是 1234567890,后面就不用看了,具体原因看后面】

1
stringToInt((int)a1234567890Abcd, midBytes);  // 1234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ转换成1234567890,0x28,abcdef,0x16,0x17,0x18,......,0x23

//////////////////////////////////////
【这是关键的判断,刚才一大堆的针对中间部分字符串的处理】
【要保证排序之后的结果与 1234567890 相同】
【那么最长只有 123456789 ,最短是1】
【先分析123456789:排序之前,要保证数值的位数 整除 数值,这就要进行爆破分析,详见后面】

1
2
3
4
5
6
7
8
9
10
v30 = 0;
 if ( midBytes > 0 )
 {
   while ( a1234567890Abcd[v30] == Arglist[v30 + 7] )// 第六个判断:必须满足
   {
     if ( ++v30 >= midBytes )                  // 必须跳出去:重复次数是 midBytes
       goto LABEL_41;
   }
   goto LABEL_49;
 }

//////////////////////////////////////
【这个一个坑,对后面的字符进行处理(如有),这个方法换算也没搞明白sub_EB10E1,感觉就是随便一个算法】

1
2
3
Arglist7laterTrans = &Arglist[midBytes + 7];
sub_EB10E1((int)Arglist7laterTrans, restBytes);
if ( restBytes <= 0 )                         // 第七个判断

//////////////////////////////////////
【这个就有迷惑性了】
【第一感觉就是,restBytes>0,执行else部分,然后再跳到LABEL_45】【好多人掉进去了】
【其实:restBytes=0,不用执行else部分,直接进行signA == 0xF52E0765 】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ( restBytes <= 0 )                         // 第七个判断
  {
LABEL_45:
    sub_EB100C("\n你想吃猪肉,到猪洞七层准备打个白野猪.白野猪死后尸体尽然掉出一把 屠龙 .");
    if ( signA == 0xF52E0765 )                  // 第八个判断
   ……………………
   }
   else
  {                                             // 这是一个大坑,没必要进入
    while ( ((unsigned __int8)asc_EB55C0[v3] ^ (unsigned __int8)Arglist7laterTrans[v3]) == asc_EB3DC8[v3] )
    {
      if ( ++v3 >= restBytes )
        goto LABEL_45;
        ………………
    }
  }

//////////////////////////////////////
'''

##【4、编写Python代码】

'''
详见下面内容
'''

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
import os
 
#从IDA里拷贝出来【如有时间,再考虑具体算法】
#初始化大数组
def InitArrayA():
    #先直接从 IDA 里拷贝。
    tmpArray = [ 0x00000000,0x09073096,0x120E612C,0x1B0951BA,0xFF6DC419,0xF66AF48F,0xED63A535,0xE46495A3,0xFEDB8832,0xF7DCB8A4,0xECD5E91E,0xE5D2D988,0x01B64C2B,0x08B17CBD,0x13B82D07,0x1ABF1D91,0xFDB71064,0xF4B020F2,0xEFB97148,0xE6BE41DE,0x02DAD47D,0x0BDDE4EB,0x10D4B551,0x19D385C7,0x036C9856,0x0A6BA8C0,0x1162F97A,0x1865C9EC,0xFC015C4F,0xF5066CD9,0xEE0F3D63,0xE7080DF5,0xFB6E20C8,0xF269105E,0xE96041E4,0xE0677172,0x0403E4D1,0x0D04D447,0x160D85FD,0x1F0AB56B,0x05B5A8FA,0x0CB2986C,0x17BBC9D6,0x1EBCF940,0xFAD86CE3,0xF3DF5C75,0xE8D60DCF,0xE1D13D59,0x06D930AC,0x0FDE003A,0x14D75180,0x1DD06116,0xF9B4F4B5,0xF0B3C423,0xEBBA9599,0xE2BDA50F,0xF802B89E,0xF1058808,0xEA0CD9B2,0xE30BE924,0x076F7C87,0x0E684C11,0x15611DAB,0x1C662D3D,0xF6DC4190,0xFFDB7106,0xE4D220BC,0xEDD5102A,0x09B18589,0x00B6B51F,0x1BBFE4A5,0x12B8D433,0x0807C9A2,0x0100F934,0x1A09A88E,0x130E9818,0xF76A0DBB,0xFE6D3D2D,0xE5646C97,0xEC635C01,0x0B6B51F4,0x026C6162,0x196530D8,0x1062004E,0xF40695ED,0xFD01A57B,0xE608F4C1,0xEF0FC457,0xF5B0D9C6,0xFCB7E950,0xE7BEB8EA,0xEEB9887C,0x0ADD1DDF,0x03DA2D49,0x18D37CF3,0x11D44C65,0x0DB26158,0x04B551CE,0x1FBC0074,0x16BB30E2,0xF2DFA541,0xFBD895D7,0xE0D1C46D,0xE9D6F4FB,0xF369E96A,0xFA6ED9FC,0xE1678846,0xE860B8D0,0x0C042D73,0x05031DE5,0x1E0A4C5F,0x170D7CC9,0xF005713C,0xF90241AA,0xE20B1010,0xEB0C2086,0x0F68B525,0x066F85B3,0x1D66D409,0x1461E49F,0x0EDEF90E,0x07D9C998,0x1CD09822,0x15D7A8B4,0xF1B33D17,0xF8B40D81,0xE3BD5C3B,0xEABA6CAD,0xEDB88320,0xE4BFB3B6,0xFFB6E20C,0xF6B1D29A,0x12D54739,0x1BD277AF,0x00DB2615,0x09DC1683,0x13630B12,0x1A643B84,0x016D6A3E,0x086A5AA8,0xEC0ECF0B,0xE509FF9D,0xFE00AE27,0xF7079EB1,0x100F9344,0x1908A3D2,0x0201F268,0x0B06C2FE,0xEF62575D,0xE66567CB,0xFD6C3671,0xF46B06E7,0xEED41B76,0xE7D32BE0,0xFCDA7A5A,0xF5DD4ACC,0x11B9DF6F,0x18BEEFF9,0x03B7BE43,0x0AB08ED5,0x16D6A3E8,0x1FD1937E,0x04D8C2C4,0x0DDFF252,0xE9BB67F1,0xE0BC5767,0xFBB506DD,0xF2B2364B,0xE80D2BDA,0xE10A1B4C,0xFA034AF6,0xF3047A60,0x1760EFC3,0x1E67DF55,0x056E8EEF,0x0C69BE79,0xEB61B38C,0xE266831A,0xF96FD2A0,0xF068E236,0x140C7795,0x1D0B4703,0x060216B9,0x0F05262F,0x15BA3BBE,0x1CBD0B28,0x07B45A92,0x0EB36A04,0xEAD7FFA7,0xE3D0CF31,0xF8D99E8B,0xF1DEAE1D,0x1B64C2B0,0x1263F226,0x096AA39C,0x006D930A,0xE40906A9,0xED0E363F,0xF6076785,0xFF005713,0xE5BF4A82,0xECB87A14,0xF7B12BAE,0xFEB61B38,0x1AD28E9B,0x13D5BE0D,0x08DCEFB7,0x01DBDF21,0xE6D3D2D4,0xEFD4E242,0xF4DDB3F8,0xFDDA836E,0x19BE16CD,0x10B9265B,0x0BB077E1,0x02B74777,0x18085AE6,0x110F6A70,0x0A063BCA,0x03010B5C,0xE7659EFF,0xEE62AE69,0xF56BFFD3,0xFC6CCF45,0xE00AE278,0xE90DD2EE,0xF2048354,0xFB03B3C2,0x1F672661,0x166016F7,0x0D69474D,0x046E77DB,0x1ED16A4A,0x17D65ADC,0x0CDF0B66,0x05D83BF0,0xE1BCAE53,0xE8BB9EC5,0xF3B2CF7F,0xFAB5FFE9,0x1DBDF21C,0x14BAC28A,0x0FB39330,0x06B4A3A6,0xE2D03605,0xEBD70693,0xF0DE5729,0xF9D967BF,0xE3667A2E,0xEA614AB8,0xF1681B02,0xF86F2B94,0x1C0BBE37,0x150C8EA1,0x0E05DF1B,0x0702EF8D ]
    return tmpArray
#根据输入的SN,获取resultA
def CalcResultA(SN):
    initByte = 0x00
    tmpByteA = 0x00
    tmpByteB = 0x00
    for ch in SN:
        #print("ch:{}".format(ch))
        initByte ^= ord(ch)
        for i in range(8):
            tmpByteA = (2 * initByte) & 0xFF
            #print("tmpByteA:{}".format(hex(tmpByteA)[2:]))
            tmpByteB = tmpByteA ^ 7
            if tmpByteA < 0x80 :   #对应于 正数
                tmpByteB = tmpByteA
            initByte = tmpByteB
        #print("tmpByteB:{}".format(hex(tmpByteB)[2:]))
    return tmpByteB
 
#对输入的字符进行处理:'0'->0   '9'--->9   'a'---->10 ....
def String2IntList(SN):
    returnList =[]
    for ch in SN:
        tmpBase = 0x30
        if ord(ch)>=0x3A:
            tmpBase = 0x37
        returnList.append(ord(ch)-tmpBase)
    return returnList
 
#搜索函数
def DoSearch(SN):
    global arrayA
    #print("当前的SN:{}\n 长度:{}".format(SN,len(SN)))
    #os.system("pause")
    #TODO:已经构造完成:开始进行判断
    resultA = CalcResultA(SN)
    #print("resultA:{}".format(hex(resultA)))
    signA = 0xFFFFFFFF
    lenSN = len(SN)
    for ch in SN:
        tmpIndex = (signA ^ ord(ch)) & 0xFF
        tmpValue = arrayA[tmpIndex]
        #print("tmpIndex:{}".format(hex(tmpIndex)))
        #SAR算数移位
        if signA >= 0x80000000:
            #负数,右移之后,要在最高位带上FF
            signA = ((signA >> 8)|0xFF000000) ^ tmpValue
        else:
            signA = (signA >> 8) ^ tmpValue
        #print("signA:{}".format(hex(signA)))
    signA = signA ^ 0xFFFFFFFF #取反 就是 异或 全1
 
    if signA != 0xF52E0765:
        #print("signA: {} != 0xF52E0765".format(hex(signA)))
        #os.system("pause")
        return None
 
    #输出一个结果:  
    print("找到SN:{}".format(SN))
    os.system("pause")
 
#主函数入口
def main():
    #序列号
    SN = ""
    #分析算法结构
    '''
    01  3个字符
    02  4个固定字符,即为 KCTF
    03  不大于9个字符,且必须为数值1-9
    04  没有字符,题目埋了一个坑
    '''
    #第3部分:9个字符。
    part3Code = "381654729"
    for a in usedList:
        for b in usedList:
            for c in usedList:
                SN = chr(a) + chr(b) + chr(c)
                #对SN:前3个字符进行计算,以确保其相互异或,结果为 7
                tmpIntLst = String2IntList(SN)
                if tmpIntLst[0] ^ tmpIntLst[1] ^ tmpIntLst[2] != 0x7:
                    continue
                #print("找到SN:{}".format(SN))
                #前单个字符要进行sign换算
                SN = SN +'KCTF' + part3Code
                #调用另外一个命令,继续构造
                DoSearch(SN)
 
if __name__ == "__main__":
    global arrayA
    arrayA=[]
    usedList = list(range(0x21,0x7E,1))
    print("usedCharList:\n{}".format([hex(x)[2:].upper() for x in usedList]))
    arrayA = InitArrayA()
    #main()
    '''
    以下是辅助代码
    constStr = '1234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    constIntList = String2IntList(constStr)
    print("constIntList")
    print(','.join(hex(x)[2:] for x in constIntList))
 
    print('测试最后的部分')
    asc_EB55C0 = '我,韩立,作为一名光荣的程序员,月亮不睡我不睡,太阳没起我就起,夜以继日,终于把肉身修炼腐朽了...然后,穿越了...'
    asc_EB3DC8 = '我辈读书人一生所求不过四事:为天地立心,为生民立命,为往圣继绝学,为万世开太平.'
 
    asc_EB55C0_to_gbk = asc_EB55C0.encode("gbk")
    asc_EB3DC8_to_gbk = asc_EB3DC8.encode("gbk")
 
    returnRestList = []
    for i in range(20):
        returnRestList.append(asc_EB55C0_to_gbk[i]^asc_EB3DC8_to_gbk[i])
    print("returnRestList")
    print(','.join(hex(x)[2:] for x in returnRestList))
    #0,0,9d,8,1d,0,68,c5,1f,3c,1c,11,1b,41,8,2,7e,11,7a,62
    '''

##找9位数值代码

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
###找到满足要求的数值:9
import itertools
from itertools import permutations
'''
num_list = [1,2,3,4,5,6,7,8,9]
num_list = [1,2,3,4,5,6,7,8]
num_list = [1,2,3,4,5,6,7] #none
num_list = [1,2,3,4,5,6]
num_list = [1,2,3,4,5] #none
num_list = [1,2,3,4]#none
num_list = [1,2,3]
num_list = [1,2]
num_list = [1]
'''
def permute(nums):
    result = []
    for i in permutations(nums,len(nums)):
        #print(list(i))
        tmp = "".join(str(x) for x in list(i))
        #print("tmp:{}".format(tmp))
        result.append(tmp)
 
    return result
 
 
def main():
    #print('\n')
    #print(permute(num_list))
    for i in range(9):
        num_list = []
        ##生成 num_list
        for j in range(i+1):
            num_list.append(j+1)
        print("#"*50+"\n"+"num_list:{}".format(num_list))
        ##num_list 生成 permute(num_list) 用作候选       
        lst = permute(num_list)
        ##开始查找
        cur = len(num_list)
        for k in lst:
            for m in range(cur):
                n = m + 1
                x = int(k[:n])
                if x % n != 0:
                    break
                if n == cur:
                    print("found:\t{}".format(k))
 
if __name__ == "__main__":
    print("开始查找")
    main()   
    print("#"*50)
'''
found:381654729
found:38165472
found:123654
found:321654
found:123
found:321
found:12
found:1
'''

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2022-5-14 11:54 被htg编辑 ,原因: 修改部分笔误
上传的附件:
收藏
点赞5
打赏
分享
最新回复 (3)
雪    币: 5568
活跃值: (2936)
能力值: ( LV12,RANK:394 )
在线值:
发帖
回帖
粉丝
htg 4 2022-5-14 00:23
2
0

视频链接:https://pan.baidu.com/s/1gfP597Bbw8y6HujTEMLqlQ 
提取码:t352


最后于 2022-5-14 15:07 被kanxue编辑 ,原因: 放上B站视频
雪    币: 16429
活跃值: (59390)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2022-5-14 09:19
3
0
htg 视频链接:https://pan.baidu.com/s/1gfP597Bbw8y6HujTEMLqlQ  提取码:t352
感谢视频分析,这视频我上传到B站看雪学苑号分享给大家了
雪    币: 5568
活跃值: (2936)
能力值: ( LV12,RANK:394 )
在线值:
发帖
回帖
粉丝
htg 4 2022-5-14 09:38
4
0
Editor 感谢视频分析,这视频我上传到B站看雪学苑号分享给大家了
谢谢
游客
登录 | 注册 方可回帖
返回