首页
论坛
课程
招聘
[原创]看雪 2022·KCTF 秋季赛 > 第六题 病疫先兆 by 心学
2022-11-28 23:05 8577

[原创]看雪 2022·KCTF 秋季赛 > 第六题 病疫先兆 by 心学

htg 活跃值
4
2022-11-28 23:05
8577

工具:IDA、Python

一、观察程序运行结果

1
2
3
4
5
6
7
C:\Users\surface>C:\Users\surface\OneDrive\Crack\CTF\Kanxue2022KCTFAutumn\06\CrackMe\CrackMe.exe
please input :
0123456789
error
请按任意键继续. . .
 
C:\Users\surface>

代码错误时:输出error

二、IDA反编译伪代码

主程序

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int preValue; // eax
  unsigned int preValueCopy; // ebx
  int sufValue; // eax
  unsigned int sufValueCopy; // edi
  int *v7; // esi
  int *v8; // esi
  char inputSN[128]; // [esp+4h] [ebp-A0h] BYREF
  char sufStr[8]; // [esp+84h] [ebp-20h] BYREF
  char preStr[8]; // [esp+8Ch] [ebp-18h] BYREF
  int v13; // [esp+94h] [ebp-10h]
  int v14; // [esp+98h] [ebp-Ch]
  int v15; // [esp+9Ch] [ebp-8h]
 
  memset(inputSN, 0, sizeof(inputSN));
  printf("please input :\n");
  scanf_s("%s", inputSN);
  if ( sub_B91000(inputSN) != 0xE )
    goto LABEL_19;
  preStr[5] = 0;                                // 字符串截断符:只允许5个字节
  *(_DWORD *)preStr = *(_DWORD *)inputSN;
  preStr[4] = inputSN[4];
  preValue = atoi(preStr);
  v15 = *(_DWORD *)&inputSN[5];
  sufStr[5] = 0;                                // 字符串截断符:只允许5个字节
  *(_DWORD *)sufStr = *(_DWORD *)&inputSN[9];
  preValueCopy = preValue;
  sufStr[4] = inputSN[0xD];
  sufValue = atoi(sufStr);
  v13 = 0;
  sufValueCopy = sufValue;
  v14 = 1;                                      // 需保证为1
  srand(preValueCopy);
  v7 = dword_B9F000;
  while ( rand() == *v7 )                       // 依次获取的随机值需与内置全局数组相等
  {
    if ( (int)++v7 >= (int)dword_B9F050 )
      goto LABEL_7;                             // 要跳出来。避开 v14=0
  }
  v14 = 0;                                      // 执行了这一步就错
LABEL_7:
  srand(sufValueCopy);
  v8 = dword_B9F050;
  while ( rand() == *v8 )                       // 依次获取的随机值需与内置全局数组相等
  {
    if ( (int)++v8 >= (int)&dword_B9F0A0 )
      goto LABEL_12;                            // 要跳出来。避开 v14=0
  }
  v14 = 0;                                      // 执行了这一步就错
LABEL_12:
  if ( (_BYTE)v15 == 'K' && *(_WORD *)((char *)&v15 + 1) == 'TC' && HIBYTE(v15) == 'F' )// KCTF
    v13 = 1;
  if ( v14 && v13 )
  {
    printf("success : %s\n", inputSN);
    system("pause");
  }
  else
  {
LABEL_19:
    printf("error\n");
    system("pause");
  }
  return 0;
}

初始化随机种子

1
2
3
4
void __cdecl srand(unsigned int Seed)
{
  *(_DWORD *)(_getptd() + 0x14) = Seed;
}

随机函数

1
2
3
4
5
6
7
8
9
10
int __cdecl rand()
{
  int v0; // ecx
  unsigned int v1; // eax
 
  v0 = _getptd();
  v1 = 0x343FD * *(_DWORD *)(v0 + 0x14) + 0x269EC3;
  *(_DWORD *)(v0 + 0x14) = v1;
  return HIWORD(v1) & 0x7FFF;
}

程序整理的逻辑结构清晰,最终通过 if ( v14 && v13 ) 之后,才判断正确

三、整理加密逻辑

1、获取用户字符串

2、检查字符串长度:0xE = 14

1
if ( sub_B91000(inputSN) != 0xE )

3、将字符串分为三部分

第一部分:前5个字符,转为整数

