吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 22579|回复: 54
收起左侧

[CTF] 详细分析看雪2016CTF第一题Crack_ME用IDA+X64DBG追码

  [复制链接]
pk8900 发表于 2018-6-21 17:48
本帖最后由 pk8900 于 2018-6-25 23:17 编辑

前言


        很长时间没有发帖子了,这段时间一直在饿补,随着逆向学习深入,发现自己要学的东西太多了,许多东西过去都是只听说过,跟本没学习过,于是看网上的各种教程,并寻找用来练手的东西,发现看雪CTF里有不少CRACKME,于是拿来分析研究。
        这个CRACKME的作者叫[凉飕飕],CTF要求是追码,整个追码过程断续的进行2天时间,然后写的这个帖子,尽量还原原始分析步骤,如果有遗漏和不对的地方,欢迎大家请跟贴讨论。
       此贴并非技术贴,大牛可以一带而过,和我一样的新手也许可以从中学习到一些方法和经验。

下载地址


        CRACKME原帖及下载地址:https://ctf.pediy.com/game-fight-2.htm

crack简介


        PEid显示,Microsoft Visual C++ 8 *,图标是个MFC图标,估计是VC++的程序错不了。界面如下:
       Image 001.png
        输入任意注册码,点击OK,有提示信息框:something you lost!,并且每尝试一次,左边还有一个文本框递增次数。

使用工具


       IDA7.0,X64DBG,辅助工具:PEid,慧星小助手(查看控件ID,本例中:密码框为1002,次数框为1000)

摸清流程


       既然程序有提示字符串,那就从字符串下手。X64DBG载入,运行后程序自动退出,有反调试。
       打开IDA进行静态分析,查找字符串,找到结果如下:
      .text:00402423        sub_402120        push    offset Text     ; "something you lost!"
      转到sub_402120调用处,此处IDA代码如下:
      
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
      BeginPaint(hWnd, &Paint);        EndPaint(hWnd, &Paint);
      return 0;
  }
  return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
