ResScope,一个将比exeScope更强的软件资源分析工具,很不错的软件。
delphi程序,无壳无反调试无校验,我喜欢,:)。
通过字符串容易找到这里,很容易看到,读取注册表中的用户名和注册码,分别计算后进行再比较。
代码:
CODE:0051B6EB mov cl, 1 CODE:0051B6ED mov edx, offset aSoftwareRest_3 ; "SOFTWARE\\RESTOOLS\\ResScope" CODE:0051B6F2 mov eax, [ebp+var_8] CODE:0051B6F5 call @Registry@TRegistry@OpenKey$qqrx17System@AnsiStringo ; Registry::TRegistry::OpenKey(System::AnsiString,bool) CODE:0051B6FA test al, al CODE:0051B6FC jz loc_51B888 CODE:0051B702 lea eax, [ebp+var_C] CODE:0051B705 call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &) CODE:0051B70A lea eax, [ebp+var_10] CODE:0051B70D call @System@@LStrClr$qqrr17System@AnsiString ; System::__linkproc__ LStrClr(System::AnsiString &) CODE:0051B712 mov edx, offset aReguser_2 ; "reguser" CODE:0051B717 mov eax, [ebp+var_8] CODE:0051B71A call @Registry@TRegistry@ValueExists$qqrx17System@AnsiString ; Registry::TRegistry::ValueExists(System::AnsiString) CODE:0051B71F test al, al CODE:0051B721 jz short loc_51B733 CODE:0051B723 lea ecx, [ebp+var_C] CODE:0051B726 mov edx, offset aReguser_2 ; "reguser" CODE:0051B72B mov eax, [ebp+var_8] CODE:0051B72E call @TRegistry@ReadString$qqrx10AnsiString ; TRegistry::ReadString(AnsiString) CODE:0051B733 CODE:0051B733 loc_51B733: ; CODE XREF: sub_51B6A0+81j CODE:0051B733 mov edx, offset aRegcode_1 ; "regcode" CODE:0051B738 mov eax, [ebp+var_8] CODE:0051B73B call @Registry@TRegistry@ValueExists$qqrx17System@AnsiString ; Registry::TRegistry::ValueExists(System::AnsiString) CODE:0051B740 test al, al CODE:0051B742 jz short loc_51B754 CODE:0051B744 lea ecx, [ebp+var_10] CODE:0051B747 mov edx, offset aRegcode_1 ; "regcode" CODE:0051B74C mov eax, [ebp+var_8] CODE:0051B74F call @TRegistry@ReadString$qqrx10AnsiString ; TRegistry::ReadString(AnsiString) CODE:0051B754 CODE:0051B754 loc_51B754: ; CODE XREF: sub_51B6A0+A2j CODE:0051B754 mov eax, [ebp+var_10] CODE:0051B757 call @System@@LStrLen ; System::__linkproc__ LStrLen CODE:0051B75C cmp eax, 30h CODE:0051B75F jnz loc_51B888 CODE:0051B765 mov eax, [ebp+var_C] CODE:0051B768 call @System@@LStrLen ; System::__linkproc__ LStrLen CODE:0051B76D test eax, eax CODE:0051B76F jle loc_51B888 CODE:0051B775 lea eax, [ebp+var_14] ; 保存返回值 CODE:0051B775 ; 指向18字节长PCHAR CODE:0051B778 push eax CODE:0051B779 mov cl, 1 CODE:0051B77B mov dl, 1 CODE:0051B77D mov eax, [ebp+var_C] ; "reguser" CODE:0051B780 call sub_51A06C ; 处理用户名 CODE:0051B785 mov eax, [ebp+var_14] ; 处理用户名得到的18字节长的串,先入栈,待会好比较 CODE:0051B788 push eax CODE:0051B789 lea ecx, [ebp+var_18] ; 保存返回值。。 CODE:0051B78C mov dl, 1 CODE:0051B78E mov eax, [ebp+var_10] ; "regcode" CODE:0051B791 call sub_519298 ; 注册码变换函数 CODE:0051B796 mov edx, [ebp+var_18] CODE:0051B799 pop eax CODE:0051B79A call @System@@LStrCmp$qqrv ; System::__linkproc__ LStrCmp(void) CODE:0051B79F jnz short loc_51B7A5 ;
这个软件的爆破起来不是很容易
代码:
CODE:00525CBB mov eax, ds:off_525214 ; * Reference to class THexDumpPass CODE:00525CC0 call sub_425EEC ; * Reference to: Classes.TComponent.Create(TComponent;boolean;TComponent); CODE:00525CC5 mov ebx, eax CODE:00525CC7 lea eax, [ebp+var_130] CODE:00525CCD call sub_518E18 ; 用户名进行变换转为一串字符 CODE:00525CD2 mov edx, [ebp+var_130] CODE:00525CD8 mov eax, ebx CODE:00525CDA mov ecx, [eax] CODE:00525CDC call dword ptr [ecx+18h] ; Classes::TComponent::SetName()
在此处设置用户名变换而来得串设置组件名字,然后使用注册码变换而来得串来FindComponent(),如果注册码正确,两个串应该相等,则能得到正确的句柄,
保存起来,然后在校验注册时候使用该句柄,若没找到则例外出错。
下面来看看注册算法:
使用peid来探测一下用了什么算法,一下出来8种算法,够吓人,不过还有不少没探测出来,:)。
DEDE反汇编看看,注意到类名中有大量的类似TCipher_MD5、TCipher_Blowfish、TCipher_IDEA类似这样的类名,
熟悉的人一眼就看出来使用DEC控件,这是Hagen Reddmann写的Delphi Encryption Compendium 控件,控件支持20余种Hash,40多种分组密码算法,
而且附带源码,是很好的学习算法的教程。
在分析过程中可以用delphi编译一个小例子,然后跟踪看看,并参考源码效果很好。
先来看看处理用户名的函数
CODE:0051B780 call sub_51A06C ; 处理用户名
用户ID的生成是调用CPUID指令获取cpu信息,然后编码至16字节。与用户名连接起来,通过简单算法变换为18h字节,
代码:
CODE:0051A0C3 mov edx, ds:dword_578440 ; 用户ID,类似"92F1L8EACT2FFNFF"一串字符。。。 CODE:0051A0C9 call @System@@LStrCat$qqrv ; System::__linkproc__ LStrCat(void) CODE:0051A4A6 push offset unk_575B10 ; IVector,初始化向量 CODE:0051A4AB mov edx, offset byte_575B08 ; keyCAST..64位密钥 CODE:0051A4B0 lea eax, [ebp+var_F4_TCast128Data] ; 0012FAF8 CODE:0051A4B0 ; TCast128Data= record CODE:0051A4B0 ; InitBlock: array[0..7] of byte; { initial IV } CODE:0051A4B0 ; LastBlock: array[0..7] of byte; { current IV } CODE:0051A4B0 ; xKey: array[0..31] of DWord; CODE:0051A4B0 ; Rounds: integer; CODE:0051A4B0 ; end; CODE:0051A4B6 mov ecx, 8 ; sizeof(Key) CODE:0051A4BB call Cast128Init ; 注意此处Cast128非调用DEC的函数,乃是独立的类。 ; 注意其sbox乃是作者随机生成
采用CBC模式调用Cast128算法,即密码分组链接模式即每次分组加密结果,与下组明文xor后,作为输入。
代码:
CODE:0051A50E lea edx, [ebp+eax+var_48] ; 输入串,输出也保存再该地址 CODE:0051A512 lea eax, [ebp+var_F4_TCast128Data] ; 密钥初始化结果 CODE:0051A518 call Cast128EncryptCBC ; procedure Cast128EncryptCBC(var Data: TCast128Data; InData, OutData: pointer); CODE:0051A518 ; { encrypts the data in a 64bit block using the CBC chaining mode }
下面就是调用DEC中的密码算法来进行加密
代码:
CODE:0051A6FB mov edx, ds:off_4F31E0 ; Reference to class TCipher_3Way,这个可由DEDE反汇编代码中看到 CODE:0051A701 mov eax, [ebp+var_14] CODE:0051A704 call @TCipherManager@@SetClass ; TCipherManager::__linkproc__ SetClass CODE:0051A709 xor ecx, ecx CODE:0051A70B mov edx, [ebp+var_18] CODE:0051A70E mov eax, [ebp+var_14] CODE:0051A711 call @TCipherManager@@InitKey ; procedure TCipherManager.InitKey(const Key: String; IVector: Pointer); CODE:0051A716 push 18h ; 待Encode串长度 CODE:0051A718 lea ecx, [ebp+var_60] ; 返回串 CODE:0051A71B lea edx, [ebp+var_48] ; Source串,由Cast128而来 CODE:0051A71E mov eax, [ebp+var_14] CODE:0051A721 call @TCipherManager@@EncodeBuffer ; procedure TCipherManager.EncodeBuffer CODE:0051A721 ; (const Source; var Dest; DataSize: Integer);
下面级连的还有
代码:
CODE:0051A7F6 mov edx, ds:off_4F29DC ; * Reference to class TCipher_Blowfish (注: pbox,sbox非标) CODE:0051A8F1 mov edx, ds:off_4F2940 ; * Reference to class TCipher_Gost (注: Gost_Data非标) CODE:0051AA15 mov edx, ds:off_4F2A7C ; * Reference to class TCipher_IDEA CODE:0051AB39 mov edx, ds:off_4F3144 ; * Reference to class TCipher_Q128 (注: Q128_Data非标) CODE:0051AC5D mov edx, ds:off_4F2B18 ; * Reference to class TCipher_SAFER (注: DEC库中默认没有添加入列表,需修改代码注册该Cipher类到控件) CODE:0051AD58 mov edx, ds:off_4F2E34 ; * Reference to class TCipher_SAFER_K128 CODE:0051AE7C mov edx, ds:off_4F2BB4 ; * Reference to class TCipher_SAFER_K40 CODE:0051AF77 mov edx, ds:off_4F2CF4 ; * Reference to class TCipher_SAFER_K64
对用户名的变换用了10种算法,不过调用DEC库格式比较标准,跟踪起来也什么困难.
下面看注册码变换算法
代码:
CODE:0051B791 call sub_519298 ; 注册码变换函数 注册码为30h字符,先解码后为18h字节数据,每2字符变换为一个字节 然后 CODE:005193C2 push offset unk_575AE0 ; GOST初始化向量 CODE:005193C7 mov edx, offset unk_575AE8 ; GOST密钥 20h字节 CODE:005193CC lea eax, [ebp+var_98] ; TGOSTData= record CODE:005193CC ; InitBlock: array[0..7] of byte; { initial IV } CODE:005193CC ; LastBlock: array[0..7] of byte; { current IV } CODE:005193CC ; XKey: array[0..7] of DWord; CODE:005193CC ; end; CODE:005193D2 mov ecx, 20h CODE:005193D7 call GOSTInit ; procedure GOSTInit(var Data: TGOSTData; Key: pointer; Len: integer; IV: pointer ; 注意此处Cast128非调用DEC的函数,乃是独立的类。其Gost_Data非标
同样采用CBC模式解码数据,每次处理8字节,共处理3次。
代码:
CODE:00519429 lea edx, [ebp+eax+var_50] ; 输出 CODE:0051942D lea eax, [ebp+var_98] ; var Data: TGOSTData CODE:00519433 call GOSTDecryptCBC ; //procedure GOSTDecryptCBC(var Data: TGOSTData; InData, OutData: pointer); CODE:00519433 ; { decrypts the data in a 64bit block using the CBC chaining mode }
同样下面级连了一串变换,不过此处调用的都是@TCipherManager@@DecodeBuffer,解码函数
代码:
CODE:005195FB mov edx, ds:off_4F327C ; * Reference to class TCipher_Twofish (注: Twofish_Data,Twofish_8x8值非标) CODE:00519714 mov edx, ds:off_4F300C ; * Reference to class TCipher_TEAN CODE:00519810 mov edx, ds:off_4F2F74 ; * Reference to class TCipher_TEA CODE:0051990C mov edx, ds:off_4F33B4 ; * Reference to class TCipher_Square CODE:00519A2D mov edx, ds:off_4F3318 ; * Reference to class TCipher_Shark CODE:00519B29 mov edx, ds:off_4F30A8 ; * Reference to class TCipher_SCOP CODE:00519C4A mov edx, ds:off_4F2D94 ; * Reference to class TCipher_SAFER_SK64 CODE:00519D6B mov edx, ds:off_4F2C54 ; * Reference to class TCipher_SAFER_SK40 CODE:00519E67 mov edx, ds:off_4F2ED4 ; * Reference to class TCipher_SAFER_SK128
最后将用户名变换输出结果和注册码变换输出进行比较。
对于1.75版只使用了Cast128和Gost两种算法,而1.92版堆叠了大概有18种密码学算法,
但由于该库格式又比较固定,总体说来所以不难做出注册机。基本上就照着作者的流程,用户名变换部分全部照抄,
注册码变换部分从后往前逆向使用。类似这样,只是把DecodeBuffer改为EncodeBuffer就可以了,类似
代码:
CipherManager1.Algorithm := 'Twofish'; CipherManager1.InitKey(keystr, nil); CipherManager1.EncodeBuffer(Source,Out,$18);
另外注意其中有些DEC库函数的s盒被更改,所以还需要修改DEC控件中的cipher.inc里面的值,重新编译该控件。
感谢DiKeN/IPB提供ResScope1.75注册机源码,省下了我不少时间,:)。
yesky1/IPB
7.21