Speed Video Splitter 4.32 的注册算法分析

程序没有加壳,直接用OD载入!
输入假码有提示,可能通过查找参考字符或者难过堆栈返回找到关键CALL:


00404917         .  8>lea edi,dword ptr ds:[esi+64]
0040491A         .  5>push eax
0040491B         .  5>push ecx
0040491C         .  E>call Speed_Vi.0040E320
00404921         .  8>add esp,8
00404924         .  8>test al,al
00404926         .  7>jnz short Speed_Vi.00404944
00404928         .  6>push 40
0040492A         .  6>push Speed_Vi.004233A4                 ;  ASCII "Sorry"
0040492F         .  6>push Speed_Vi.00423378                 ;  ASCII "Invalid username or registration code      "
00404934         .  8>mov ecx,esi
00404936         .  E>call <jmp.&MFC42.#4224>
0040493B         .  C>mov byte ptr ds:[42484C],0
00404942         .  E>jmp short Speed_Vi.0040499B
00404944         >  5>push edi
00404945         .  8>lea eax,dword ptr ss:[esp+C]
00404949         .  6>push Speed_Vi.0042336C                 ;  ASCII "License to "

进行CALL后:


0040E320        /$  8>mov edx,dword ptr ss:[esp+4]
0040E324        |.  5>push esi
0040E325        |.  5>push edi
0040E326        |.  B>mov edi,Speed_Vi.0042477C
0040E32B        |.  8>mov esi,edx
0040E32D        |.  B>mov ecx,1
0040E332        |.  3>xor eax,eax
0040E334        |.  F>repe cmps byte ptr es:[edi],byte ptr d>;  测试用户名是否为空
0040E336        |.  7>je short Speed_Vi.0040E362
0040E338        |.  8>mov eax,dword ptr ss:[esp+10]
0040E33C        |.  5>push ebx
0040E33D        |.  B>mov edi,Speed_Vi.0042477C
0040E342        |.  8>mov esi,eax
0040E344        |.  B>mov ecx,1
0040E349        |.  3>xor ebx,ebx
0040E34B        |.  F>repe cmps byte ptr es:[edi],byte ptr d>;  测试注册码是否为空
0040E34D        |.  5>pop ebx
0040E34E        |.  7>je short Speed_Vi.0040E362
0040E350        |.  5>push eax
0040E351        |.  5>push edx
0040E352        |.  E>call Speed_Vi.0040E0F0                 ;  如果上面2个不跳,则来到最后的关键CALL
0040E357        |.  8>add esp,8
0040E35A        |.  8>test eax,eax
0040E35C        |.  5>pop edi
0040E35D        |.  5>pop esi
0040E35E        |.  0>setne al
0040E361        |.  C>retn
0040E362        |>  5>pop edi
0040E363        |.  3>xor al,al
0040E365        |.  5>pop esi
0040E366        \.  C>retn

继续进行CALL:call Speed_Vi.0040E0F0
一直单步分析,很快就看到了对用户名的处理:


