接着上次的分析,继续分析下去,对于这个CrackMe,其要求是写出注册机,那么我们就根据其注册码的验证过程,进行逆向分析,首先找到最关键的一个爆破点,我们先看看这个爆破点的位置,如图1可见最后的验证关键点其实在那个MagicNum的值上面,因为用户名是我们给定的,属于已知值,而序列号则是可以由我们构造的也可以认为是已知值,唯一不确定的就是那个MagicNum了,而这个值却跟我们输入的用户名及序列号密切相关。因此切实找到:MagicNum(魔数)如图2与用户名、序列号之间的关系就是最后写出注册机的关键所在了。

(图1)

接下来我们大致看看这三者之间的关系 :最后的注册码正确与否取决于序列号的最后四个字符,即:我们设dword ptr[402168]为a,

dword ptr[40216c]为b,dword ptr[402179]为c,需要算出的最后四个字节为nResult,其必须满足,nResult的值必须都在ASCII码可打印表示范围内(PS:不然你无法输入对应的字符......),且nResult == a^b^MagicNum|0x40404040&0x77777777^c。其中的a\b\c皆可由我们构造,而MagciNum则与我们构造的这些数相关,到底有些什么关系呢?我们接下来看看MagicNum与我们构造的用户名及序列号之间的关系。

(图2)

其中的edi可以是用户名算出值亦可为序列号算出值。(此值即为Step1过程中算出的值),整个递归过程与edi值有关。通过阅读程序,我们需要找到edi与递归次数之间的直接关系,这样我们可以想办控制魔数的自增次数从而控制住魔数的值。通过在给定用户名的情况下,利用edi与魔数之间的关系,构造出满足条件的序列号(这个过程可以控制在可容忍范围内的穷举)。在分析的过程中比较棘手的地方是就是递归调用。OK,看到这里基本定位了关键点所在。我们先暂停一下,仅从分析如何快速制作出可行的注册机的角度来尝试一下,在这个角度上,首先要保证递归函数调用后的返回值ECX为1(这个时候关于递归与魔数的关系就可以不用考虑了),把这个条件分析出来。然后只用去利用:nResult == a^b^MagicNum|0x40404040&0x77777777这个值,自己去构造符合条件的剩余的序列号部分即可。例如:nResult值已知的情况下,设序列号剩下的两个字节分别为X1、X2,要求nResult^X1==X2,且X1,X2都在['0','~']这个范围内。根据这个思路可以写出如下的注册机:

