emu8086注册算法分析及KeyGen实现
from:http://www.2cto.com/Article/201204/127992.html
【文章作者】: NoAir
【软件名称】: emu8086 v4.08
【保护方式】: 有点戏剧性
【编写语言】: Visual Basic
【软件介绍】: 一款优秀的8086汇编IDE,支持可视化调试,内置FASM
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
仅以此文纪念一下emu8086,现在官网不能打开了,软件也不更新了,用emu8086编写16位汇编程序挺方便的,图形化的操作模式,支持调试,还有一些模板以及虚拟设备供操作。我把emu8086 v4.08和KeyGen及src都上传到了本地。
写完KeyGen之后才发现以前有位朋友发过一篇关于emu8086破解的文章,但是这不影响本文的分析,本文主要分析emu8086另外一种注册算法,有兴趣的朋友就当温习一下VB的逆向,走马观花一下吧。
如何找关键点,这个不用说了,直接搜索字符串,然后回溯就看得见了
下面步入正题,为什么说是戏剧性呢,看看下面的代码就知道:
005DA83A . test edx,edx
005DA83C . je short emu8086.005DA850 ; 戏剧性的je,如果前3位是112
005DA83E . mov dword ptr ss:[ebp-4],4
005DA845 . mov word ptr ss:[ebp-24],0FFFF ; 返回值为-1
005DA84B . jmp emu8086.005DAAA5 ; 注册成功
分析完戏剧性的代码后,下面开始分析另一种注册算法:
005DAA12 . mov dword ptr ss:[ebp-4],10
005DAA19 . mov ecx,dword ptr ss:[ebp+C]
005DAA1C . mov edx,dword ptr ds:[ecx]
005DAA1E . mov dword ptr ss:[ebp-64],edx
005DAA21 . mov dword ptr ss:[ebp-6C],8
005DAA28 . mov eax,dword ptr ss:[ebp+10]
005DAA2B . push eax
005DAA2C . mov ecx,dword ptr ss:[ebp+8] ; 用户名
005DAA2F . mov edx,dword ptr ds:[ecx]
005DAA31 . push edx
005DAA32 . lea eax,dword ptr ss:[ebp-3C]
005DAA35 . push eax
005DAA36 . call emu8086.005DAAF0 ; 注册码计算
上面有一行mov dword ptr ss:[ebp-4],10
Dim state as integer
state=10
作者代码中频频出现类似的赋值语句,估计是用于判断语句执行到哪一行了吧,类似的语句可以全部忽略了。
进入5DAAF0,
005DAAF0 $ push ebp
005DAAF1 . mov ebp,esp
005DAAF3 . sub esp,18
005DAAF6 . push <jmp.&MSVBVM60.__vbaExceptHandler> ; SE 处理程序安装
............................
005DAB35 . mov edx,dword ptr ss:[ebp+C]
005DAB38 . lea ecx,dword ptr ss:[ebp-38]
005DAB3B . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化name=用户名
............................
005DAB57 . mov edx,emu8086.00450CA4 ; abcdefghijklmnopqrstuvwxyz
005DAB5C . lea ecx,dword ptr ss:[ebp-5C]
005DAB5F . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化str1
005DAB6C . mov edx,emu8086.00450CE0 ; qw10pasdfghjklzxcvbnmertyu
005DAB71 . lea ecx,dword ptr ss:[ebp-28]
005DAB74 . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化str2
005DAB81 . mov edx,emu8086.00450D1C ; z9rtasdf01823asjfsd1234346gfhplmasdr613412qwerx
005DAB86 . lea ecx,dword ptr ss:[ebp-2C]
005DAB89 . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化str3
005DAB96 . mov edx,emu8086.00450D80 ; jkaserkkn837c3frtqzx
005DAB9B . lea ecx,dword ptr ss:[ebp-50]
005DAB9E . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化str4
005DABAB . mov edx,emu8086.004376EC
005DABB0 . lea ecx,dword ptr ss:[ebp-4C]
005DABB3 . call dword ptr ds:[<&MSVBVM60.__vbaStrCopy>] ; 初始化key为空字符串
...........................
005DABD9 . push ecx
005DABDA . lea edx,dword ptr ss:[ebp-6C]
005DABDD . push edx
005DABDE . call dword ptr ds:[<&MSVBVM60.rtcUpperCaseVar>] ; 用户名转换成大写,保存到[ebp-58]
...........................
005DABF0 . lea ecx,dword ptr ss:[ebp-58]
005DABF3 . call dword ptr ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
005DAC09 . lea ecx,dword ptr ss:[ebp-58] ; FINSOS
005DAC0C . push ecx
005DAC0D . call emu8086.00548830 ; 用户名翻转
{
00548875 . push eax ; 用户名
00548876 . call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>] ; 得到用户名长度,用户名是用unicode编码的
0054887C . mov ecx,eax
0054887E . call dword ptr ds:[<&MSVBVM60.__vbaI2I4>] ; 用户名长度转换成long型
00548884 . mov esi,eax
00548886 > mov eax,1
0054888B . cmp si,ax
0054888E . jl short emu8086.005488F6
.......................................
005488B4 . call dword ptr ds:[<&MSVBVM60.rtcMidCharVar>];截取最后第i字符,i初始为用户名长度,循环自减
;esi--
005488F4 . jmp short emu8086.00548886
}
005DAC24 . mov word ptr ss:[ebp-24],1 ; i=1
005DAC31 . mov word ptr ss:[ebp-34],1 ; j=1
005DAC3E . mov word ptr ss:[ebp-54],0 ; flag 初始化0
005DAC4B . movsx esi,word ptr ss:[ebp-24] ; i 循环体
005DAC4F . mov edx,dword ptr ss:[ebp-58]
005DAC52 . push edx ; 用户名name
005DAC53 . call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>] ; __vbaLenBstr
005DAC59 . xor ebx,ebx
005DAC5B . cmp esi,eax
005DAC5D . setg bl
005DAC60 . movsx esi,word ptr ss:[ebp-34]
005DAC64 . mov eax,dword ptr ss:[ebp-50]
005DAC67 . push eax ; str4
005DAC68 . call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>] ; __vbaLenBstr
005DAC6E . xor ecx,ecx
005DAC70 . cmp esi,eax
005DAC72 . setg cl
005DAC75 . or ebx,ecx
005DAC77 . test ebx,ebx
005DAC79 . jnz emu8086.005DAF65 ; for (i=1;i<=user_len && j<=0x14;i++)
{
005DACCE . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar截取name中第i个字符
005DACEA . call dword ptr ds:[<&MSVBVM60.__vbaInStrVar>] ; 查找str1中索引位置u_index
005DACF0 . push eax
005DACF1 . call dword ptr ds:[<&MSVBVM60.__vbaI2Var>] ; MSVBVM60.__vbaI2Var
005DAD21 . jle emu8086.005DAF02 ; u_index小于0,说明没搜索到 因此不是字母
005DAD2E . cmp word ptr ss:[ebp-54],1 ; 判断flag是否为1
005DAD33 . jnz emu8086.005DADE4
;if flag==0
{
005DAD88 . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar截取str2中第u_index个字符
;接下来保存字符到key中,并修改flag为1
005DAE58 . mov edx,eax ; 第u_index个字符
005DAE5A . lea ecx,dword ptr ss:[ebp-4C]
005DAE5D . call dword ptr ds:[<&MSVBVM60.__vbaStrMove>] ; 保存key中
005DAE84 . mov word ptr ss:[ebp-54],1 ; flag 赋值为1
}
;else
{
........ . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar截取str3中第u_index个字符
;接下来保存字符到key中,并修改flag为0
}
005DAE91 . mov dx,word ptr ss:[ebp-34] ; j++
005DAE95 . add dx,1
005DAE9F . mov word ptr ss:[ebp-34],dx
005DAEAA . lea eax,dword ptr ss:[ebp-28]
005DAEAD . push eax
005DAEAE . call <emu8086.move> ; move(str2),move函数的功能是把字符串尾字符移动到首位
005DAEC5 . lea ecx,dword ptr ss:[ebp-2C]
005DAEC8 . push ecx
005DAEC9 . call <emu8086.move> ; move(str3)
005DAEE0 . cmp word ptr ss:[ebp-54],1 ; flag为1
005DAEE5 . jnz short emu8086.005DAF02
{
005DAEEE . lea edx,dword ptr ss:[ebp-28]
005DAEF1 . push edx
005DAEF2 . call <emu8086.move> ; move(str2)
}
005DAF09 . lea eax,dword ptr ss:[ebp-50]
005DAF0C . push eax
005DAF0D . call <emu8086.move> ; move(str4)
005DAF24 . movsx ecx,word ptr ss:[ebp-54]
005DAF28 . test ecx,ecx
005DAF2A . jnz short emu8086.005DAF47 ; flag为0
{
005DAF33 . lea edx,dword ptr ss:[ebp-50]
005DAF36 . push edx
005DAF37 . call <emu8086.move> ; move(str4)
}
005DAF4E . mov ax,word ptr ss:[ebp-24] ; i ++
005DAF52 . add ax,1
005DAF5C . mov word ptr ss:[ebp-24],ax
005DAF60 .^ jmp emu8086.005DAC44
;move函数(char* str)
{
005DB234 . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar截取str最后一个字符
005DB244 . mov edx,eax
005DB246 . lea ecx,dword ptr ss:[ebp-24]
005DB2AF . mov word ptr ss:[ebp-28],dx ; m++ 截取位置
005DB2B3 > mov ax,word ptr ss:[ebp-28]
005DB2B7 . cmp ax,word ptr ss:[ebp-94] ; str长度
005DB2BE . jg emu8086.005DB35A
{
005DB30A . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar循环向后移动字符
005DB355 .^ jmp emu8086.005DB29E
}
}
;主循环结束
005DAF6C . movsx esi,word ptr ss:[ebp-34] ; j
005DAF70 . mov ecx,dword ptr ss:[ebp-50] ; str4
005DAF73 . push ecx ; str4
005DAF74 . call dword ptr ds:[<&MSVBVM60.__vbaLenBstr>] ; 得到str4长度
005DAF7A . cmp esi,eax ; 判断是否大于j
005DAF7C . jg emu8086.005DB039
{
005DAFD1 . call dword ptr ds:[<&MSVBVM60.#632>] ; rtcMidCharVar
005DAFF8 . lea ecx,dword ptr ss:[ebp-4C] ; 4C是result?
005DAFFB . call dword ptr ds:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove
005DB022 . mov dx,word ptr ss:[ebp-34]
005DB026 . add dx,1 ; j++
005DB030 . mov word ptr ss:[ebp-34],dx
005DB034 .^jmp emu8086.005DAF65 ; 循环填充key
}
005DB040 . mov eax,dword ptr ss:[ebp+10] ; g_count是0x64
005DB043 . mov cx,word ptr ds:[eax]
005DB046 . sub cx,1 ; g_count-1得到0x63
005DB050 . mov word ptr ss:[ebp-C4],cx
005DB057 . mov word ptr ss:[ebp-C0],1
005DB060 . mov word ptr ss:[ebp-24],1
005DB066 . jmp short emu8086.005DB07D
005DB068 > mov dx,word ptr ss:[ebp-24]
005DB06C . add dx,word ptr ss:[ebp-C0] ; i++
005DB079 . mov word ptr ss:[ebp-24],dx
005DB07D > mov ax,word ptr ss:[ebp-24]
005DB081 . cmp ax,word ptr ss:[ebp-C4] ;   005DB088 . jg short emu8086.005DB0AE ; 循环0x63-i次
{
005DB08A . mov dword ptr ss:[ebp-4],29
005DB091 . lea ecx,dword ptr ss:[ebp-4C]
005DB094 . push ecx
005DB095 . call <emu8086.move> ; 循环move(key)
005DB09A . mov edx,eax
005DB0AC .^ jmp short emu8086.005DB068
}
最后得到key,下面是纯C代码:调用keygen(szName,szKey,g_count);
代码:
//move功能是把字符串尾字符移动到首位
void move(char* str){
char temp;
unsigned char len=0;
while(*(str+len)) len++;
temp=*(str+len-1);
while(len){
*(str+len-1)=*(str+len-2);
len--;
}
*str=temp;
}
void keygen(char* name,char* key,unsigned char g_count){
//初始化变量
char str1[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; //常量字符串,长度0x1A
char str2[]="QW10PASDFGHJKLZXCVBNMERTYU";
char str3[]="Z9RTASDF01823ASJFSD1234346GFHPLMASDR613412QWERX";
char str4[]="JKASERKKN837C3FRTQZX";
unsigned char i=1,j=1,user_len=-1,k=0; //user_len为name的长度
BOOL flag=0,gbk_flag=0; //flag为交替标志,gbk_flag用于GBK编码的处理
char u_char; //欲搜索的字符
short u_index=0; //索引位置
//用户名转换成大写
while (++user_len,*name) *name++=*name>=0x61&&*name<=0x7A?*name-0x20:*name;
//用户名翻转
for ((i=0,name-=user_len);i<(int)user_len/2;i++)
{
*(char*)(name+i)^=*(char*)(name+user_len-i-1);
*(char*)(name+user_len-i-1)^=*(char*)(name+i);
*(char*)(name+i)^=*(char*)(name+user_len-i-1);
}
//主循环
for (i=1;i<=user_len && j<=0x14;i++)
{
//在str1中搜索字符u_char,得到索引位置u_index
u_index=0;
u_char=*(name+i-1);
while(u_index<0x1A){
if (u_char==*(str1+u_index)){
break;
}
u_index++;
}
u_index++; //最后还要加1,因为初始为0
if (u_index<=0x1A){ //如果在str1中搜索到了u_char
if (flag==1){
*(key+k)=*(str2+u_index-1);
k++;
flag=0; //flag交替
}
else{ //等效于if flag==0
*(key+k)=*(str3+u_index-1);
k++;
flag=1; //flag交替
}
j++; //j为当前key的长度
move(str2);
move(str3);
}
//GBK中文处理,摆脱name为unicode编码的限制
if(u_char&0x80 && gbk_flag==0){gbk_flag=1;continue;}
gbk_flag=0;
if (flag==1){
move(str2);
}
move(str4);
if (flag==0)
{
move(str4);
}
}
//如果key的长度小于0x14,则用str4补齐,直到j大于0x14
do
{
if (j>0x14){
break;
}
*(key+j-1)=*(str4+j-1);
j++;
} while(j<=0x14);
i=0;
g_count--;
//如果name长度小于等于g_count-1,则循环移动key,直到i大于g_count-1
do
{
if (i>(g_count-1)){
break;
}
move(key);
i++;
} while(i<=g_count); //其中的0x63是g_count-1
}