逆向序列号生成算法(三)
?接著上次的分析,繼續(xù)分析下去,對(duì)于這個(gè)CrackMe,其要求是寫(xiě)出注冊(cè)機(jī),那么我們就根據(jù)其注冊(cè)碼的驗(yàn)證過(guò)程,進(jìn)行逆向分析,首先找到最關(guān)鍵的一個(gè)爆破點(diǎn),我們先看看這個(gè)爆破點(diǎn)的位置,如圖1可見(jiàn)最后的驗(yàn)證關(guān)鍵點(diǎn)其實(shí)在那個(gè)MagicNum的值上面,因?yàn)橛脩?hù)名是我們給定的,屬于已知值,而序列號(hào)則是可以由我們構(gòu)造的也可以認(rèn)為是已知值,唯一不確定的就是那個(gè)MagicNum了,而這個(gè)值卻跟我們輸入的用戶(hù)名及序列號(hào)密切相關(guān)。因此切實(shí)找到:MagicNum(魔數(shù))如圖2與用戶(hù)名、序列號(hào)之間的關(guān)系就是最后寫(xiě)出注冊(cè)機(jī)的關(guān)鍵所在了。
接下來(lái)我們大致看看這三者之間的關(guān)系 :最后的注冊(cè)碼正確與否取決于序列號(hào)的最后四個(gè)字符,即:我們?cè)O(shè)dword ptr[402168]為a,
dword ptr[40216c]為b,dword ptr[402179]為c,需要算出的最后四個(gè)字節(jié)為nResult,其必須滿(mǎn)足,nResult的值必須都在A(yíng)SCII碼可打印表示范圍內(nèi)(PS:不然你無(wú)法輸入對(duì)應(yīng)的字符......),且nResult == a^b^MagicNum|0x40404040&0x77777777^c。其中的a\b\c皆可由我們構(gòu)造,而MagciNum則與我們構(gòu)造的這些數(shù)相關(guān),到底有些什么關(guān)系呢?我們接下來(lái)看看MagicNum與我們構(gòu)造的用戶(hù)名及序列號(hào)之間的關(guān)系。
(圖2)
其中的edi可以是用戶(hù)名算出值亦可為序列號(hào)算出值。(此值即為Step1過(guò)程中算出的值),整個(gè)遞歸過(guò)程與edi值有關(guān)。通過(guò)閱讀程序,我們需要找到edi與遞歸次數(shù)之間的直接關(guān)系,這樣我們可以想辦控制魔數(shù)的自增次數(shù)從而控制住魔數(shù)的值。通過(guò)在給定用戶(hù)名的情況下,利用edi與魔數(shù)之間的關(guān)系,構(gòu)造出滿(mǎn)足條件的序列號(hào)(這個(gè)過(guò)程可以控制在可容忍范圍內(nèi)的窮舉)。在分析的過(guò)程中比較棘手的地方是就是遞歸調(diào)用。OK,看到這里基本定位了關(guān)鍵點(diǎn)所在。我們先暫停一下,僅從分析如何快速制作出可行的注冊(cè)機(jī)的角度來(lái)嘗試一下,在這個(gè)角度上,首先要保證遞歸函數(shù)調(diào)用后的返回值ECX為1(這個(gè)時(shí)候關(guān)于遞歸與魔數(shù)的關(guān)系就可以不用考慮了),把這個(gè)條件分析出來(lái)。然后只用去利用:nResult == a^b^MagicNum|0x40404040&0x77777777這個(gè)值,自己去構(gòu)造符合條件的剩余的序列號(hào)部分即可。例如:nResult值已知的情況下,設(shè)序列號(hào)剩下的兩個(gè)字節(jié)分別為X1、X2,要求nResult^X1==X2,且X1,X2都在['0','~']這個(gè)范圍內(nèi)。根據(jù)這個(gè)思路可以寫(xiě)出如下的注冊(cè)機(jī):
// 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;// //函數(shù)聲明部分 //// //擴(kuò)展用戶(hù)名到16字節(jié) void ExtendNameTo16Bytes( char *pStrName );//驗(yàn)證序列號(hào)Step1 void CalculateKeyStep1( const char *pStrName, const char *pStrSerialNum, DWORD &outTempNum1, DWORD &outTempNum2 );//抽離的遞歸函數(shù) void Func( DWORD nTempNum1, DWORD nTempNum2,UINT &uCount, DWORD &nMagicNum );//驗(yàn)證序列號(hào)Step2 bool CalculateKeyStep2( DWORD nTempNum1, DWORD nTempNum2, const char *pStrName, char *szLast8BytesSerials );//構(gòu)造最后的8字節(jié)序列號(hào) bool GenerateCorrect8BytesSerial( const DWORD nSrc, char szOUT[] );//N個(gè)字節(jié)字符串生成器 void GenerateNBytesStrings( const char *pStrUserName, char *pStr, const UINT nBytes );// //函數(shù)實(shí)現(xiàn)部分 // 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; //取最低一個(gè)字節(jié),根據(jù)下面程序的執(zhí)行if ( uCount > 8 ) //其實(shí)為取CL的低四位.{nValue = nTempNum2;uCount >>= 4; //取CL高4位.}//nValue為DWORD,占4個(gè)字節(jié),如下圖示://|---|---|---|---|//| 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 <= '~' ){//第一字節(jié)cV1 = cCount^((nSrc&0xff000000)>>24);if ( 0 == cS1 && cV1 >= '0' && cV1 <= '~' )cS1 = cCount;//第二字節(jié)cV2 = cCount^((nSrc&0xff0000)>>16);if ( 0 == cS2 &&cV2 >= '0' && cV2 <= '~' )cS2 = cCount;//第三字節(jié)cV3 = cCount^((nSrc&0xff00)>>8);if ( 0 == cS3 && cV3 >= '0' && cV3 <= '~' )cS3 = cCount;//第四字節(jié)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; }測(cè)試結(jié)果如下:
此次僅僅是利用了注冊(cè)算法的進(jìn)行的部分逆運(yùn)算獲取的注冊(cè)碼,且在程序?qū)崿F(xiàn)過(guò)程中沒(méi)有達(dá)成對(duì)所有存在的注冊(cè)碼的窮舉,因此,需要對(duì)注冊(cè)機(jī)程序做進(jìn)一步的修改,使之達(dá)到既定的要求。
To be continued.......
總結(jié)
以上是生活随笔為你收集整理的逆向序列号生成算法(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计时器操作--打点计时器
- 下一篇: 怎样用c语言编出旗子的图案,三色棋解法的