// KeyGen.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <assert.h>
#include <iostream>
#include <math.h>
#include <bitset>
#pragma warning(disable:4244)typedef unsigned long DWORD;
typedef unsigned int  UINT;//
//函数声明部分
////
//扩展用户名到16字节
void ExtendNameTo16Bytes( char *pStrName );//验证序列号Step1
void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum, DWORD &outTempNum1, DWORD &outTempNum2 );//抽离的递归函数
void Func( DWORD nTempNum1, DWORD nTempNum2,UINT &uCount, DWORD &nMagicNum );//验证序列号Step2
bool CalculateKeyStep2( DWORD nTempNum1, DWORD nTempNum2, const char *pStrName, char *szLast8BytesSerials );//构造最后的8字节序列号
bool GenerateCorrect8BytesSerial( const DWORD nSrc, char szOUT[] );//N个字节字符串生成器
void GenerateNBytesStrings( const char *pStrUserName, char *pStr, const UINT nBytes );//
//函数实现部分
//
void ExtendNameTo16Bytes( char *pStrName )
{assert( NULL != pStrName );if ( NULL == pStrName ) return;size_t nStrLen = strlen(pStrName);if ( nStrLen >= 16 ) return;size_t nNeedtoExtend = 16 - nStrLen;int nCount = 0;while ( nNeedtoExtend-- ){*(pStrName+nStrLen+nCount) = *(pStrName+nCount);++nCount;}
}void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum, DWORD &outTempNum1, DWORD &outTempNum2 )
{assert( NULL != pStrName );assert( NULL != pStrSerialNum );if ( NULL == pStrName || NULL == pStrSerialNum )return;outTempNum1 =  *(DWORD*)pStrName;outTempNum2 = *(DWORD*)(pStrName+4);DWORD nSerialTemp1 = *(DWORD*)pStrSerialNum;DWORD nSerialTemp2 = *(DWORD*)(pStrSerialNum+4);outTempNum1 ^= nSerialTemp1;outTempNum2 ^= nSerialTemp2;outTempNum1 &= 0x7F3F1F0F; //0111 1111-0011 1111-0001 1111-0000 1111//x31=0,x23=0,x15=0,x7=0outTempNum2 &= 0x07030100; //0111 0000-0011 0000-0001 0000-0000 0000//x31=0,x23=0,x15=0,x7=0for (int idx=0; idx<8; ++idx){std::bitset<32>  vBtsEAX( outTempNum1<<idx );std::bitset<32>  vBtsEBX( outTempNum2<<idx );std::bitset<32>  vBtsR(0);vBtsR[7] = vBtsEAX[31];vBtsR[6] = vBtsEAX[23];vBtsR[5] = vBtsEAX[15];vBtsR[4] = vBtsEAX[7];vBtsR[3] = vBtsEBX[31];vBtsR[2] = vBtsEBX[23];vBtsR[1] = vBtsEBX[15];vBtsR[0] = vBtsEBX[7];int nValue = vBtsR.to_ulong();if (idx <= 3){nValue <<= 8*(3-idx);outTempNum1 ^= nValue;}else{nValue <<= 8*(7-idx);outTempNum2 ^=nValue;}}
}void Func( DWORD nTempNum1, DWORD nTempNum2, UINT &uCount, DWORD &nMagicNum )
{if ( uCount <= 0x80 ) return;UINT uTMCount = uCount;DWORD nValue = nTempNum1;uCount &= 0xff;       //取最低一个字节,根据下面程序的执行if ( uCount > 8 )     //其实为取CL的低四位.{nValue = nTempNum2;uCount >>= 4;     //取CL高4位.}//nValue为DWORD,占4个字节,如下图示://|---|---|---|---|//| 1 | 2 | 3 | 4 |//|___|___|___|___|//UINT uRotateCount = (UINT)(log((float)uCount)/log(2.0f));switch( uRotateCount%4 ){case 1:nValue >>= 16; //|---|break;         //| 2 |//|___|case 2:nValue >>= 8;  //|---|break;         //| 3 |//|___|case 3:break;         //|---|//| 4 |//|___|default:nValue >>= 24; //|---|break;         //| 1 |//|___|}uCount = (uTMCount&0xff00)>>8;nValue &=uCount;uCount = 0x80;while ( uCount&nValue ? 1:uCount>>=1 ){if ( !(uCount&nValue) ) continue;nValue ^= uCount;uCount ^= ((uCount&0xff)<<8);uTMCount &= 0xff00;uTMCount ^= uCount;++nMagicNum;Func( nTempNum1, nTempNum2, uTMCount, nMagicNum );uCount = 0x80;}uCount = uTMCount; //In order to retrieve the return value
}bool GenerateCorrect8BytesSerial( const DWORD nSrc, char szOUT[] )
{if ( NULL == szOUT )return false;char cCount = '0';char cV1(0);char cV2(0);char cV3(0);char cV4(0);char cS1(0);char cS2(0);char cS3(0);char cS4(0);while ( cCount <= '~' ){//第一字节cV1 = cCount^((nSrc&0xff000000)>>24);if ( 0 == cS1 && cV1 >= '0' && cV1 <= '~' )cS1 = cCount;//第二字节cV2 = cCount^((nSrc&0xff0000)>>16);if ( 0 == cS2 &&cV2 >= '0' && cV2 <= '~' )cS2 = cCount;//第三字节cV3 = cCount^((nSrc&0xff00)>>8);if ( 0 == cS3 && cV3 >= '0' && cV3 <= '~' )cS3 = cCount;//第四字节cV4 = cCount^((nSrc&0xff));if ( 0 == cS4 &&cV4 >= '0' && cV4 <= '~' )cS4 = cCount;if ( 0 != cS1 && 0 != cS2 && 0 != cS3 && 0 != cS4 ){szOUT[0] = cS1;szOUT[1] = cS2;szOUT[2] = cS3;szOUT[3] = cS4;szOUT[4] = cV4;szOUT[5] = cV3;szOUT[6] = cV2;szOUT[7] = cV1;return true;}++cCount;}return false;
}bool CalculateKeyStep2( DWORD nTempNum1, DWORD nTempNum2, const char *pStrName, char *szLast8BytesSerials )
{if ( NULL == szLast8BytesSerials || NULL == pStrName )return false;DWORD nMagicNum = 0xfedcba98;UINT  uCount = 0xff01;Func( nTempNum1, nTempNum2, uCount,nMagicNum );if ( 1 == uCount ){nTempNum1 = *(DWORD*)(pStrName+8);nTempNum2 = *(DWORD*)(pStrName+ 0xc);nTempNum1 ^= nTempNum2; nTempNum1 ^= nMagicNum;nTempNum1 |= 0x40404040;nTempNum1 &= 0x77777777;return GenerateCorrect8BytesSerial( nTempNum1, szLast8BytesSerials );}return false;
}void GenerateNBytesStrings( const char *pStrUserName, char *pStr, const UINT nBytes )
{assert( NULL != pStr );if( NULL ==  pStr ) return;while ( pStr[nBytes-1] <= '~' ){////Add codes for what you want to deal withDWORD nT1(0), nT2(0);CalculateKeyStep1( pStrUserName, pStr, nT1, nT2 );char szLast8BytesSerials[9] = { 0 };if ( CalculateKeyStep2( nT1, nT2, pStrUserName, szLast8BytesSerials ) ){printf("%s's Serial is: %s%s\n", pStrUserName, pStr, szLast8BytesSerials );}//++pStr[0];for(UINT idx=0; idx<nBytes && pStr[nBytes-1] <= '~'; ++idx){if( pStr[idx] > '~' ){pStr[idx] = '0';pStr[idx+1]++;}}}
}int _tmain(int argc, _TCHAR* argv[])
{const char szHint0[] = "***********This is the KeyGen for CycleCrackMe***********";const char szHint1[] = "Please Input your name:";std::cout << szHint0 << std::endl;std::cout << szHint1 << std::endl;char szUserName[32] = { 0 };std::cin >> szUserName;std::cout << "Please Waiting......" << std::endl;ExtendNameTo16Bytes( (char*)szUserName );char szSerialFirst8Bytes[] = "00000000";GenerateNBytesStrings( szUserName, szSerialFirst8Bytes, 8 );system("pause");return 0;
}

测试结果如下:

此次仅仅是利用了注册算法的进行的部分逆运算获取的注册码,且在程序实现过程中没有达成对所有存在的注册码的穷举,因此,需要对注册机程序做进一步的修改,使之达到既定的要求。

To be continued.......

逆向序列号生成算法(三)相关推荐

  1. snowflake算法 php,Snowflake —— 分布式全局唯一 id 生成算法

    简介 Snowflake 是 Twitter 提出一种的分布式唯一序列号生成算法,理论上单节点 1 毫秒可以生成 4096 个(每秒四百万个)唯一序列,这个序列是个 long 类型的数字,在数据库中的 ...

  2. 第十六章-序列号生成算法分析-Part1

    步骤: 1.查看CrackMe程序信息 2.使用OllyDbg加载CrackMe程序 3.GetDlgItemTextA函数设置一个内存断点 4.读取输入的序列号的位置 5.输入的序列号的值 6.输入 ...

  3. 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

    1.点评 对于IM系统来说,如何做到IM聊天消息离线差异拉取(差异拉取是为了节省流量).消息多端同步.消息顺序保证等,是典型的IM技术难点. 就像即时通讯网整理的以下IM开发干货系列一样: <I ...

  4. 实验三:实现一个大素数生成算法

    一.实验内容 掌进一步掌握大素数分解的一般原理和实现方法.能用间接方法实现大素数分解.用代码实现Solovay-Strassen素性测试法或Miller-Rabin素性测试法. 二.分实现一个大素数生 ...

  5. 某酷ckey签名生成算法系列--(三)ast代码控制流平坦化

    某酷ckey签名生成算法系列--(三)ast代码控制流平坦化 观察三个switch的值分别是Ci.mi和Ai.而这三个值又因为li的确定而确定的.也就是说已知li的值,就可以分别计算出Ci.mi和Ai ...

  6. mysql序列号生成_忘掉 Snowflake,感受一下性能高出587倍的全局唯一ID生成算法

    今天我们来拆解 Snowflake 算法,同时领略百度.美团.腾讯等大厂在全局唯一 ID 服务方面做的设计,接着根据具体需求设计一款全新的全局唯一 ID 生成算法.这还不够,我们会讨论到全局唯一 ID ...

  7. spwm控制算法c语言实现,三种SPWM波形生成算法的分析与实现

    标签: SPWM SPWM(Sinusoidal PWM)法是一种比较成熟的,目前使用较广泛的PWM法.前面提到的采样控制理论中的一个重要结论:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果 ...

  8. 使用深度学习检测DGA(域名生成算法)——LSTM的输入数据本质上还是词袋模型...

    from:http://www.freebuf.com/articles/network/139697.html DGA(域名生成算法)是一种利用随机字符来生成C&C域名,从而逃避域名黑名单检 ...

  9. 微信用户全局唯一标识_分布式系统的唯一ID生成算法对比

    在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识. 那么如何实现全局唯一id呢?有以下几种方案. (1)方案一:独立数据库自增id 这个方案就是说你的系统每次要生成一个id,都是往一个独立库 ...

  10. java怎样生成32位全是整形的主键_你肯定会需要的分布式Id生成算法雪花算法(Java)...

    最近公司正好在做数据库迁移从oracle到mysql,因为之前oracle主键是使用的 SYS_GUID() 这个oracle提供的函数来生成全球唯一的标识符(原始值)由16个字节组成. 不过由于my ...

最新文章

  1. mysql如何查看事务日记_MySQL中的几种日志了解
  2. java 操作mysql数据库得到错误码_[数据库/Java]数据库开发过程中产生的MySQL错误代码及其解决方案...
  3. Spring3.0 AOP 具体解释
  4. Java中多态(向上转型)机制
  5. 使用SQLPLUS生成HTML报表
  6. SAP ERP classification和C4C的同步
  7. 数据库怎么看是什么编码_离婚了怎么发朋友圈?看你喜欢什么类型
  8. I - The Mad Mathematician FZU - 2042(未解决)
  9. javascript学习系列(12):数组中的join方法
  10. 漫话:如何给女朋友解释String对象是不可变的?
  11. FPGA RAM存储器设计
  12. NB-IOT(4)---移远NB-IOT BC26模块模组简介和实际应用方向详解
  13. 董事长亲自“翻墙偷拍”后,身价暴涨3000万
  14. 第12章类和动态内存分配
  15. 21天Jmeter打卡Day13 逻辑控制器之if和简单控制
  16. python从入门到精通 明日科技 电子书-python从入门到项目实践 (明日科技) 配套视频教程+源码...
  17. go struct 零值_《Go 语言程序设计》读书笔记 (五) 协程与通道
  18. 快速傅里叶变换python_图像傅里叶变换快速实现 python
  19. linux ps auxf,ps -aux命令详解
  20. 周六见 | PyCon China 2022 上海站就等你啦!

热门文章

  1. ping网络通不通、ttl讲解、查询ip归属地、tracert路由分析诊断
  2. jmeter生成接口测试报告
  3. Linux之磁盘与文件系统管理
  4. python遍历文件夹循环处理图片
  5. 区块链Baas平台纳管实战
  6. android usb 读写权限,Android默认给予USB读写权限,去掉受权对话框
  7. linux服务器安装虚拟桌面,并可视化连接远程桌面
  8. 数据清洗之 数据整理
  9. php 音频转换 WAV转MP3
  10. 一图读懂 | 亿美软通富媒体消息助力营销价值提升