crackme.chm之Chafe1

    这个程序没有注册或者确定按钮,所以我们只有找到窗口过程才能知道

    按下F5便可知道窗口过程的处理流程

LRESULT __stdcall sub_401239(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  LRESULT result; // eax@2

  switch ( Msg )
  {
    case 0x10u:
      KillTimer(hWnd, 0xCAu);
      PostQuitMessage(0);
      result = 0;
      break;
    case 0xFu:
      sub_401325();
      result = 0;
      break;
    case 1u:
      SetTimer(hWnd, 0xCAu, 1u, 0);
      result = 0;
      break;
    case 0x113u:
      sub_401453();
      if ( byte_403166 == byte_403167 )
      {
        result = 0;
      }
      else
      {
        byte_403167 = byte_403166;
        if ( byte_403166 == 16 )
        {
          SetWindowTextA(::hWnd, "YES! You found your serial!!");
          result = 0;
        }
        else
        {
          SetWindowTextA(::hWnd, "Your serial is not valid.");
          result = 0;
        }
      }
      break;
    default:
      if ( Msg != 273 || lParam != dword_403180 )
      {
        result = DefWindowProcA(hWnd, Msg, wParam, lParam);
      }
      else
      {
        MessageBoxA(
          hWnd,
          "Small crack-me program written by Chafe/TEX99\n\rRULES: Find a serial that matches your name or write a keygenerator. NO PATCHES IS ALLOWED!\n\r\n\rReleased 25/3-99",
          "About TEXme v1.0",
          0);
        result = 0;
      }
      break;
  }
  return result;
}

    通过简单的分析上面的函数,我们知道程序的验证应该是通过Timer消息来完成的,并且核心在sub_401453


    这个函数按F5反编译过来的函数是不准确的,所以这边我们需要自己分析函数


    这边可以看到红色部分先保存了当前的esp的值到一个全局变量,然后再将一处地址赋给esp(esp可以看做一个指针),然后根据byte_403166的值对esp进行调整,相当于移动指针,使其指向下一个数据,最后指向retn后返回的地址是当前esp指向的一块dd大小的数据值,上面的步骤相当于模拟一个函数的调用,即call FuncArray[index],其中FuncArray就是off_403152表示指向函数地址数组的指针,而index就是byte_403166,这边在IDA将其重命名下

    这边也可以看到我们的猜测是正确的,也就是验证函数就是依次执行这个函数数组中的每个函数

    首先来看第一个函数,按下F5自动分析

    可以看到,这个函数就是取序列号,序列号如果转换成整形失败,则不继续执行下一个函数(index+4前面已经分析过了,esp+4表示指向下一个函数,每个函数地址的大小为4字节),我们看到转换后的整形序列号值保存到dword_403188中了,所以这个地方可以将其重命名为iSerial。函数的关键:序列号只能使用数字字符0~9,而且不能使用”-”

    接下来分析第二个函数,这边由于函数是嵌套在另一个函数中的,所以使用F5没有效果,这边直接根据视图来分析函数


    简单的分析可以得知,这就是一个获取用户名的过程,将名字保存到byte_40318C字符数组里面(这边将byte_40318C重命名为szBuf),红色部分以及第二幅图的作用就是将szBuf未使用部分填充0,第三幅图发现eax是关键,而通过第一幅图可以知道,eax保存的是用户名的字符长度,这边还要注意如果用户名长度不为0,还会多一个byte_403168赋0的操作,这个操作会影响下一个函数的分析函数②的关键就是:用户名长度>0。

    现在来看第三个函数


    编辑下原先的函数范围,将中间的loc_401361设置为函数,然后再在loc_401325处创建新函数,这样原先的loc_401361就可以使用F5了,当然直接分析也是很简单的,分析结果如下

    在函数②的时候知道byte_403168会被赋值0,所以它也相当于一个索引计数i,我们看到只要i没变成16,index就不会+4,可以知道这边就是利用timer来模拟一个while循环,它的等价代码如下:(IDA的反编译结果有些问题,需要结合实际代码进行分析)

int __cdecl sub_401361()
{
  char *p;
  do
  {
	  p = &szBuf[i++];
	  iSerial++;
	  iSerial = *(int *)p ^ iSerial;
  }while (i < 16);
}

    现在分析下第四个函数

    这个函数非常简单,就是iSerial的值在经过函数③后加上152118392要为0,而前面①已经知道iSerial不能为负数,函数③也没法将iSerial变成负数,所以这边要利用溢出,即两个大数相加要超过32位的寄存器标示范围,那么结果也是0,只是多一个进位,用计算器可以得出这个数为4142848904

    通过上面的分析可以得出等价伪代码以及对应注册算法如下

#include <stdio.h>

/*int __cdecl sub_401361()
{
  char *p;
  char szBuf[?] = "?";
  int iSerial = ?;
  int i=0;
  do
  {
	  p = &szBuf[i++];
	  iSerial++;
	  iSerial = *(int *)p ^ iSerial;
  }while (i < 16);
}*/

void main()
{
	char szBuf[20] = "@@@@@@@@@@@@@@@@@@@";
	int iSerial = 4142848904;
	char *p;
	for (int i=15; i>=0; i--)
	{
		p = &szBuf[i];
		iSerial = *(int *)p ^ iSerial;
		iSerial--;
	}
	printf("The Serial:%u\t\n", iSerial);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值