一个.net KeygenMe简单分析

一个.net KeygenMe简单分析

【文章标题】: foxabu的KeygenMe1的简单分析
【文章作者】: hawking
【作者邮箱】: rich_hawking@hotmail.com
软件名称】: NativeClrMixedKeygenMe.exe
【软件大小】: 52.0k
下载地址】: http://bbs.pediy.com/showthread.php?s=&threadid=40887
【编写语言】: .net
【使用工具】: reflector PEBrowseDbg PEiD
【操作平台】: win2k sp4
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  foxabu兄台的这个KeygenMe没有加壳,甚至都没有作混淆处理,算法其实也超简单。正好适合我这样喜欢.net的菜鸟把玩。
  一、静态分析
  用reflector打开,很容易就定位到注册按钮的代码
  private unsafe void button_tryreg_Click(object sender, EventArgs e)
  {
      $ArrayType$$$BY0PP@D e$$$bypp@d;
      string s = this.textBox_reg.Text;
      string text = this.textBox_machineCode.Text;
      lpMachineCode = (sbyte modopt(IsSignUnspecifiedByte)*) Marshal.StringToHGlobalAnsi(text).ToPointer();
      int num2 = text.Length / 2;
      if ((s.Length >= num2) && native_decode1((sbyte modopt(IsSignUnspecifiedByte)*) Marshal.StringToHGlobalAnsi(s).ToPointer(), (sbyte modopt(IsSignUnspecifiedByte)*) &e$$$bypp@d, 0xff))
      {
          MessageBox.Show("\u6ce8\u518c\u6210\u529f^_^");
          this.button_tryreg.Enabled = false;
      }
      else
      {
          MessageBox.Show("\u597d\u50cf\u5931\u8d25\u4e86\u54e6,\u7ee7\u7eed\u54e6~\u5176\u5b9e\u5f88\u7b80\u5355\u7684");
      }
  }
  从上面这段代码可以看出,注册码的长度必须大于等于机器码长度的一半。而且程序会把取到的注册码和一个Array数组及0xFF作为参数,传递给一个native方法native_decode1。
  [return: MarshalAs(UnmanagedType.U1)]
  [PreserveSig, MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType=MethodCodeType.Native), SuppressUnmanagedCodeSecurity]
  public static unsafe bool modopt(CallConvCdecl) native_decode1(sbyte modopt(IsSignUnspecifiedByte)*, sbyte modopt(IsSignUnspecifiedByte)*, int);
  可以看出这个native方法会返回一个bool值,从名称看是解码用的。要注册成功,这里必须得返回true。但是这个是native方法,所以reflector分析不出来这个方法具体做了些什么。
  
  再看看reflector里还有没有什么有用的信息。
  private void FormMain_Load(object sender, EventArgs e)
  {
      this.textBox_machineCode.Text = GetmachineCode();
  }
  
  internal static unsafe string GetmachineCode()
  {
      sbyte modopt(IsSignUnspecifiedByte)* numPtr2 = (sbyte modopt(IsSignUnspecifiedByte)*) Marshal.StringToHGlobalAnsi(Environment.MachineName + Environment.UserName).ToPointer();
      sbyte modopt(IsSignUnspecifiedByte)* numPtr = (sbyte modopt(IsSignUnspecifiedByte)*) Marshal.AllocHGlobal(0xff).ToPointer();
      native_encode(numPtr2, numPtr, 0xff);
      IntPtr pUnk = new IntPtr(numPtr2);
      Marshal.Release(pUnk);
      IntPtr ptr = new IntPtr(numPtr);
      return Marshal.PtrToStringAnsi(ptr);
  }
  
  [PreserveSig, MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType=MethodCodeType.Native), SuppressUnmanagedCodeSecurity]
  public static unsafe void modopt(CallConvCdecl) native_encode(sbyte modopt(IsSignUnspecifiedByte)*, sbyte modopt(IsSignUnspecifiedByte)*, int);
   
  这里我们可以看出机器码其实是将系统环境变量中的MachineName和UserName组合起来,再经由native方法native_encode编码得到的。
   
  [return: MarshalAs(UnmanagedType.U1)]
  internal static unsafe bool modopt(CallConvCdecl) decodeAndCompare(sbyte modopt(IsSignUnspecifiedByte)* str)
  {
      int num2 = stackalloc byte[__CxxQueryExceptionSize()];
      IntPtr ptr2 = new IntPtr(lpMachineCode);
      string text2 = Marshal.PtrToStringAnsi(ptr2);
      IntPtr ptr = new IntPtr(str);
      string s = Marshal.PtrToStringAnsi(ptr);
      try
      {
          s = Encoding.ASCII.GetString(Convert.FromBase64String(s), 0, Convert.FromBase64String(s).Length);
      }
      catch when (?)
      {
          uint num = 0;
          __CxxRegisterExceptionObject((void*) Marshal.GetExceptionPointers(), (void*) num2);
          try
          {
              try
              {
                  return false;
              }
              catch when (?)
              {
              }
              if (num != 0)
              {
                  throw;
              }
          }
          finally
          {
              __CxxUnregisterExceptionObject((void*) num2, (int) num);
          }
      }
      return ((s == text2) ? ((bool modopt(CallConvCdecl)) true) : ((bool modopt(CallConvCdecl)) false));
  }
  
  还有上面这个方法,从名称看是用于解码并比较的。具体作用就是将传入的参数s看作Base64String,通过Convert.FromBase64String转换成byte数组,然后再通过Encoding.ASCII编码成ASCII字符串。并将得到的这个字符串和机器码作比较,相同则返回true。
  刚开始我就是因为没好好在reflector中找找有用的信息,忽略了这个方法,所以白花了不少冤枉功夫。
  
  二、动态调试
  用OD调试.net程序是一件很痛苦的事,这里我们使用PEBrowse进行动态调试。
  打开PEBrowse,载入NativeClrMixedKeygenMe.exe。不断F5并忽略碰到的异常,直到程序完全跑起来。
  菜单中选择View-->Modules Only,使左侧的树型控件只显示Modules。
  依次展开左侧的模块列表NativeClrMixedKeygenMe.exe--->.NET Methods--->NativeClrMixedKeygenMe.FormMain
  在button_tryreg_Click方法上下断点。输入注册码1234567890,点注册按钮
  Disassembly of JITTED NativeClrMixedKeygenMe.FormMain::button_tryreg_Click (06000078) at 0x04AEB9C8
    ; Stack Size (in BYTES): 288 (0x00000120)
    ; Number of Parameters: 1
    ; Local Variables Size (in BYTES): 272 (0x00000110)
    ; Prologue Size (in BYTES): 35 (0x23)
    ; Standard Frame
    0x4AEB9C8: 6A00                 PUSH        0x0   断在这里              
    0x4AEB9CA: 6A00                 PUSH        0x0                 
    0x4AEB9CC: 6A00                 PUSH        0x0                 
    0x4AEB9CE: 68107A0C04           PUSH        0x40C7A10           
    0x4AEB9D3: E8D856510B           CALL        0x100010B0         
    0x4AEB9D8: 55                   PUSH        EBP                 
    0x4AEB9D9: 8BEC                 MOV         EBP,ESP            
    0x4AEB9DB: 57                   PUSH        EDI                 
    0x4AEB9DC: 56                   PUSH        ESI                 
    0x4AEB9DD: 53                   PUSH        EBX                 
    0x4AEB9DE: 81EC10010000         SUB         ESP,0x110           
    0x4AEB9E4: C745F0C7C6C8C7       MOV         DWORD PTR [EBP-0x10],0xC7C8C6C7; VAR:0x10
    ; end of prologue
    0x4AEB9EB: 8BF1                 MOV         ESI,ECX            
    ; IL_0000: ldarg.0
    ; IL_0001: ldfld textBox_reg
    ; IL_0006: callvirt  System.Windows.Forms.TextBox::get_Text()
    ; IL_000B: stloc.1
    ;...............................................................
    ;...............................................................
    ; IL_0046: ldloca.s 0x05
    ; IL_0048: call  System.IntPtr::ToPointer()
    ; IL_004D: ldloca.s 0x04
    ; IL_004F: ldc.i4 0x000000FF
    ; IL_0054: call native_decode1
    ; IL_0059: stloc.2
    0x4AEBA41: 68FF000000           PUSH        0xFF               
    0x4AEBA46: 8D95F0FEFFFF         LEA         EDX,[EBP-0x110]     ; VAR:0x110
    0x4AEBA4C: 8B8DECFEFFFF         MOV         ECX,DWORD PTR [EBP-0x114]; VAR:0x114
    0x4AEBA52: E8CD84F400           CALL        0x5A33F24                  ;一路F10来到这里,然后F11跟进           
    0x4AEBA57: 25FF000000           AND         EAX,0xFF            
    ; IL_005A: ldloc.2
    ; IL_005B: brfalse.s IL_0076
    0x4AEBA5C: 741B                 JZ          0x4AEBA79           ; (*+0x1D) 爆破的话修改这里
    ; IL_005D: ldstr ""
    ; IL_0062: call  System.Windows.Forms.MessageBox::Show()
    ; IL_0067: pop
  
  
  
  Disassembly of 0x004019D0 in NativeClrMixedKeygenMe.exe
    ; Section: .text
    0x4019D0: 6AFF                 PUSH        0xFF                 
    0x4019D2: 68D83C4000           PUSH        0x403CD8             ; .text:0x8B 0x54 0x24 0x08
    0x4019D7: 64A100000000         MOV         EAX,DWORD PTR FS:[0x0]
    0x4019DD: 50                   PUSH        EAX                  
    0x4019DE: 51                   PUSH        ECX                  
    0x4019DF: 53                   PUSH        EBX                  
    0x4019E0: 56                   PUSH        ESI                  
    0x4019E1: 57                   PUSH        EDI                  
    0x4019E2: A11CB04000           MOV         EAX,DWORD PTR [0x40B01C]; .data:0x4E 0xE6 0x40 0xBB
    0x4019E7: 33C4                 XOR         EAX,ESP              
    0x4019E9: 50                   PUSH        EAX                  
    0x4019EA: 8D442414             LEA         EAX,[ESP+0x14]      
    0x4019EE: 64A300000000         MOV         DWORD PTR FS:[0x0],EAX
    0x4019F4: 90                   NOP                              
    0x4019F5: 8B442424             MOV         EAX,DWORD PTR [ESP+0x24]
    0x4019F9: 50                   PUSH        EAX                  
    0x4019FA: 8D4C2414             LEA         ECX,[ESP+0x14]      
    0x4019FE: E81D010000           CALL        0x401B20                        ;将传入的注册码转换成string            
    0x401A03: 33DB                 XOR         EBX,EBX                       ;i = 0      
    0x401A05: 895C241C             MOV         DWORD PTR [ESP+0x1C],EBX
    0x401A09: 8D4C2410             LEA         ECX,[ESP+0x10]      
    0x401A0D: E8AE010000           CALL        0x401BC0                      ;取转换后的string的长度      
    0x401A12: 85C0                 TEST        EAX,EAX                     
    0x401A14: 8B742428             MOV         ESI,DWORD PTR [ESP+0x28]
    0x401A18: 7E29                 JLE         0x401A43             ; (*+0x2B)  
    0x401A1A: 8B7C242C             MOV         EDI,DWORD PTR [ESP+0x2C]
    0x401A1E: 8BFF                 MOV         EDI,EDI              
    0x401A20: 53                   PUSH        EBX                  ; <==0x00401A41(*+0x21)
    0x401A21: 8D4C2414             LEA         ECX,[ESP+0x14]      
    0x401A25: E876010000           CALL        0x401BA0                        ;取转换后的string中的某一位(substring(i,1))            
    0x401A2A: 3BFB                 CMP         EDI,EBX                      ;只处理前0xFF位
    0x401A2C: 7E05                 JLE         0x401A33             ; (*+0x7)  
    0x401A2E: 2AC3                 SUB         AL,BL                           ;substring(i,1) - i            
    0x401A30: 880433               MOV         BYTE PTR [EBX+ESI],AL        ;保存计算后的结果
    0x401A33: 8D4C2410             LEA         ECX,[ESP+0x10]       ; <==0x00401A2C(*-0x7)
    0x401A37: 83C301               ADD         EBX,0x1                     ;i++         
    0x401A3A: E881010000           CALL        0x401BC0            
    0x401A3F: 3BD8                 CMP         EBX,EAX              
    0x401A41: 7CDD                 JL          0x401A20             ; (*-0x21)  
    0x401A43: 8D4C2410             LEA         ECX,[ESP+0x10]       ; <==0x00401A18(*-0x2B)
    0x401A47: E874010000           CALL        0x401BC0            
    0x401A4C: 56                   PUSH        ESI                  
    0x401A4D: C6043000             MOV         BYTE PTR [EAX+ESI],0x0
    0x401A51: E86AFFFFFF           CALL        0x4019C0                        ;关键call F11跟进            
    0x401A56: 83C404               ADD         ESP,0x4              
    0x401A59: 8AD8                 MOV         BL,AL               
    0x401A5B: C744241CFFFFFFFF     MOV         DWORD PTR [ESP+0x1C],0xFFFFFFFF
    0x401A63: 8D4C2410             LEA         ECX,[ESP+0x10]      
    0x401A67: E8F4000000           CALL        0x401B60            
    0x401A6C: 8AC3                 MOV         AL,BL               
    0x401A6E: 8B4C2414             MOV         ECX,DWORD PTR [ESP+0x14]
    0x401A72: 64890D00000000       MOV         DWORD PTR FS:[0x0],ECX
    0x401A79: 59                   POP         ECX                  
    0x401A7A: 5F                   POP         EDI                  
    0x401A7B: 5E                   POP         ESI                  
    0x401A7C: 5B                   POP         EBX                  
    0x401A7D: 83C410               ADD         ESP,0x10            
    0x401A80: C3                   RET                              
  
  在左侧模块列表中的decodeAndCompare方法上下断点。F5中断
  Disassembly of JITTED decodeAndCompare (06000042) at 0x04AEBAC0
    ; Stack Size (in BYTES): 76 (0x0000004C)
    ; Number of Parameters: 0
    ; Local Variables Size (in BYTES): 60 (0x0000003C)
    ; Prologue Size (in BYTES): 48 (0x30)
    ; Standard Frame
    0x4AEBAC0: 6A00                 PUSH        0x0                 
    0x4AEBAC2: 6A00                 PUSH        0x0                 
    0x4AEBAC4: 6A00                 PUSH        0x0                 
    0x4AEBAC6: 68D0379000           PUSH        0x9037D0            
    0x4AEBACB: E8E055510B           CALL        0x100010B0         
    0x4AEBAD0: 55                   PUSH        EBP                 
    0x4AEBAD1: 8BEC                 MOV         EBP,ESP            
    0x4AEBAD3: 57                   PUSH        EDI                 
    0x4AEBAD4: 56                   PUSH        ESI                 
    0x4AEBAD5: 53                   PUSH        EBX                 
    0x4AEBAD6: 83EC3C               SUB         ESP,0x3C            
    0x4AEBAD9: 33C0                 XOR         EAX,EAX            
    0x4AEBADB: 8945C0               MOV         DWORD PTR [EBP-0x40],EAX; VAR:0x40
    0x4AEBADE: 8945BC               MOV         DWORD PTR [EBP-0x44],EAX; VAR:0x44
    0x4AEBAE1: 8965F0               MOV         DWORD PTR [EBP-0x10],ESP; VAR:0x10
    0x4AEBAE4: 33C0                 XOR         EAX,EAX            
    0x4AEBAE6: 8945E4               MOV         DWORD PTR [EBP-0x1C],EAX; VAR:0x1C
    0x4AEBAE9: C745B8C7C6C8C7       MOV         DWORD PTR [EBP-0x48],0xC7C8C6C7; VAR:0x48
    ; end of prologue
    0x4AEBAF0: 8BF1                 MOV         ESI,ECX            
    ; IL_0000: call __CxxQueryExceptionSize
    ; IL_0005: localloc
    ; IL_0007: stloc.2
    0x4AEBAF2: E85D84F400           CALL        0x5A33F54           
    0x4AEBAF7: 85C0                 TEST        EAX,EAX            
    0x4AEBAF9: 7421                 JZ          0x4AEBB1C           ; (*+0x23)
    0x4AEBAFB: 83C003               ADD         EAX,0x3            
    0x4AEBAFE: 83E0FC               AND         EAX,0xFC            
    0x4AEBB01: F7D8                 NEG         EAX                 
    0x4AEBB03: 03C4                 ADD         EAX,ESP            
    0x4AEBB05: 7202                 JB          0x4AEBB09           ; (*+0x4)
    0x4AEBB07: 33C0                 XOR         EAX,EAX            
    0x4AEBB09: 852424               TEST        DWORD PTR [ESP],ESP         ; <==0x04AEBB05(*-0x4), 0x04AEBB18(*+0xF)
    0x4AEBB0C: 8BD4                 MOV         EDX,ESP            
    0x4AEBB0E: 81EA00100000         SUB         EDX,0x1000         
    0x4AEBB14: 8BE2                 MOV         ESP,EDX            
    0x4AEBB16: 3BE0                 CMP         ESP,EAX            
    0x4AEBB18: 73EF                 JAE         0x4AEBB09           ; (*-0xF)
    0x4AEBB1A: 8BE0                 MOV         ESP,EAX            
    0x4AEBB1C: 8965F0               MOV         DWORD PTR [EBP-0x10],ESP; VAR:0x10        ; <==0x04AEBAF9(*-0x23)
    0x4AEBB1F: 8945D0               MOV         DWORD PTR [EBP-0x30],EAX; VAR:0x30
    ; IL_0008: ldloca.s 0x06
    ; IL_000A: ldsfld lpMachineCode
    ; IL_000F: call  System.IntPtr::.ctor()
    0x4AEBB22: A170CA4000           MOV         EAX,DWORD PTR [0x40CA70]
    0x4AEBB27: 8945C4               MOV         DWORD PTR [EBP-0x3C],EAX; VAR:0x3C
    ; IL_0014: ldloc.s 0x06
    ; IL_0016: call  System.Runtime.InteropServices.Marshal::PtrToStringAnsi()
    ; IL_001B: stloc.s 0x05
    0x4AEBB2A: 8B4DC4               MOV         ECX,DWORD PTR [EBP-0x3C]; VAR:0x3C
    0x4AEBB2D: E86657CEFF           CALL        0x47D1298           ; (0x047D1298)
    0x4AEBB32: 8945BC               MOV         DWORD PTR [EBP-0x44],EAX; VAR:0x44
    ; IL_001D: ldloca.s 0x04
    ; IL_001F: ldarg.0
    ; IL_0020: call  System.IntPtr::.ctor()
    0x4AEBB35: 8975C8               MOV         DWORD PTR [EBP-0x38],ESI; VAR:0x38        ;跟到这里,双击ESI寄存器可以看见里面保存的是刚刚经过计算后的注册码字符串
    ; IL_0025: ldloc.s 0x04
    ; IL_0027: call  System.Runtime.InteropServices.Marshal::PtrToStringAnsi()
    ; IL_002C: stloc.1
    0x4AEBB38: 8B4DC8               MOV         ECX,DWORD PTR [EBP-0x38]; VAR:0x38
    0x4AEBB3B: E85857CEFF           CALL        0x47D1298           ; (0x047D1298)
    0x4AEBB40: 8945C0               MOV         DWORD PTR [EBP-0x40],EAX; VAR:0x40
    ; IL_002D: call  System.Text.Encoding::get_ASCII()
    ; IL_0032: ldloc.1
    ; IL_0033: call  System.Convert::FromBase64String()
    ; IL_0038: ldc.i4.0
    ; IL_0039: ldloc.1
    ; IL_003A: call  System.Convert::FromBase64String()
    ; IL_003F: ldlen
    ; IL_0040: callvirt  System.Text.Encoding::GetString()
    ; IL_0045: stloc.1
    ; IL_0046: leave.s IL_0095
    0x4AEBB43: 833D3014DA0100       CMP         DWORD PTR [0x1DA1430],0x0
    0x4AEBB4A: 7523                 JNE         0x4AEBB6F           ; (*+0x25)
    0x4AEBB4C: B98CA32D04           MOV         ECX,0x42DA38C      
    0x4AEBB51: E8C664E0FB           CALL        0x8F201C            
    0x4AEBB56: 8BF0                 MOV         ESI,EAX            
    0x4AEBB58: 8BCE                 MOV         ECX,ESI            
    0x4AEBB5A: BA9F4E0000           MOV         EDX,0x4E9F         
    0x4AEBB5F: E8645779FF           CALL        0x42812C8           ; (0x042812C8)
    0x4AEBB64: 8D153014DA01         LEA         EDX,[0x1DA1430]     
    0x4AEBB6A: E8B77E3875           CALL        mscorwks.dll!DllUnregisterServerInternal + 0x0206          ; (0x79E73A26)
    0x4AEBB6F: 8B353014DA01         MOV         ESI,DWORD PTR [0x1DA1430]        ; <==0x04AEBB4A(*-0x25)
    0x4AEBB75: 8B4DC0               MOV         ECX,DWORD PTR [EBP-0x40]; VAR:0x40
    0x4AEBB78: FF1504D14803         CALL        DWORD PTR [0x348D104]
    0x4AEBB7E: 8BF8                 MOV         EDI,EAX            
    0x4AEBB80: 6A00                 PUSH        0x0                 
    0x4AEBB82: 8B4DC0               MOV         ECX,DWORD PTR [EBP-0x40]; VAR:0x40
    0x4AEBB85: FF1504D14803         CALL        DWORD PTR [0x348D104]
    0x4AEBB8B: FF7004               PUSH        DWORD PTR [EAX+0x4]
    0x4AEBB8E: 8BCE                 MOV         ECX,ESI            
    0x4AEBB90: 8BD7                 MOV         EDX,EDI            
    0x4AEBB92: 8B01                 MOV         EAX,DWORD PTR [ECX]
    0x4AEBB94: FF90DC000000         CALL        DWORD PTR [EAX+0xDC]
    0x4AEBB9A: 8945C0               MOV         DWORD PTR [EBP-0x40],EAX; VAR:0x40
    0x4AEBB9D: E99D000000           JMP         0x4AEBC3F           
    ;...............................................................
    ;...............................................................
    0x4AEBC8B: 5B                   POP         EBX                 
    0x4AEBC8C: 5E                   POP         ESI                 
    0x4AEBC8D: 5F                   POP         EDI                 
    0x4AEBC8E: 5D                   POP         EBP                 
    0x4AEBC8F: C3                   RET                             
  三、算法及注册机
  算法超简单,只要了解.net的都不成问题。这里就不赘述了。
复制内容到剪贴板
代码:
trcode.Text = System.Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(tmcode.Text)) ;
  byte[] buffer = System.Text.Encoding.ASCII.GetBytes(trcode.Text);
  for ( int i = 0 ; i < buffer.Length ; i++ )
  {
          if ( i < 0xFF )
          {
                  buffer[i] = (byte)(buffer[i] + i) ;
          }
  }
  trcode.Text = System.Text.Encoding.Default.GetString(buffer);
--------------------------------------------------------------------------------
【经验总结】
  .net的软件如果没有经过混淆,托管方法用reflector就可以分析的八九不离十。再结合动态调试工具,就算其中夹杂着一
  些非托管代码,难度也不是很大。
  tankaiha兄的.net方面文章字字玑珠,像我这样的菜鸟应该仔细研读。关于PEBrowse可以参见tankaiha的《【原创】用
  PEBrowse对.Net程序进行动态调试》一文(http://bbs1.pediy.com/showthread.php?s=&threadid=24646
原文地址:https://www.cnblogs.com/cxd4321/p/1218528.html