0040E1E6        |> /8A>|/mov al,byte ptr ds:[ebx+ebp]      ;  按位取用户名ASCII
0040E1E9        |. |33>||xor esi,esi                       ;  此处ESI为索引,首先清0
0040E1EB        |> |3A>||/cmp al,byte ptr ds:[esi*2+424398>;  在字符表中查询
0040E1F2        |. |74>|||je short Speed_Vi.0040E1FC       ;  找到则结束循环
0040E1F4        |. |46 |||inc esi
0040E1F5        |. |83>|||cmp esi,34
0040E1F8        |.^|7C>||\jl short Speed_Vi.0040E1EB
0040E1FA        |. |EB>||jmp short Speed_Vi.0040E20D
0040E1FC        |> |8A>||mov cl,byte ptr ds:[esi*2+424399] ;  以ESI*2为索引取出字符
0040E203        |. |51 ||push ecx
0040E204        |. |8D>||lea ecx,dword ptr ss:[esp+38]
0040E208        |. |E8>||call <jmp.&MFC42.#940>
0040E20D        |> |83>||cmp esi,34
0040E210        |. |75>||jnz short Speed_Vi.0040E220
0040E212        |. |8B>||mov edx,dword ptr ss:[esp+18]
0040E216        |. |8D>||lea ecx,dword ptr ss:[esp+34]
0040E21A        |. |52 ||push edx
0040E21B        |. |E8>||call <jmp.&MFC42.#940>
0040E220        |> |8B>||mov edi,ebx
0040E222        |. |83>||or ecx,FFFFFFFF
0040E225        |. |33>||xor eax,eax
0040E227        |. |45 ||inc ebp
0040E228        |. |F2>||repne scas byte ptr es:[edi]
0040E22A        |. |F7>||not ecx
0040E22C        |. |49 ||dec ecx
0040E22D        |. |3B>||cmp ebp,ecx
0040E22F        |.^\72>|\jb short Speed_Vi.0040E1E6
0040E231        |>  8B>|mov eax,dword ptr ss:[esp+34]
0040E235        |.  8B>|mov ecx,dword ptr ds:[eax-8]
0040E238        |.  83>|cmp ecx,10                         ;  如果用户名长度大于0x10,则直接跳转比较
0040E23B        |.  7D>|jge short Speed_Vi.0040E277
0040E23D        |.  8B>|mov eax,ecx
0040E23F        |.  B9>|mov ecx,10
0040E244        |.  2B>|sub ecx,eax
0040E246        |.  8D>|lea edx,dword ptr ss:[esp+1C]
0040E24A        |.  51 |push ecx
0040E24B        |.  52 |push edx
0040E24C        |.  B9>|mov ecx,Speed_Vi.00424860
0040E251        |.  E8>|call <jmp.&MFC42.#4129>
0040E256        |.  50 |push eax
0040E257        |.  8D>|lea ecx,dword ptr ss:[esp+38]
0040E25B        |.  C6>|mov byte ptr ss:[esp+30],3
0040E260        |.  E8>|call <jmp.&MFC42.#939>
0040E265        |.  8D>|lea ecx,dword ptr ss:[esp+1C]
0040E269        |.  C6>|mov byte ptr ss:[esp+2C],2
0040E26E        |.  E8>|call <jmp.&MFC42.#800>
0040E273        |.  8B>|mov eax,dword ptr ss:[esp+34]
0040E277        |>  8B>|mov ecx,dword ptr ss:[esp+20]
0040E27B        |.  51 |push ecx                           ; /s2
0040E27C        |.  50 |push eax                           ; |s1
0040E27D        |.  FF>|call dword ptr ds:[<&MSVCRT._mbscm>; \_mbscmp
0040E283        |.  83>|add esp,8
0040E286        |.  8D>|lea ecx,dword ptr ss:[esp+34]
0040E28A        |.  85>|test eax,eax
0040E28C        |.  C6>|mov byte ptr ss:[esp+2C],1
0040E291        |.  74>|je short Speed_Vi.0040E2AE

整个算法,用到了用户输入的用户名以及2个固定字符串:


00424398  41 78 42 69 43 49 64 41 65 58 66 4D 67 6A 68 45  AxBiCIdAeXfMgjhE
004243A8  69 56 6A 5A 6B 65 6C 52 6D 79 6E 42 6F 4B 70 64  iVjZkelRmynBoKpd
004243B8  71 54 72 53 73 50 74 57 75 6C 76 6B 77 44 78 48  qTrSsPtWulvkwDxH
004243C8  79 46 7A 7A 61 71 62 70 43 4F 44 6B 45 67 46 59  yFzzaqbpCODkEgFY
004243D8  47 6D 48 74 49 61 4A 72 4B 51 4C 6E 4D 73 4E 75  GmHtIaJrKQLnMsNu
004243E8  4F 55 50 47 51 4A 52 4C 53 4E 54 62 55 63 56 66  OUPGQJRLSNTbUcVf
004243F8  57 68 58 6F 59 77 5A 43 65 74 46                 WhXoYwZCetF

0012D208   010D42E8  ASCII "aeLHlXiwoPe"

通过后补字符的长度,得知,当用户名长度少于5时则无法注册成功!
弄清楚了,可以写算法注册机了:
代码如下:


#include
#include
using namespace std;

const char g_string[]="AxBiCIdAeXfMgjhE\
iVjZkelRmynBoKpd\
qTrSsPtWulvkwDxH\
yFzzaqbpCODkEgFY\
GmHtIaJrKQLnMsNu\
OUPGQJRLSNTbUcVf\
WhXoYwZCetF";
const char g_str[]="aeLHlXiwoPe";

int main()
{
  cout<<"Please Enter your name(Not Space):\n";
  char name[20]={0};
  cin>>name;
  
  char code[32]={0};
  int len=strlen(name);
  for(int i=0;i    for(int j=0;j<0x34;j++)
      if(name[i]==g_string[j*2])
        code[i]=g_string[j*2+1];
  }
  if(len<0x10)
    for(int i=len;i<0x10;i++)
      code[i]=g_str[i-len];
  
  cout<<"Your RegCode is:\n";
  cout<  
  cout<<"\nEnter a Key Exit===========\n";
  getchar();
  getchar();
  return 0;
}

呵呵,就 这样OVER了!!

“Speed Video Splitter 4.32 的注册算法分析”的一个回复

发表评论