吾爱破解 - LCG - LSG |安卓破解|病毒分析|破解软件|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 1552|回复: 11

[原创] 一个crackme的分析

[复制链接]
女萝岩 发表于 2020-1-21 02:50
本帖最后由 女萝岩 于 2020-1-21 03:06 编辑

拿到crackmePEID的深度扫描发现是aspack2.x的壳,用Abstersiver把壳脱掉。运行脱壳后的程序,随便输入几组用户名和注册码,发现并没有任何提示信息。由此可以推测只有输入正确的nameserial程序才会给出提示信息。用OD加载起来程序,大概浏览一下代码,找到两处调用MessageBox的地方,很容易判断上面那个MessageBox是和验证注册码相关的。因为它上面有一个跳转,那个跳转是要跳转到返回的,所以这个跳转很关键,而决定要不要跳转的判断条件是eax的值,而eax的值是call 00401509的返回值。所以00401509这个call很关键,我们在这个地址处下一个断点重点分析一下。
image.png
下面就是00401509这个CALL的详细分析。Ctrl+F2重新启动被调试程序,F9让程序运行起来,我先输入一组nameserialnamenvluoyan,serialwhaththefuck ,点击程序的check按钮,发现程序被断在了00401509

image.png

总结一下就是获取用户输入的name并对name的长度进行判断。

image.png
总结一下就是对name的长度进行运算,确保name的长度必须在一个范围之内,即0x0190<=((0x02bc-5*(0x30-0x48/Lname))*0x6b)-0xcf6c<=0x2300解一下这个不等式。这个不等式我之后直接用16进制计算,发现得不到正确的结果,不知道为什么。

image.png

我们输入的nvluoyan8位,正好在[39]之间。

name的长度验证之后就进行了对serial的处理了。处理serial的函数需要三个参数,第一个是hwnd,和算法没关系,第二个是name的长度Lname,第三个是用户输入的用户名name image.png

然后进入00401305这个call的内部来看看。

image.png
没什么重要的信息,就是一些字符串初始化操作,字符串的作用都在注释中写清楚了,就不多做解释了。

image.png
然后获取用户输入的Serial,把Serial存放到UserSerial中。

image.png
获取用户输入的Serial的第一个字符,用0x11cfSerial的第一个字符取余,检测结果是不是0x17,如果是则继续流程,否则返回。可见这个crackmeSerial的第一个字符是有要求的,我们可以写一个小程序来输出一下,很简单的遍历输出即可。

image.png
输出结果是:

image.png
所以用户输入的Serial的第一个字符必须是$ * 6 8 ? H Q T l ~ 10个中的其中一个。为了让流程继续下去,我们修改一下输入的Serial,从whatthefuck变为6hatthefuck

image.png
然后是取得用户的name,并把name中的每个字符按顺序加起来,最后的结果保存在sum中。00401305这个CALL有两个重要的参数一个是用户输入的name,一个是name的长度Lname,在这里用到了!

image.png
这部分就是就是关键了,详细的在注释中都说明了。

image.png
然后对SerialFirst数组格式化,就是在最前面加一个大写字母T,把结果存放到SerialFormatFirst数组中。

image.png

然后是对SerialFirst数组的第二次格式化,结果存放到SerialFormatSecond[256]中。

image.png

然后就是一个重要的call了,这个call有三个参数,其中两个是根据name生成并格式化过的Serial和它的长度,另外一个是用户输入的Serial image.png

程序并没有直接比较SerialFormatSecondUserSerial中的每个字符,而是取得SerialFormatSecond中的每个字符,经过一定运算得到一个字符,如果这个字符和UserSerial中相应的某个字符比较是否相等,所以注册机就很好写了。值得注意的一点是,有一个代码是idiv ecx,是对0xA取余。取余这个操作很有意思,它的结果总是小于自己,对0x0A取余,那么结果一定小于0x0A,想一想为什么要对0x0A取余呢?因为它的结果一定是在00x0x0A-1这个范围里面,再加上0x30,那么这个范围就变成了0x30~0x3A-1,对应的ASCII码就是数字0到数字9,这样就保证用户的注册码只能包含数字来了。注册机源码本来想用python写的,可是对于位操作不太熟悉,所以就用C语言写了一个。
[C] 纯文本查看 复制代码
#include <iostream>
#include "string.h"
#include "stdio.h"
#include "stdlib.h"


