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]                    ; &nbsp  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
  }

原文地址:https://www.cnblogs.com/tk091/p/2456168.html