if ( (unsigned __int16)wParam > 0x40Au )
{
  switch ( (unsigned __int16)wParam )
  {
    case 0x40Bu:                              // 注册成功条件:wParam==0x40B
      *(_OWORD *)v20 = xmmword_41DB98;
      v23 = 0;
      v21 = xmmword_41DBA8;
      v22 = xmmword_41DBB8;
      memset(&v24, 0, 0x96u);
      MessageBoxW(hWnd, v20, L"Successed", 0);// 成功位置
      return 0;
    case 0x40Cu:
      sub_402970(lParam, &v19);
      v9 = operator new[](2 * (v19 + 1));
      sub_402870(v9, lParam);
      sub_4029B0(v9);
      sub_402A00(v9);
      memset(v20, 0, 0xC8u);
      sub_402870(v20, v9);
      j_j_j___free_base(v9);
      v10 = 0;
      if...
      if ( sub_402810(lParam, v20) )
        SendMessageW(hWnd, 0x111u, 0x40Au, 0);
      return 0;
    case 0x40Fu:
      MessageBoxW(0, L"something you lost!", L"Failed", 0);// 失败处
      return 0;
  }
  return DefWindowProcW(hWnd, 0x111u, wParam, lParam);
}
if ( (unsigned __int16)wParam == 0x40A )
{
  *(_OWORD *)Text = xmmword_41DB60;
  v26 = xmmword_41DB70;
  v27 = xmmword_41DB80;
  v28 = qword_41DB90;
  memset(&v29, 0, 0x2Cu);
  MessageBoxW(hWnd, Text, L"Failed", 0);      // 失败处

在失败对话框上方,有明显"Successed"成功字样,分析得出成功的条件就是:wParam==0x40B,再往上可以看到:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
switch ( Msg )
    {
      case 0x113u:
v13 = operator new(0x14u);
        v40 = 0;
        *(_OWORD *)v39 = 0i64;
        v14 = GetDlgItem(hWnd, 1000);
        GetDlgItemTextW(v14, 1000, v39, 10);
        v19 = -858993460;
        sub_4027C0(v39, (const char *)L"%d", &v19);
        *v13 = v19;
        v13[1] = hWnd;
        v13[3] = GetDlgItem(hWnd, 1002);
        v13[2] = GetDlgItem(hWnd, 1000);
        v13[4] = v13;
“.............................”
while ( *((_WORD *)&v32 + v15) );
        if ( OpenEventW(0x1F0003u, 0, L"Cracker") )
          CreateThread(0, 0, StartAddress, v13, 0, 0);
        return 0;
“.............................”
return DefWindowProcW(hWnd, Msg, wParam, lParam);

case 0x113u,百度查询,0x113为时间事件,也就是程序里有一个时钟事件,用做检测,事件中包含"Cracker",“Failed”等可疑字串,未发现成功字串,只有一个可疑的CreateThread(0, 0, StartAddress, v13, 0, 0);函数,创建新线程。
(此期间尝试跟踪此时间事件,结果一点进展都没有,关于程序反调试,我采用的附加的方法轻松绕过,反调试部分放在最后分析。)
绕了半天的弯路后,又回到这里,决定从成功注册的条件:wParam==0x40B入手,IDA查的常量:0x40B(IDA操作为查找立即数),果然找到了关键位置,sub_401870,右键对函数重命名为:check_right_401870(命名方便在函数列表中查找,保留地址是方便X64DBG中进行调试定位),在这个子程序中调用了 return PostMessageW((HWND)struct_count_list->win_handle, 0x111u, 0x40Bu, 0);,这样wParam就被赋值0x40B,就可以出现成功的提示了。(此期间尝试在X64DBG中对基址+1870地址下断,根本断不下来,说明验证过程中没有调用这里)
继续IDA中静态分析,查找check_right_401870函数调用关系,(IDA操作:鼠标定位于check_right_401870函数中,右键“邻近浏览器”),在邻近浏览器图表视图中通过双击父结点,及隐藏非重要节点,得出如下流程图:
Image 002.png
成功只有一个路线,IDA的这个功能直的很有用,如果想爆破实现的话,那就可以按图索骥了。
3.

逐段分析


这个过程是漫长的,也可以说是经验积累的过程,大牛通常能一眼看穿的代码,而新手就要推敲半天,我想这就是大牛和新手之间最大的差别吧~~~。
3.1

msgcheck_2120


此函数代码很长,下面仅贴上关键部分(IDA F5 伪代码):
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
if ( (unsigned __int16)wParam == 0x3EB )    // 点击了OK按钮
    {
      sub_402790(String, 10, (const char *)L"%d", ++dword_420318);
      v4 = GetDlgItem(hWnd, 1000);
      SetWindowTextW(v4, String);
      v5 = (struct_count_array *)operator new(0x14u);
      v40 = 0;
      v6 = v5;
      *(_OWORD *)v39 = '\0';
      v7 = GetDlgItem(hWnd, 1000);
      GetWindowTextW(v7, v39, 10);
      vsscanf_27C0((int)v39, (const char *)L"%d", &v19);
      *(_DWORD *)&v6->Now_count = v19;
      v6->win_handle = (int)hWnd;
      v6->passwd_handle = (int)GetDlgItem(hWnd, 1002);
      v6->count_handle = (int)GetDlgItem(hWnd, 1000);
      v6->field_10 = (int)v6;
      CreateThread(0, 0, (LPTHREAD_START_ROUTINE)check_01_4020E0, v6, 0, 0);// 调用检查函数
      return 0;
    }

msgcheck_402120是MSG处理函数,这里处理所有返回的事件,上面代码为OK按钮事件,用CreateThread函数创建一个新线程,处理按钮事件,参数V6,即上面的V5,通过X64DBG中查看,估计是一个结构体,于是在IDA中自定义了一个结构体:如下
[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
9
struct_count_array struc ; (sizeof=0x14, mappedto_101)
00000000 Now_count       dw ?
00000002                 db ? ; undefined
00000003                 db ? ; undefined
00000004 win_handle      dd ?
00000008 count_handle    dd ?
0000000C passwd_handle   dd ?
00000010 field_10        dd ?
00000014 struct_count_array ends

在IDA中将变量V5指定为结构体指针,这样代码会更加清晰,结构体中:Now_count=当前尝试次数 win_handle=主窗体句柄  count_handle=显示次数文本框句柄  passwd_handle=密码框句柄 field_10=当前结构体指针,通过这结构体可以轻松访问密码框和次数框的文本内容。
结构体大小是20,从v5 = (struct_count_array *)operator new(0x14u);这句中就可以确定。
3.2

check_01_20E0


代码比较少,调用了check_02_401CB0,参数共2个,第一个是我们自定义的那个结构体数据,第二个参数是0
[C++] 纯文本查看 复制代码
1
2
3
4
5
6
int __stdcall check_01_4020E0(struct_count_array *lpMem)
{
  check_02_401CB0((int)lpMem, 0);
  sub_402CE4(lpMem);
  return 0;
}

3.3

check_02_401CB0


伪代码如下:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
void __fastcall check_02_401CB0(int a1, int a2)
{
  struct_count_array *canshu_1; // edi
  int v3; // ecx
  unsigned int len; // esi
  WCHAR *v5; // eax
  _WORD *next_passwd; // ebx
  clock_t v7; // [esp+4h] [ebp-D0h]
  WCHAR String; // [esp+8h] [ebp-CCh]
 
  canshu_1 = (struct_count_array *)a1;
  if ( a1 )
  {
    if ( a2 )
    {
      v7 = clock();
      memset(&String, 0, 200u);
      GetWindowTextW((HWND)canshu_1->passwd_handle, &String, 200);
      len = 0;
      v5 = &String;
      if ( String )
      {
        do
        {
          ++v5;
          ++len;
        }
        while ( *v5 );
      }
      next_passwd = operator new[](2 * (len + 1));
      if ( clock() - v7 > 2 )
        exit(0);
      sub_402870((int)next_passwd, &String);    // 修改了PASSWORD
      if ( len >= 7 )
      {
        if ( len <= 7 )
        {
          check_03_401A60((int)canshu_1, next_passwd);// len>=7 并且<=7 ,所以密码长度只能为7位
          return;
        }
        SendMessageW((HWND)canshu_1->win_handle, 0x40Du, 0, 0);
      }
      else
      {
        SendMessageW((HWND)canshu_1->win_handle, 0x40Eu, 0, 0);
      }
      j_j_j___free_base(next_passwd);
      return;
    }
    if ( checkhave_b_401C00((HWND *)a1)         // 检查是否含有‘b’
      && (memset(&String, 0, 0xC8u),
          GetWindowTextW((HWND)canshu_1->passwd_handle, &String, 100),
          checkhave_402A50(v3, (__int16 *)&String, 'p')) )// 检查是否含有 p
    {
      check_02_401CB0((int)canshu_1, 1);
    }
    else
    {
      SendMessageW((HWND)canshu_1->win_handle, 0x111u, 0x40Fu, 0);
    }
  }
}

这个函数很有意思,参数a2=0时,检测密码中是否含有字母‘p’和‘b’,如果有则调用自身,传a2参数为1,当a2=1时,检查密码长度是否为7位,通过这两个条件即可进入下一验证环节。
3.4

check_03_1A60


这个函数是一个加密函数,check_03_401A60(struct_count_array *a1, _WORD *a2),a2取我们输入的密码,在这个过程中被加密,加密过程也比较简单,只是做了一下异或加密。前半部分分析了半天貌似没有实际做用,估计是作者加的干扰代码。
函数最后调用:return check_right_401870(next_canshu_a1, next_passwd);,将加密后的密码传入下一验证步骤。
3.5

check_right_1870


伪代码如下:
[C++] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
BOOL __fastcall check_right_401870(struct_count_array *a1, _WORD *a2)
{
  struct_count_array *struct_count_list; // ebx
  _WORD *password; // edi
  char *char_N; // ecx
  signed int v5; // eax
  signed int v6; // eax
  __int16 *v7; // ecx
  unsigned int count; // edx
  __int16 *i; // eax
  unsigned int v10; // ecx
  unsigned int v11; // eax
  unsigned int len_pw; // ecx
  _WORD *v13; // eax
  unsigned int v14; // eax
  unsigned int len_pw1; // edx
  _WORD *j; // eax
  unsigned int v17; // ecx
  unsigned int v18; // eax
  int n; // esi
  __int16 enc_s; // cx
  __int64 *v21; // ebx
  __int16 *_pswd; // eax
  __int16 v23; // dx
  __int16 *v24; // ecx
  int v25; // eax
  int v26; // ecx
  __int64 *v27; // eax
  _WORD *v28; // esi
  unsigned int v29; // ecx
  struct_count_array *tmp_arg_1; // [esp+Ch] [ebp-54h]
  __int16 enc_list[28]; // [esp+10h] [ebp-50h]
  char v33; // [esp+48h] [ebp-18h]
  __int64 v34; // [esp+50h] [ebp-10h]
  __int16 v35; // [esp+58h] [ebp-8h]
 
  struct_count_list = a1;
  password = a2;
  tmp_arg_1 = a1;
  memset(enc_list, 0, 54u);
  char_N = &v33;
  v5 = '0';
  do
  {
    *(_WORD *)char_N = v5;
    char_N += 2;
    ++v5;
  }
  while ( v5 <= '9' );                          // 填充数字 0-9
  v6 = 'a';
  v7 = enc_list;
  do
  {
    *v7 = v6;
    ++v7;
    ++v6;
  }
  while ( v6 <= 'z' );                          // 填充小写字母 a-z
  count = 0;
  for ( i = enc_list; *i; ++count )             // 计算个数
    ++i;
  v10 = 0;
  if ( count )
  {
    do
    {
      v11 = (unsigned __int16)enc_list[v10];
      if ( v11 >= 'a' && v11 <= 'z' )
        enc_list[v10] = v11 - 32;
      ++v10;
    }
    while ( v10 < count );                      // 小字字母转为大写字母
  }
  len_pw = 0;
  v13 = password;
  if ( password )
  {
    if ( *password )
    {
      do
      {
        ++v13;
        ++len_pw;
      }
      while ( *v13 );                           // count_pw = Password长度
    }
    v14 = 0;
    if ( len_pw )
    {
      do
      {
        if ( v14 >= 2 )
        {
          if ( v14 >= 4 )
            password[v14] ^= 'B';
          else
            password[v14] ^= 'P';
        }
        else
        {
          password[v14] ^= 0xFu;
        }
        ++v14;
      }
      while ( v14 < len_pw );
    }                                           // 还原已经加密过的PASSWORD
    len_pw1 = 0;
    for ( j = password; *j; ++len_pw1 )
      ++j;
    v17 = 0;
    if ( len_pw1 )
    {
      do
      {
        v18 = (unsigned __int16)password[v17];
        if ( v18 >= 'a' && v18 <= 'z' )
          password[v17] = v18 - 32;
        ++v17;
      }
      while ( v17 < len_pw1 );                  // password小写转大写
    }
  }
  n = 0;
  v34 = '\0';
  v35 = 0;
  if ( *password )
  {
    enc_s = enc_list[0];
    v21 = &v34;
    _pswd = password;
    do
    {
      if ( enc_s )
      {
        v23 = *_pswd;
        v24 = enc_list;
        v25 = 0;
        while ( v23 != *v24 )
        {
          v24 = &enc_list[++v25];
          if ( !enc_list[v25] )
            goto LABEL_37;
        }
        *(_WORD *)v21 = enc_list[v25];
        v21 = (__int64 *)((char *)v21 + 2);
LABEL_37:
        enc_s = enc_list[0];
      }
      _pswd = &password[++n];
    }
    while ( password[n] );
    struct_count_list = tmp_arg_1;
  }                                             // 取出第一个字母的位置 V34
  v26 = 0;
  v27 = &v34;
  if ( (_WORD)v34 )
  {
    do
    {
      v27 = (__int64 *)((char *)v27 + 2);
      ++v26;
    }
    while ( *(_WORD *)v27 );
    if ( v26 == 2 )
    {
      LODWORD(v34) = '5\01';
      HIDWORD(v34) = &unk_420050;               // “15PB”
      v28 = password + 2;
      v35 = 0;
      v29 = 0;
      while ( *((_WORD *)&v34 + v29) == *v28 )
      {
        ++v29;
        ++v28;
        if ( v29 >= 4 )
        {
          if ( !sub_401740(struct_count_list, password) )
            return PostMessageW((HWND)struct_count_list->win_handle, 0x111u, 0x40Au, 0);// --40B--成功--
          return PostMessageW((HWND)struct_count_list->win_handle, 0x111u, 0x40Bu, 0);
        }
      }
    }
  }
  return PostMessageW((HWND)struct_count_list->win_handle, 0x111u, 0x40Au, 0);
}

这一函数很长,实现的功能也很复杂,函数首先构建一个密码表“123456789AB......Z”,实际用到的只有前9个数字字符,然后还原上一步聚中加密的密码,并转为大写,然后对比了我们输入密码的3至6位是否为“15PB”,这样我们就可以确定我们的密码第3-6位正确的应该是:“15pb”
函数另外调用了sub_401740(struct_count_list, password)来进行验证,IDA伪代码如下:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
signed int __fastcall sub_401740(_DWORD *a1, _WORD *a2)
{
  _WORD *the_password; // ebx
  struct_count_array *v3; // edx
  __int128 *v4; // ecx
  signed int char_1; // eax
  int len; // esi
  _WORD *v7; // eax
  char *new_string; // edi
  unsigned __int16 *last_passwd; // eax
  int v10; // esi
  __int128 *v11; // ecx
  __int16 v12; // di
  __int16 v13; // ax
  __int128 *v14; // ecx
  signed int v15; // edx
  unsigned __int16 *v17; // [esp+4h] [ebp-50h]
  struct_count_array *v18; // [esp+8h] [ebp-4Ch]
  char *v19; // [esp+Ch] [ebp-48h]
  __int128 charlist_1to9; // [esp+30h] [ebp-24h]
  __int64 v21; // [esp+40h] [ebp-14h]
  int v22; // [esp+48h] [ebp-Ch]
  __int16 v23; // [esp+4Ch] [ebp-8h]
 
  the_password = a2;
  v22 = 0;
  v3 = (struct_count_array *)a1;
  v23 = 0;
  v18 = (struct_count_array *)a1;
  charlist_1to9 = '\0';
  v4 = &charlist_1to9;
  char_1 = '1';
  v21 = '\0';
  do
  {
    *(_WORD *)v4 = char_1;
    v4 = (__int128 *)((char *)v4 + 2);
    ++char_1;
  }                                             // charlist_1to9="123456789"
  while ( char_1 <= '9' );
  len = 0;
  v7 = the_password;
  if ( the_password && *the_password )
  {
    do
    {
      ++v7;
      ++len;
    }
    while ( *v7 );                              // 计算密码长度
  }
  new_string = cat_4028D0((char *)&charlist_1to9, &the_password[*(_DWORD *)&v3->Now_count]);
  last_passwd = &the_password[len - 1];
  v19 = new_string;
  v10 = 0;
  v17 = last_passwd;
  if ( (_WORD)charlist_1to9 )
  {
    v11 = &charlist_1to9;
    v12 = *last_passwd & 1;
    while ( 1 )
    {
      v13 = v12 + (*(_WORD *)v11 >> 2);
      if ( v13 == '2' )
        break;
      if ( v13 != 'd' )
      {
        v11 = (__int128 *)((char *)&charlist_1to9 + 2 * ++v10);
        if ( *(_WORD *)v11 )
          continue;
      }
      new_string = v19;
      goto LABEL_12;
    }
  }
  else
  {
LABEL_12:
    v14 = &charlist_1to9;
    v15 = '1';
    while ( *(_WORD *)v14 == *(_WORD *)((char *)v14 + (char *)the_password - (char *)&charlist_1to9) )// 第一位 是 1   the_password 是原始密码
    {
      v15 += 6;
      v14 = (__int128 *)((char *)v14 + 2);
      if ( v15 > '9' )
      {
        if ( (unsigned __int16)*the_password + *((unsigned __int16 *)new_string + 9) == 'c'// 第二位即:*((unsigned __int16 *)new_string + 9,所以第
 
二位是2
          && *v17 == *(_DWORD *)&v18->Now_count + *((unsigned __int16 *)new_string + 6) )// 最后一位:次数+字串里的7,即8
        {
          return 1;
        }
        return 0;
      }
    }
  }
  return 0;
}

在sub_401740中,验证第一位是否为“1”,函数将密码连接成“123456789”+从调用次数开始截取到最后的密码部分,比如第一次点OK时密码是“ABCDEFG”,连接后即为“123456789BCDEFG”,验证第一位+第二位是否等于‘c’,所以第二位为‘2’,通过取“123456789BCDEFG”中第(7+验证次数)位与我们输入密码的最后一位比较,来验证最后一位,所以第一次验证时最后一位应该是“8”,但第二次验证则无法验证成功第二位的2,因为2被截去了,所以只能在第一次按OK时输入“1215pb8”才能通过验证。
至此整个分析过程完成,并成功找到密码。
截个图:
Image 003.png
4.

反调试分析


关于反调试:程序启动有反调试:2处:
1、比较父进程名称,是否为exeplorer.exe
0109152C | 74 04                        | je crack_.1091532                              |
0109152E | 84 D2                        | test dl, dl                                    |
01091530 | 74 1A                        | je crack_.109154C                              | 跳去结束程序
01091532 | 57                           | push edi                                       |
01091533 | FF 15 60 80 0A 01            | call dword ptr ds:[<&CloseHandle>]             |
01091539 | 8B 4D FC                     | mov ecx, dword ptr ss:[ebp-0x4]                |
0109153C | 33 C0                        | xor eax, eax                                   |
0109153E | 5F                           | pop edi                                        |
0109153F | 5E                           | pop esi                                        |
01091540 | 33 CD                        | xor ecx, ebp                                   |
01091542 | 5B                           | pop ebx                                        |
01091543 | E8 7D 17 00 00               | call crack_.1092CC5                            |
01091548 | 8B E5                        | mov esp, ebp                                   |
0109154A | 5D                           | pop ebp                                        |
0109154B | C3                           | ret                                            |
0109154C | 6A 00                        | push 0x0                                       |
0109154E | E8 5E 36 00 00               | call <crack_.sub_1094BB1>                      |
2、通过以下代码,比较运行中延时时间,大于2毫秒,则认为被调试,程序退出,这块没明白具体原理,下断结果3毫秒,比判断多了1毫秒,不知道是怎么产生的,猜测是程序被调试响应速度慢。
[Asm] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
01091575 | 57                           | push edi                                       | 获取时间
01091576 | E8 63 33 00 00               | call <crack_.sub_10948DE>                      |
0109157B | 8B 35 84 81 0A 01            | mov esi, dword ptr ds:[<&LoadStringW>]         |
01091581 | 8B F8                        | mov edi, eax                                   |
01091583 | 6A 64                        | push 0x64                                      |
01091585 | 68 20 04 0B 01               | push crack_.10B0420                            | 10B0420:L"Crack_Me_4"
0109158A | 6A 67                        | push 0x67                                      |
0109158C | 53                           | push ebx                                       |
0109158D | FF D6                        | call esi                                       |
0109158F | 6A 64                        | push 0x64                                      |
01091591 | 68 58 03 0B 01               | push crack_.10B0358                            | 10B0358:L"CRACK_ME_4"
01091596 | 6A 6D                        | push 0x6D                                      |
01091598 | 53                           | push ebx                                       |
01091599 | FF D6                        | call esi                                       |
0109159B | 8B CB                        | mov ecx, ebx                                   |
0109159D | E8 FE 00 00 00               | call <crack_.sub_10916A0>                      |
010915A2 | E8 37 33 00 00               | call <crack_.sub_10948DE>                      |
010915A7 | 2B C7                        | sub eax, edi                                   |
010915A9 | 6A 00                        | push 0x0                                       |
010915AB | 83 F8 02                     | cmp eax, 0x2                                   | 比较时间
010915AE | 0F 8F DA 00 00 00            | jg crack_.109168E                              |

中间也有几处反调试,皆为第2种方法,通过时间差来判断。

附上Crackme程序: Crack_Me.exe.rar (124.37 KB, 下载次数: 33)
IDA数据文件: Crack_Me.idb.rar (456.83 KB, 下载次数: 21)

免费评分

参与人数 18吾爱币 +20 热心值 +17 收起 理由
xiaobaixiaobai + 1 + 1 谢谢@Thanks!
ourshiningdays + 1 + 1 用心讨论,共获提升!
jimmy2752 + 1 + 1 我很赞同!
lmdme + 1 + 1 我很赞同!
liphily + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
郭雨帅哥 + 1 + 1 谢谢@Thanks!
pass101 + 1 + 1 用心讨论,共获提升!
xyuetao + 1 + 1 谢谢@Thanks!
zzzlucas + 1 + 1 谢谢@Thanks!
无痕软件 + 3 + 1 兄弟进展神速啊。
plasd + 1 + 1 谢谢@Thanks!
vLove0 + 1 + 1 用心讨论,共获提升!
hjm666 + 1 + 1 用心讨论,共获提升!
sysksy + 1 + 1 谢谢@Thanks!
liyuaism + 1 + 1 我很赞同!
独行风云 + 1 + 1 用心讨论,共获提升!
测试中…… + 1 + 1 用心讨论,共获提升!
lanlana + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

cyhcuichao 发表于 2018-6-22 22:31
学习了楼主
 楼主| pk8900 发表于 2018-6-22 10:31
hjm666 发表于 2018-6-22 09:25
刚开始学的时候有注意到看雪的这个CM,后来死在了反调试上,现在一直就没有遇到过反调试CM,想问附加程序后 ...

想找反调试,就不能进行附加,载入后查找API调用,进程检测的几个API函数是必须要跟的,比如:<kernel32.CreateToolhelp32Snapshot>快照函数,<kernel32.Process32FirstW>和<kernel32.Process32NextW>遍历函数,<kernel32.ExitProcess>和<kernel32.TerminateProcess>结束进程函数,在这个CM中引用了时间较验,可下断<kernel32.GetSystemTimeAsFileTime>,关于反调试方面太多,大多通过下断<kernel32.ExitProcess>后,进行回溯。本例中最快方法是IDA中查找MFC的Exit调用。
头像被屏蔽
yahanwangluo 发表于 2018-6-21 19:50
其实从技术角度出发来看,这个东西应该这样分析,我不会!
测试中…… 发表于 2018-6-21 21:14
学习下,比较忙都没时间搞这些东西......
chenjingyes 发表于 2018-6-22 00:52
谢谢楼主分享   
iteamo 发表于 2018-6-22 08:40
越学习发现不会的越多  怎么办
gutfreund 发表于 2018-6-22 09:13 来自手机
学习下,学海无涯
hjm666 发表于 2018-6-22 09:25
刚开始学的时候有注意到看雪的这个CM,后来死在了反调试上,现在一直就没有遇到过反调试CM,想问附加程序后,是怎么找到检测OD在运行的关键点,并过检测的啊??
连长233 发表于 2018-6-22 10:06 来自手机
谢谢楼主分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-3-15 23:20

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表