基丶 发表于 2021-5-1 15:14

看雪CTF逆向练习题-[乘风破浪] 难度171

题目链接: https://ctf.pediy.com/itembank.htm

上传统

无壳,放心干。

看图标跟library,是个MFC程序,用xspy工具查响应事件函数。

两个事件相应函数对应两个函数,查下按钮的ID,ID为1,那么ID为1的事件函数我们需要逆的函数。



拖到IDA,跳到0401830 Look Look,F5看下伪代码

看到SetEvent函数,向一个事件发信号,大胆推断,这个程序使用了多线程。所以我们拖到OD去xxoo它。

多次拖OD后发现地址会变化,估计是搞了动态基址,我们用PE工具干掉。



NT头-》可选头-》DllCharacteristics,把动态基址的位去掉即可。



拖到OD后,会断在主模块,这是线程还没创建,我们给创建线程函数下断 bp CreateThread后运行


断下后,我们看下CreateThread的回调函数,有一些是输入法的回调函数,我们要过滤掉。


所以一共有两个本模块的回调函数,4011B0 和 401220。




我们在这些回调函数的WaitForSingleObject 函数的后面下断点,看看那个函数会断下,最后是发现401220 函数会断下。

所以我们到IDA跳到401220结合OD,动静态结合分析。


void __stdcall sub_401220(LPVOID lpThreadParameter)
{
_DWORD *v1; // edi
DWORD v2; // esi
int V3; // ecx
_DWORD *v4; // ebp
int v5; // eax
int v6; // ebx
int v7; // eax
int v8; // edx
int v9; // edi
int v10; // ecx

v1 = lpThreadParameter;                     // 我们输入的用户名
if ( lpThreadParameter && dword_442A64 )
{
    while ( 1 )
    {
      v2 = WaitForSingleObject(hEvent, 0xFFFFFFFF);
      ResetEvent(hEvent);
      if ( !v2 )
      {
      V3 = *(_DWORD *)(v1 - 12);          // V3是用户名长度
      v4 = v1 + 30;                           // V4 = 用户名
      if ( V3 )
      {
          v5 = *(_DWORD *)(v1 - 12);      // V5 = 注册码长度
          if ( v5 )
          {
            if ( V3 == v5 && !(unsigned __int8)sub_401870(v1 + 30, v1 + 31) )// V1+30 = 用户名 ; V1+31 = 注册码
                                                // 这里判断 用户名 == 注册码 && strcmp(用户名,注册码)
            {
            v6 = *(_DWORD *)(*v4 - 12);       // V6 = 注册码长度
            if ( v6 >= 8 )                  // 长度 >=8
            {
                v7 = 0;
                v8 = v6 - 1;
                v9 = 2 * v6 - 2;
                while ( 1 )
                {
                  if ( v7 < 0
                  || v7 > *(_DWORD *)(*v4 - 12)// *(_DWORD *)(*v4 - 12) = 用户名长度
                  || v8 < 0                   // V8 = 长度 -1
                  || (v10 = *((_DWORD *)lpThreadParameter + 31), v8 > *(_DWORD *)(v10 - 12)) )// *(_DWORD *)(v10 - 12)) 注册码长度
                                                //
                                                // V10= *((_DWORD *)lpThreadParameter + 31) 注册码
                  {
                  sub_401E00(-2147024809);
                  }
                  if ( *(_WORD *)(*v4 + 2 * v7) != *(_WORD *)(v9 + v10) )//   *(_WORD *)(*v4 + 2 * v7) 取出用户名每个子串,V7是下标,从0开始+2
                                                //
                                                // V10是注册码,V9是(注册码的长度 *2-2),下标从长度开始,每个循环-2
                                                //
                                                // 用户名跟注册码的缓冲区都处理过,变成了宽字符,每个字符占2字节。
                                                //
                  break;
                  ++v7;
                  v9 -= 2;
                  --v8;
                  if ( v7 >= v6 )               // 用户名下标 >= 注册码长度
                  {
                  if ( hObject )            // 成功
                      SetEvent(hObject);
                  break;
                  }
                }
            }
            }
          }
      }
      }
      if ( !dword_442A64 )
      break;
      v1 = lpThreadParameter;
    }
}
}

以上算法就能得出,注册码与用户名的长度必须一致,并且注册码长度必须大于等于8,。
注册码就是用户名倒过来的(裤子都脱了,你就给我看这个?)

rbj520 发表于 2021-5-20 08:22

謝謝分享,學習學習

chmod755 发表于 2021-7-29 14:06

            if ( V3 == v5 && !(unsigned __int8)sub_401870(v1 + 30, v1 + 31) )// V1+30 = 用户名 ; V1+31 = 注册码
                                                // 这里判断 用户名 == 注册码 && strcmp(用户名,注册码)
楼主,这个应该是 用户名长度==   注册码长度吧

cptw 发表于 2021-5-1 21:27

大佬厉害

nmy124 发表于 2021-5-1 23:02

谢谢楼主的实例,分析很到位,谢谢

fzj 发表于 2021-5-2 08:12

虽然我看不懂,但顶大佬

我的未来梦 发表于 2021-5-2 10:06

可以学习一下,

shckfb 发表于 2021-5-2 14:43

谢谢楼主实例

朱朱你堕落了 发表于 2021-5-3 05:56

最好能把crackme以附件形式上传一下。

daxiang789 发表于 2021-5-14 22:55

感谢分享,学习了
页: [1] 2
查看完整版本: 看雪CTF逆向练习题-[乘风破浪] 难度171