沈阳电脑维修上门服务13889116605: 最近挺忙的,本来需要各种为前路做准备,无奈自己天生属于低血压型的人,偏偏就是提不起劲儿来干正事儿,却把大好的一天光阴全交代到SRS的分析工作上了。虽然说这多少算不务正业,不过由于本人有...
最近挺忙的,本来需要各种为前路做准备,无奈自己天生属于低血压型的人,偏偏就是提不起劲儿来干正事儿,却把大好的一天光阴全交代到SRS的分析工作上了。虽然说这多少算不务正业,不过由于本人有着严重的软件新版本强迫症,故这算是给自己的一个开脱理由吧。
这款软件的破解工作展开比较容易,没有加壳,直接上工具分析之。首先请出OllyDBG来,通过查找字符串引用和查找APIMessageBox引用的老法子,很容易就定位了大概的关键代码位置。
找到关键位置后,顺手又记录了一下函数调用栈,然后打开了神器IDA。用IDA载入后,跳转到之前找到的关键代码RVA处,着手分析。说起来,我自己喜欢将动态和静态调试方法结合起来用,不如果不愿意太费神分析汇编代码,有时候直接看运行时结果便是最直观和省时的。载入后发觉之前判断的关键点实际上是CWinApp::ShowAppMessageBox函数,继续沿着刚才记录的函数运行时调用栈往上找,依次顺藤摸瓜:AfxMessageBox--->Sub44D240,这才到了SRS程序的代码领空,于是正是着手分析。(IDA这个静态分析功能实在是强大,它可以根据二进制代码特征判断出其是否是库函数代码。这大大节省了我的时间,应为对这类代码我只需要看SDK文档就行了,而不需要去分析其实际的代码行为)
进一步根据线索判断(Sub44D240里包含了SendMessage、AfxMessageBox、GetWindowText等函数,足以说明该函数的关键作用),我锁定在了Sub44D240函数的Sub4430F0调用上,继续追踪到Sub442FF0上,继而是Sub44B9E0过程,最终到了Sub44BC30过程上。在该过程中,我同时追踪到了用户输入的ProductID以及SerialNumber等信息,这进一步是我确认了我的判断。
该过程具体代码如下:
.text:0044BC30
.text:0044BC30;===============SUBROUTINE=======================================
.text:0044BC30
.text:0044BC30;Attributes:bp-basedframe
.text:0044BC30
.text:0044BC30check_PID_SNprocnear;CODEXREF:realCheckSN+182p
.text:0044BC30
.text:0044BC30productId_segs_temp=dwordptr-14h
.text:0044BC30new_ProdectID=dwordptr-10h
.text:0044BC30sn_lowerDWord=dwordptr-4
.text:0044BC30SN=dwordptr8
.text:0044BC30
.text:0044BC30pushebp
.text:0044BC31movebp,esp
.text:0044BC33andesp,0FFFFFFF8h
.text:0044BC36subesp,14h
.text:0044BC39pushebx
.text:0044BC3Apushesi
.text:0044BC3Bpushedi
.text:0044BC3Cpush14h;unsignedint
.text:0044BC3Emovebx,eax;ebx指向ProductID首位置
.text:0044BC40callj_??2@YAPAXI@Z;operatornew(uint)
.text:0044BC45movesi,eax
.text:0044BC47leaeax,[esi+6]
.text:0044BC4Apusheax
.text:0044BC4Bmoveax,[ebp+SN]
.text:0044BC4Eleaecx,[esi+4]
.text:0044BC51pushecx
.text:0044BC52leaedx,[esi+2]
.text:0044BC55pushedx
.text:0044BC56pushesi;新申请的14h的空间
.text:0044BC57pushoffseta4x4x4x4x;"%4x-%4x-%4x-%4x"
.text:0044BC5Cpusheax;SN首地址
.text:0044BC5DcallString2Integer;将字符串SN转换为数组,比如
.text:0044BC5D;“E6E9-24CB-2968-09BC”变为
.text:0044BC5D;E9E6...
.text:0044BC62movecx,[esi+4];ECX为后半部分SN
.text:0044BC65movedi,[esi];EDI为前半部分SN
.text:0044BC67pushesi;void*
.text:0044BC68mov[esp+40h+sn_lowerDWord],ecx
.text:0044BC6Ccallj__free
.text:0044BC71leaedx,[esp+40h+productId_segs_temp]
.text:0044BC75pushedx
.text:0044BC76pushoffseta4x;"%4x"
.text:0044BC7Bpushebx
.text:0044BC7CcallString2Integer;将ProductID第一段转换为Word
.text:0044BC81movesi,[esp+4Ch+productId_segs_temp];esi储存productId_1
.text:0044BC85leaeax,[esp+4Ch+productId_segs_temp]
.text:0044BC89pusheax
.text:0044BC8Aleaecx,[ebx+0Ah]
.text:0044BC8Dpushoffsetasc_48C804;"%x"
.text:0044BC92pushecx
.text:0044BC93callString2Integer
.text:0044BC98movedx,[esp+58h+productId_segs_temp];EDX储存ProductId_2
.text:0044BC9Caddesp,38h
.text:0044BC9Fxoreax,eax
.text:0044BCA1pusheax
.text:0044BCA2pushesi
.text:0044BCA3pusheax
.text:0044BCA4pushedx
.text:0044BCA5call__allmul
.text:0044BCAAmovesi,eax;product_1*product_2乘积的低word放入esi
.text:0044BCACleaeax,[esp+20h+productId_segs_temp]
.text:0044BCB0pusheax
.text:0044BCB1leaecx,[ebx+14h]
.text:0044BCB4pushoffsetasc_48C804;"%x"
.text:0044BCB9pushecx
.text:0044BCBAmov[esp+2Ch+new_ProdectID+4],edx
.text:0044BCBEcallString2Integer
.text:0044BCC3movedx,[esp+2Ch+new_ProdectID+4]
.text:0044BCC7moveax,[esp+2Ch+productId_segs_temp];eax保存productId_3
.text:0044BCCBaddesp,0Ch
.text:0044BCCEpushedx
.text:0044BCCFpushesi
.text:0044BCD0push0
.text:0044BCD2pusheax
.text:0044BCD3call__allmul
.text:0044BCD8leaecx,[esp+20h+productId_segs_temp]
.text:0044BCDCpushecx
.text:0044BCDDpushoffsetasc_48C804;"%x"
.text:0044BCE2addebx,1Eh
.text:0044BCE5pushebx
.text:0044BCE6movesi,eax
.text:0044BCE8mov[esp+2Ch+new_ProdectID+4],edx
.text:0044BCECcallString2Integer
.text:0044BCF1movedx,[esp+2Ch+new_ProdectID+4]
.text:0044BCF5moveax,[esp+2Ch+productId_segs_temp]
.text:0044BCF9addesp,0Ch
.text:0044BCFCpushedx
.text:0044BCFDpushesi
.text:0044BCFEpush0
.text:0044BD00pusheax
.text:0044BD01call__allmul;结果edx高位,eax低位
.text:0044BD06movesi,edx
.text:0044BD08shresi,10h
.text:0044BD0Bmov[esp+20h+new_ProdectID],eax
.text:0044BD0Fmov[esp+20h+new_ProdectID+4],edx
.text:0044BD13xorebx,ebx
.text:0044BD15movcl,10h
.text:0044BD17call__allshr;{edx,eax}==new_prodectID>>10h
.text:0044BD1Cmovecx,[esp+20h+new_ProdectID+4]
.text:0044BD20xoredx,edx
.text:0044BD22push1
.text:0044BD24andeax,0FFFF0000h
.text:0044BD29pushedx
.text:0044BD2Aaddesi,eax
.text:0044BD2Cadcebx,edx
.text:0044BD2Emovedx,[esp+28h+new_ProdectID]
.text:0044BD32pushecx
.text:0044BD33pushedx
.text:0044BD34call__allmul;eax=0,edx为new_productID的低位
.text:0044BD39push0
.text:0044BD3Baddesi,eax
.text:0044BD3Dpush8475h
.text:0044BD42adcebx,edx
.text:0044BD44pushebx
.text:0044BD45pushesi
.text:0044BD46call__alldiv;{edx,eax}=={edx,esi}/0x8475
.text:0044BD4Bpush0
.text:0044BD4Dpush0AE6000h
.text:0044BD52pushedx
.text:0044BD53pusheax
.text:0044BD54call__allmul
.text:0044BD59addeax,91F2884Dh
.text:0044BD5Eadcedx,2DCh
.text:0044BD64movcl,0Ah
.text:0044BD66call__allshr
.text:0044BD6Bpush0
.text:0044BD6Dpush2046h
.text:0044BD72pushedx
.text:0044BD73pusheax
.text:0044BD74call__allmul
.text:0044BD79movecx,0FFFFFFFEh
.text:0044BD7Esubecx,eax
.text:0044BD80moveax,0FFFFFFFFh
.text:0044BD85sbbeax,edx
.text:0044BD87movedx,[esp+20h+sn_lowerDWord]
.text:0044BD8Bshldedx,edi,1
.text:0044BD8Faddedi,edi
.text:0044BD91cmpedi,ecx
.text:0044BD93jnzshortloc_44BDA5
.text:0044BD95cmpedx,eax
.text:0044BD97jnzshortloc_44BDA5
.text:0044BD99moveax,1
.text:0044BD9Epopedi
.text:0044BD9Fpopesi
.text:0044BDA0popebx
.text:0044BDA1movesp,ebp
.text:0044BDA3popebp
.text:0044BDA4retn
.text:0044BDA5;---------------------------------------------------------------------------
.text:0044BDA5
.text:0044BDA5loc_44BDA5:;CODEXREF:check_PID_SN+163j
.text:0044BDA5;check_PID_SN+167j
.text:0044BDA5popedi
.text:0044BDA6popesi
.text:0044BDA7xoreax,eax
.text:0044BDA9popebx
.text:0044BDAAmovesp,ebp
.text:0044BDACpopebp
.text:0044BDADretn
.text:0044BDADcheck_PID_SNendp
基本思路
通过分析,我了解到,其算法大概思路如下:首先,注册流程有效输入为SerialNumber和ProductID,RegistrationNo实际上没有参与注册的验证计算过程。SRS通过系统各种信息生成ProductID,然后用户需要提供与ProductID匹配的SerialNumber方能注册。当然,SerialNumber是需要你拿美刀换的。
简而言之,SRS将ProductID作fp变换,得到一个64bit长的整数,并将用户输入的序列号做fs变换同样得到一个64bit长整数。为了使注册成功,需要满足:
fp(gen())=fs(SerialNumber)成立 (1)
这其中,gen()的算法我们不需要管,因为其结果在界面ProductID框中已经显示了。我们需要找到fp和fs的实现算法,并顺利推出fs-1的实现。从而:
SerialNumber=fs-1(fp(ProductID)) (2)
ProductID变换算法描述
我们首先描述fp的实现。fp基本上是由我不知道原理的各种数值变换组成,为了精确表述,我直接用C语言描述:
ProductID变换函数描述
__int64getProductID(char*id){
__int64temp=1;
DWORDelem;
for(inti=0;i<4;i++){
sscanf(id+(i*5),"%x",&elem);
temp*=elem;
}
//高地位变换
DWORDlowerDWord=temp;
DWORDupperDWord=temp>>0x20;
WORDlower=upperDWord;
WORDupper=upperDWord>>0x10;
upperDWord=lower;
upperDWord<<=0x10;
upperDWord|=upper;
temp=lowerDWord;
temp<<=0x20;
temp|=upperDWord;
temp/=0x8475;
temp*=0xAE6000;
temp+=0x2DC91F2884D;
temp>>=0xA;
temp*=0x2046;
temp=0xfffffffffffffffe-temp;
returntemp;
}
SerialNumber变换算法描述
接下来描述fs函数的实现。我们称变换后的ProductID为TransPID,并且LowerDW和HighDW表示一个64bit整数的低双字和高双字,shld表示对应汇编指令的函数,TransSerialNumberHighDW和TransSerialNumberLowDW分别表示变换后的序列号高双字和低双字。则有:
TransSerialNumberHighDW(SerialNumber)= shld(UpperDW(SerialNumber),LowerDW(SerialNumber),1); (3)
TransSerialNumberLowDW(SerialNumber)= LowerDW(SerialNumber)+ LowerDW(SerialNumber); (4)
fs(SerialNumber)= TransSerialNumberHighDW<<32|TransSerialNumberLowDW; (5)
SerialNumber逆向变换算法描述
了解了fs的实现,我们接下来需要着手研究实现fs-1的思路。由于fs高双字和低双字分别由不同的方式变换的(3)、(4),所以我们需要分别求出SerialNumber的高低双字。得到SerialNumber的低双字很简单,由(4)可知,我们只要将LowerDW(fp(ProductID))除以2就行。但需注意的是,整个双字值域中,LowerDW(fp(ProductID))/2+(2<<31)同样能满足条件(由于溢出导致的相等),www.it165.net这点很重要!
再来考虑高位的算法。我们需要把HighDW(fp(ProductID))往右移一位,这是左边补入的一位可以任意。这时需要注意,考虑到shld性质,有以下两种情况:
①如果HighDW(fp(ProductID))最低位是1则需要LowerDW(SerialNumber)的最高位为1
②如果HighDW(fp(ProductID))最低位是0则需要LowerDW(SerialNumber)的最高位为0
考虑到LowerDW(fp(ProductID))/2和LowerDW(fp(ProductID))/2+(2<<31)均可满足条件,若是情况①则选取后者,因为此时可保证LowerDW(SerialNumber)的最高位为1。同理,若是情况②则需选择前者。
该算法的C语言描述如下:
voidgetSerialNumber(__int64productId,char*buffer){
DWORDupper=productId>>0x20;
DWORDlower=productId;
assert((lower&1)==0);//lower=snLower+snLower因此lower必须为偶数
DWORDsnUpper=upper>>1;
DWORDsnLower=0;
if((upper&1)==1){
snLower=lower/2;
snLower+=(1<<31);
}else{
snLower=lower/2;
}
DWORDLONGsn=snUpper;
sn<<=0x20;
sn|=snLower;
WORD*p=(WORD*)&sn;
sprintf(buffer,"%04x-%04x-%04x-%04x",p[0],p[1],p[2],p[3]);
}
小结
IDA很强大,Crack很费时间,收获的免费SRS使用权和投入的大量时间不成正比。结论:以后还是尽量少搞Crack吧。
上一篇:用于保护应用程序安全的DataSecure解决方案