int main()
{
    char name[256] = {0};
    char MagicNum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    char GenSerial[256] = {0};
    char FormatSerial[256 * 2] = { 0 };
    int Lname =0;
    int LMagicNum = strlen(MagicNum);
    int LSerial = 0;
    int sum = 0;
    char cname = 0;
    char cMagicNum=0;
    char cGenSerial = 0;

    int index = 0;
    int xor1 = 0;
    int xor2 = 0;
    int sum2 = 0;
    int FormatChar = 0;
    int index2 = 0;

    char CharComputSerial = 0;
    
    int exit;
  

    printf("please input your name :");
    scanf_s("%s",name,256);
    
    printf("\n");
    printf("*******************************************************\n");
    Lname = strlen(name);


    //用户名长度必须大于等于3小于等于9
    if (Lname < 3 || Lname>9)

    {
        printf("the length of user name shall range 3 to 9");
        return 0;
    }


  


    //return 1;

    for (int i = 0; i <= Lname; i++)
    {
        sum = sum + name[i];
    }

  //不支持ASCII码之外的字符 0x21~0x7E
  //写一个循环检测name中的每个字符是否在0x21到0x7e中当然可行
  //但我想用另外一种方法,既然name的长度为3位到9位,而且要保证
  //每个字符都属于键盘输入的可见ASCII码,那么name中所有的字符的和
  //一定有一个最小值,也一定有一个最大值
  //最小值就是0x21*3,当name长度是3位的时候
  //最大值就是0x7e*9,当name长度是9位的时候

    if (sum < 0x21 * 3 || sum>0x7e * 9)
    {
        printf("illegle user name");
        return 0;
    }


   

    do {
        
        
        cname = name[index];
   
        cMagicNum=MagicNum[3 * index - 1];
        if (index == 0)
        {
            cMagicNum = 0x0;
        }
        xor1 = cname ^ cMagicNum;
    

        xor2 = (sum * index - sum) ^ 0xffffffff;

 

        sum2 = xor1 + xor2 + 0x014d;

    
        cGenSerial = (((sum2 + Lname * (index + 3) * cname) % 0x0A) + 0x30)&0xff;

   
        cGenSerial = ((((unsigned int)cGenSerial^ 0x0ADAC)  * (index + 2)) % 0x0A + 0x030) & 0xff;
          
        
       

        GenSerial[index] = cGenSerial;
        index++;
    } while (index < Lname);

    

    

    FormatChar = ((Lname * sum) % 0x64)+0x30;


    sprintf_s(FormatSerial, "%c%s-%d", 'T', GenSerial,FormatChar);


    LSerial = strlen(FormatSerial);
   



    index2 = 1;


    do
    {
       CharComputSerial= FormatSerial[index2];
       CharComputSerial ^= 0x20;
       CharComputSerial %= 0x0A;
       CharComputSerial += 0x30;
       FormatSerial[index2] = CharComputSerial;

       index2++;

    } while(index2<LSerial);


    printf("your name:%s\n",name);
    printf("your code:%s\n",FormatSerial);
    printf("*******************************************************\n");

    scanf_s("%d",&exit);

    return 1;


}


image.png

image.png
crackme6.zip (5.2 KB, 下载次数: 6)

免费评分

参与人数 6威望 +2 吾爱币 +10 热心值 +5 收起 理由
不羡云卿颜_ + 1 + 1 谢谢@Thanks!
生有涯知无涯 + 1 我很赞同!
Hmily + 2 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
smile5 + 1 + 1 用心讨论,共获提升!
alittlebear + 1 用心讨论,共获提升!
FleTime + 1 用心讨论,共获提升!

查看全部评分

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

chenjingyes 发表于 2020-1-22 09:45
byh3025 发表于 2020-1-22 08:24
大神是通过什么找到弹窗的call的呢?

我记得有个叫什么xspy还是什么的东西可以抓按钮事件,前一阵子这个论坛看过相关文章
 楼主| 女萝岩 发表于 2020-1-21 03:16
每天下班之后分析一点,弄了差不多一个星期吧,语言表达能力很差,各位多指点。
weikun444 发表于 2020-1-21 12:21
netspirit 发表于 2020-1-21 12:42
感觉好厉害..........
52896009 发表于 2020-1-21 17:29
好厉害..........
小小学生 发表于 2020-1-21 21:54
高手在民间。很低调啊。希望多出原创作品交流交流
byh3025 发表于 2020-1-22 08:24
大神是通过什么找到弹窗的call的呢?
chenjingyes 发表于 2020-1-22 09:15
不错的题目 可以练练手
hua111 发表于 2020-1-22 10:06
还不错还可以
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2020-4-10 10:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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