1
2
3
4
preStr[5] = 0;                                // 字符串截断符:只允许5个字节
*(_DWORD *)preStr = *(_DWORD *)inputSN;
preStr[4] = inputSN[4];
preValue = atoi(preStr);

第二部分:KCTF

1
v15 = *(_DWORD *)&inputSN[5];

第三部分:后5个字符,转为整数

1
2
3
4
5
6
v15 = *(_DWORD *)&inputSN[5];
sufStr[5] = 0;                                // 字符串截断符:只允许5个字节
*(_DWORD *)sufStr = *(_DWORD *)&inputSN[9];
preValueCopy = preValue;
sufStr[4] = inputSN[0xD];
sufValue = atoi(sufStr);

4、校验

第一部分:用转换后的整数初始化种子,依次获取20个数,与内置全局数组相等

1
2
3
4
5
6
7
8
srand(preValueCopy);
v7 = dword_B9F000;
while ( rand() == *v7 )                       // 依次获取的随机值需与内置全局数组相等
{
  if ( (int)++v7 >= (int)dword_B9F050 )
    goto LABEL_7;                             // 要跳出来。避开 v14=0
}
v14 = 0;                                      // 执行了这一步就错

第二部分:KCTF

1
if ( (_BYTE)v15 == 'K' && *(_WORD *)((char *)&v15 + 1) == 'TC' && HIBYTE(v15) == 'F' )// KCTF

第三部分:用转换后的整数初始化种子,依次获取20个数,与内置全局数组相等

1
2
3
4
5
6
7
8
srand(sufValueCopy);
v8 = dword_B9F050;
while ( rand() == *v8 )                       // 依次获取的随机值需与内置全局数组相等
{
  if ( (int)++v8 >= (int)&dword_B9F0A0 )
    goto LABEL_12;                            // 要跳出来。避开 v14=0
}
v14 = 0;                                      // 执行了这一步就错

四、编写破解代码

构造序列号为:XXXXXKCTFYYYYY
直接采取爆破的方式,分别获取前后的5个数字字符。
5位数值依次传给种子,逐个生成,并与内置数值比较,成功即记录并退出循环。

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
'''
日期:2022-11-28
作者:htg.心学
'''
###########################################################
seed = 0
###初始化种子
def InitSand(sd):
    global seed
    seed = sd
###获取随机值
def GetSand():
    global seed
    seed = seed * 0x343FD
    seed = seed & 0xFFFFFFFF
    seed = seed + 0x269EC3
    seed = seed & 0xFFFFFFFF
    returnValue = seed >> 0x10
    returnValue = returnValue & 0x7FFF
    return returnValue
###两个列表
preList = [0x00003BFC,0x00002173,0x000025BB,0x0000380B,0x00002C13,0x000075BE,0x00007366,0x000046A3,0x000013C1,0x0000159B,0x00005B5F,0x0000534F,0x00004E37,0x00003A04,0x00001301,0x00005D0C,0x00004155,0x000048E9,0x000061D2,0x00006158]
sufList = [0x00002BB6,0x00006B5A,0x000003D4,0x0000152B,0x00006E04,0x0000254C,0x000040AE,0x000056CA,0x000017E1,0x000055C7,0x00003641,0x00002D3C,0x00000A41,0x00004BC5,0x00006233,0x00001FE7,0x00006E05,0x00000F6E,0x00006398,0x00006AD7]
###########################################################
######三部分:XXXXXKCTFYYYYY
##第一部分:XXXXX
##第二部分:KCTF
##第三部分:YYYYY
###########################################################
preFound = False
sufFound = False
print("寻找第一部分......")
for i in range(100000):
    InitSand(i)
    preFound = True
    for a in preList:
        if GetSand() != a:
            preFound = False
    if preFound:
        print("\t找到第一部分:{:05}".format(i))
        break
if not preFound:
    print("Failed!")
###########################################################
print("寻找第三部分......")
for j in range(100000):
    InitSand(j)
    sufFound = True
    for a in sufList:
        if GetSand() != a:
            sufFound = False
    if sufFound:
        print("\t找到第三部分:{:05}".format(j))
        break
if not sufFound:
    print("Failed!")
 
SN = "{:05}KCTF{:05}".format(i,j)
print("序列号:{}".format(SN))
###########################################################

[2023春季班]《安卓高级研修班(网课)》月薪两万班招生中~

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回