红绡枫叶 发表于 2014-12-21 23:02

Galaxy 3D screensaver算法分析

本帖最后由 红绡枫叶 于 2015-1-8 20:16 编辑

这款600K大小的3D屏保软件的注册算法挺有意思的,就把它的算法彻底分析了一遍.

VC6.0编译,MFC.找到MFC派发CMD消息的函数_AfxDispatchCmdMsg(CCmdTarget *,uint,int,void (CCmdTarget::*)(void),void *,uint,AFX_CMDHANDLERINFO *)
快速定位到注册按钮单击后的事件处理地址:
CPU Disasm
Address    Hex dump      Command                              Comments
00406940 r /.64:A1 000 mov eax, dword ptr fs:
00406946   |.6A FF   push -1
00406948   |.68 436044 push unknown_libname_605
0040694D   |.50      push eax
0040694E   |.64:8925 0 mov dword ptr fs:, esp            ; Installs SE handler unknown_libname_605
00406955   |.81EC B400 sub esp, 0B4
0040695B   |.53      push ebx
0040695C   |.55      push ebp
0040695D   |.56      push esi
0040695E   |.57      push edi
0040695F   |.8BF1      mov esi, ecx
00406961   |.6A 01   push 1                                 ; /Arg1 = 1
00406963   |.E8 665A03 call CWnd::UpdateData(int)             ; \Galaxy3D.CWnd::UpdateData(int)
00406968   |.68 2C0100 push 12C                               ; /Time = 300. ms
0040696D   |.FF15 7092 call dword ptr ds:[<&KERNEL32.Sleep>]; \KERNEL32.Sleep
00406973   |.8DBE A000 lea edi,
00406979   |.8DAE 9C00 lea ebp,
0040697F   |.8D9E 9800 lea ebx,
00406985   |.57      push edi
00406986   |.55      push ebp
00406987   |.53      push ebx
00406988   |.FF15 9882 call dword ptr ds: ;算法所在地
0040698E   |.83C4 0C   add esp, 0C
00406991   |.85C0      test eax, eax ;关键跳转
00406993   |.0F84 9700 jz loc_406A30
00406999   |.8D4C24 10 lea ecx,
0040699D   |.E8 BE7400 call sub_40DE60                        ; [Galaxy3D.sub_40DE60
004069A2   |.6A 00   push 0                                 ; /Arg1 = 0
004069A4   |.8D4C24 14 lea ecx,                      ; |
004069A8   |.C78424 D0 mov dword ptr ss:, 0         ; |
004069B3   |.E8 187A00 call sub_40E3D0                        ; \Galaxy3D.sub_40E3D0
004069B8   |.53      push ebx                               ; /Arg1
004069B9   |.8D4C24 50 lea ecx,                       ; |
004069BD   |.E8 292403 call CString::operator=(CString const; \Galaxy3D.CString::operator=
004069C2   |.55      push ebp                               ; /Arg1
004069C3   |.8D4C24 54 lea ecx,                       ; |
004069C7   |.E8 1F2403 call CString::operator=(CString const; \Galaxy3D.CString::operator=
004069CC   |.57      push edi                               ; /Arg1
004069CD   |.8D4C24 4C lea ecx,                       ; |
004069D1   |.E8 152403 call CString::operator=(CString const; \Galaxy3D.CString::operator=
004069D6   |.6A 00   push 0                                 ; /Arg1 = 0
004069D8   |.8D4C24 14 lea ecx,                      ; |
004069DC   |.E8 9F7700 call sub_40E180                        ; \Galaxy3D.sub_40E180
004069E1   |.6A 00   push 0                                 ; /Arg2 = 0
004069E3   |.68 970000 push 97                              ; |Arg1 = 97
004069E8   |.8D4C24 70 lea ecx,                       ; |
004069EC   |.E8 C43203 call CDialog::CDialog(uint,CWnd *)   ; \Galaxy3D.CDialog::CDialog
004069F1   |.8D4C24 68 lea ecx,
004069F5   |.C68424 CC mov byte ptr ss:, 1
004069FD   |.E8 683303 call CDialog::DoModal(void)            ; [Galaxy3D.CDialog::DoModal(void)
00406A02   |.8BCE      mov ecx, esi
00406A04   |.E8 4C3603 call CDialog::OnOK(void)               ; [Galaxy3D.CDialog::OnOK(void)
00406A09   |.8D4C24 68 lea ecx,
00406A0D   |.C68424 CC mov byte ptr ss:, 0
00406A15   |.E8 862F03 call sub_4399A0                        ; [Galaxy3D.CDialog::~CDialog
00406A1A   |.8D4C24 10 lea ecx,
00406A1E   |.C78424 CC mov dword ptr ss:, -1
00406A29   |.E8 427600 call sub_40E070                        ; [Galaxy3D.sub_40E070
00406A2E   |.EB 16   jmp short loc_406A46
00406A30 l |>68 C80000 push 0C8                               ; /Time = 200. ms
00406A35   |.FF15 7092 call dword ptr ds:[<&KERNEL32.Sleep>]; \KERNEL32.Sleep
00406A3B   |.6A FF   push -1                              ; /Arg3 = -1
00406A3D   |.6A 00   push 0                                 ; |Arg2 = 0
00406A3F   |.6A 0E   push 0E                              ; |Arg1 = 0E
00406A41   |.E8 5E9903 call AfxMessageBox(uint,uint,uint)   ; \Galaxy3D.AfxMessageBox ;出错提示.
00406A46 l |>8B8C24 C4 mov ecx, dword ptr ss:
00406A4D   |.5F      pop edi
00406A4E   |.5E      pop esi
00406A4F   |.5D      pop ebp
00406A50   |.5B      pop ebx
00406A51   |.64:890D 0 mov dword ptr fs:, ecx
00406A58   |.81C4 C000 add esp, 0C0
00406A5E   \.C3      retn

于是跟进算法CALL:
CPU Disasm
Address    Hex dump      Command                              Comments
00402F00 s /$8B4C24 04 mov ecx, dword ptr ss:          ; ASCII "123456789abc",这是我输入的注册码
00402F04   |.81EC 2C01 sub esp, 12C
00402F0A   |.8D4424 00 lea eax,
00402F0E   |.6A 06   push 6
00402F10   |.50      push eax
00402F11   |.51      push ecx
00402F12   |.E8 A9FDFF call RegKeys2HexNum    ;将输入的12个注册码转换成16进制数值,即0x123456789abc
00402F17   |.83C4 0C   add esp, 0C
00402F1A   |.83F8 06   cmp eax, 6;转化后的大小为6byte,其实也就是说,注册码只能是12位
00402F1D   |.74 0C   je short loc_402F2B
00402F1F   |.B8 12AE00 mov eax, 0AE12
00402F24   |.81C4 2C01 add esp, 12C
00402F2A   |.C3      retn
00402F2B l |>6A 06   push 6
00402F2D   |.8D5424 04 lea edx,
00402F31   |.68 940000 push 94
00402F36   |.52      push edx
00402F37   |.E8 34FCFF call regkey_check   ;真正的算法验证的地方
00402F3C   |.83C4 0C   add esp, 0C
00402F3F   |.F7D8      neg eax                              ; If eax is not 0, sets it to 00003397
00402F41   |.1BC0      sbb eax, eax
00402F43   |.25 973300 and eax, 00003397
00402F48   |.05 30AE00 add eax, 0AE30
00402F4D   |.81C4 2C01 add esp, 12C
00402F53   \.C3      retn

我们看看RegKeys2HexNum函数关键的内容:
CPU Disasm
Address    Hex dump      Command                              Comments
00402CFE   |.C645 FC 0 mov byte ptr ss:, 1
00402D02   |.E8 824D03 call CString::TrimLeft(void)         ; 清除空格
00402D07   |.8D4D 08   lea ecx,
00402D0A   |.E8 2E4D03 call CString::TrimRight(void)          ; 清除空格
00402D0F   |.8975 E8   mov dword ptr ss:, esi
00402D12 l |>8B4D 08   /mov ecx, dword ptr ss:
00402D15 l |>3B71 F8   |/cmp esi, dword ptr ds:
00402D18   |.7D 6F   ||jge short loc_402D89
00402D1A   |.8A040E    ||mov al, byte ptr ds:
00402D1D   |.3C 2D   ||cmp al, 2D    ;剔除'-'
00402D1F   |.74 26   ||je short loc_402D47
00402D21   |.3C 41   ||cmp al, 41;
00402D23   |.7C 04   ||jl short loc_402D29
00402D25   |.3C 46   ||cmp al, 46;
00402D27   |.7E 18   ||jle short loc_402D41
00402D29 l |>3C 61   ||cmp al, 61
00402D2B   |.7C 04   ||jl short loc_402D31
00402D2D   |.3C 66   ||cmp al, 66
00402D2F   |.7E 10   ||jle short loc_402D41
00402D31 l |>3C 30   ||cmp al, 30
00402D33   |.0F8C FC00 ||jl loc_402E35;跳向失败
00402D39   |.3C 39   ||cmp al, 39
00402D3B   |.0F8F F400 ||jg loc_402E35;跳向失败,综合上面的比较,可得出每一位注册码都必须在0123456789ABCDEFabcdef范围内
00402D41 l |>46      ||inc esi
00402D42   |.8975 E8   ||mov dword ptr ss:, esi
00402D45   |.^ EB CE   |\jmp short loc_402D15
00402D47 l |>8D4D DC   |lea ecx,
......
00402E0E   |.6A 10   |push 10      ; /Arg3 = 10
00402E10   |.50      |push eax       ; |Arg2
00402E11   |.51      |push ecx       ; |Arg1
00402E12   |.E8 C73F02 |call _strtoul;将字符串转换成无符号长整型数
00402E17   |.83C4 0C   |add esp, 0C
00402E1A   |.88043E    |mov byte ptr ds:, al
00402E1D   |.46      |inc esi
00402E1E   \.^ EB 90   \jmp short loc_402DB0;循环将"123456789abc"转换成数值0x123456789abc
......
我们再看看regkey_check函数里面:
CPU Disasm
Address    Hex dump      Command                              Comments
00402B70 r /$81EC 0401 sub esp, 104
00402B76   |.53      push ebx
00402B77   |.55      push ebp
00402B78   |.56      push esi
00402B79   |.57      push edi
00402B7A   |.E8 37FD03 call AfxGetModuleState(void)         ; [Galaxy3D.AfxGetModuleState
00402B7F   |.8B8C24 1C mov ecx, dword ptr ss:
00402B86   |.8B40 0C   mov eax, dword ptr ds:
00402B89   |.68 688245 push offset Type                     ; /Type = "encdata"
00402B8E   |.51      push ecx                               ; |Name =>
00402B8F   |.50      push eax                               ; |hModule
00402B90   |.FF15 5492 call dword ptr ds:[<&KERNEL32.FindReso ; \KERNEL32.FindResourceA ;这是比较特别的地方,他要读取一个叫做"encdata"资源!
00402B96   |.8BF0      mov esi, eax
00402B98   |.85F6      test esi, esi
00402B9A   |.0F84 9400 jz loc_402C34
00402BA0   |.E8 11FD03 call AfxGetModuleState(void)         ; [Galaxy3D.AfxGetModuleState
00402BA5   |.8B40 0C   mov eax, dword ptr ds:
00402BA8   |.56      push esi                               ; /hRes
00402BA9   |.50      push eax                               ; |hModule
00402BAA   |.FF15 5092 call dword ptr ds:[<&KERNEL32.SizeofRe ; \KERNEL32.SizeofResource
00402BB0   |.8BC8      mov ecx, eax ;返回值是"encdata"的大小!
00402BB2   |.85C9      test ecx, ecx
00402BB4   |.74 7E   jz short loc_402C34
00402BB6   |.8B9C24 20 mov ebx, dword ptr ss: ;arg.3 =6,是常数
00402BBD   |.85DB      test ebx, ebx
00402BBF   |.74 73   jz short loc_402C34
00402BC1   |.33D2      xor edx, edx
00402BC3   |.F7F3      div ebx                  ; EDX:EAX / ebx,即SizeofResource("encdata")/6
00402BC5   |.85D2      test edx, edx            ;不能被6整除就失败了!
00402BC7   |.75 6B   jnz short loc_402C34
00402BC9   |.8BC1      mov eax, ecx
00402BCB   |.F7F3      div ebx               
00402BCD   |.8BF8      mov edi, eax         ;同样,SizeofResource("encdata")/6的商给edi
00402BCF   |.83FF 01   cmp edi, 1
00402BD2   |.897C24 10 mov dword ptr ss:, edi   ;记住了,local.64=SizeofResource("encdata")/6的商
00402BD6   |.72 5C   jb short loc_402C34
00402BD8   |.E8 D9FC03 call AfxGetModuleState(void)         ; [Galaxy3D.AfxGetModuleState
00402BDD   |.8B40 0C   mov eax, dword ptr ds:
00402BE0   |.56      push esi                               ; /hResource
00402BE1   |.50      push eax                               ; |hModule
00402BE2   |.FF15 4C92 call dword ptr ds:
00402BE8   |.85C0      test eax, eax
00402BEA   |.74 48   jz short loc_402C34
00402BEC   |.50      push eax
00402BED   |.FF15 4892 call dword ptr ds:[<&KERNEL32.LockResources]
00402BF3   |.8BF0      mov esi, eax ;这个返回值很重要了,是"encdata"数据的地址!
00402BF5   |.85F6      test esi, esi
00402BF7   |.74 3B   jz short loc_402C34
00402BF9   |.8B9424 18 mov edx, dword ptr ss:
00402C00   |.53      push ebx                               ; /Arg3
00402C01   |.8D4424 18 lea eax,    
00402C05   |.52      push edx                               ; |Arg2
00402C06   |.50      push eax                               ; |Arg1
00402C07   |.E8 A40300 call keyGenerator ;这是我自己添加的函数名字,不要误会了,生成最后变换后的注册码
00402C0C   |.83C4 0C   add esp, 0C
00402C0F   |.85C0      test eax, eax
00402C11   |.74 21   jz short loc_402C34
00402C13   |.33C0      xor eax, eax
00402C15   |.85FF      test edi, edi
00402C17   |.7E 1B   jle short loc_402C34
00402C19   |.8BD6      mov edx, esi
00402C1B l |>8BCB      /mov ecx, ebx
00402C1D   |.8D7C24 14 |lea edi, ;
00402C21   |.8BF2      |mov esi, edx
00402C23   |.33ED      |xor ebp, ebp
00402C25   |.F3:A6   |repe cmps byte ptr ds:, byte ptr es: ;这是连续比较,当遇到不相等的时候停止,ecx会在每次比较时候递减
00402C27   |.74 18   |je short loc_402C41                           ;esi是"encdata"的地址,edi是注册码已经变换后的数值
00402C29   |.8B4C24 10 |mov ecx, dword ptr ss: ;其实arg.4=local.64=SizeofResource("encdata")/6的商...OD分析的有些问题,地址是一样的
00402C2D   |.40      |inc eax
00402C2E   |.03D3      |add edx, ebx
00402C30   |.3BC1      |cmp eax, ecx   ;可分析得到,他是把转化后的注册码与"encdata"每6位一次比较,失败了就跳向下一个6byte的数据再比较
00402C32   |.^ 7C E7   \jl short loc_402C1B
00402C34 l |>5F      pop edi
00402C35   |.5E      pop esi
00402C36   |.5D      pop ebp
00402C37   |.33C0      xor eax, eax
00402C39   |.5B      pop ebx
00402C3A   |.81C4 0401 add esp, 104
00402C40   |.C3      retn
现在就是要看他是是怎么得到变换后的注册码的了,看keyGenerator函数:

CPU Disasm
Address    Hex dump      Command                                                Comments
00402FB0 k /$51      push ecx                                                 ; Galaxy3D.keyGenerator(guessed Arg1,Arg2,Arg3)
00402FB1   |.53      push ebx
00402FB2   |.55      push ebp
00402FB3   |.56      push esi
00402FB4   |.57      push edi
00402FB5   |.8D4C24 10 lea ecx,
00402FB9   |.E8 A2FFFF call loadMagicNum ;其实没用
00402FBE   |.8B5C24 20 mov ebx, dword ptr ss:
00402FC2   |.8B6C24 18 mov ebp, dword ptr ss:
00402FC6   |.8B7424 1C mov esi, dword ptr ss:
00402FCA   |.8BCB      mov ecx, ebx
00402FCC   |.8BC1      mov eax, ecx
00402FCE   |.8BFD      mov edi, ebp
00402FD0   |.C1E9 02   shr ecx, 2
00402FD3   |.F3:A5   rep movs dword ptr es:, dword ptr ds:
00402FD5   |.8BC8      mov ecx, eax
00402FD7   |.83E1 03   and ecx, 00000003
00402FDA   |.F3:A4   rep movs byte ptr es:, byte ptr ds:
00402FDC   |.33FF      xor edi, edi
00402FDE   |.85DB      test ebx, ebx
00402FE0   |.7E 45   jle short loc_403027
00402FE2 l |>8B5424 1C /mov edx, dword ptr ss:
00402FE6   |.33C9      |xor ecx, ecx
00402FE8   |.8A0C17    |mov cl, byte ptr ds: ;我们转换了的注册码,0x123456789abc
00402FEB   |.51      |push ecx                                                ; /Arg1
00402FEC   |.8D4C24 14 |lea ecx,                                     ; |
00402FF0   |.E8 ABFFFF |call copyDw                                             ; \Galaxy3D.copyDw
00402FF5   |.33F6      |xor esi, esi
00402FF7 l |>8D4C24 10 |/lea ecx,
00402FFB   |.E8 70FFFF ||call numShift ;这个是简单的变换
00403000   |.8BC8      ||mov ecx, eax
00403002   |.8BD0      ||mov edx, eax
00403004   |.C1E9 18   ||shr ecx, 18
00403007   |.C1EA 10   ||shr edx, 10
0040300A   |.32CA      ||xor cl, dl
0040300C   |.8BD0      ||mov edx, eax
0040300E   |.C1EA 08   ||shr edx, 8
00403011   |.32CA      ||xor cl, dl
00403013   |.8A142E    ||mov dl, byte ptr ds:
00403016   |.32CA      ||xor cl, dl
00403018   |.32C8      ||xor cl, al
0040301A   |.880C2E    ||mov byte ptr ds:, cl
0040301D   |.46      ||inc esi
0040301E   |.3BF3      ||cmp esi, ebx
00403020   |.^ 7C D5   |\jl short loc_402FF7
00403022   |.47      |inc edi
00403023   |.3BFB      |cmp edi, ebx
00403025   |.^ 7C BB   \jl short loc_402FE2
00403027 l |>5F      pop edi
00403028   |.5E      pop esi
00403029   |.5D      pop ebp
0040302A   |.B8 010000 mov eax, 1
0040302F   |.5B      pop ebx
00403030   |.59      pop ecx
00403031   \.C3      retn

啊....很多移位指令,不分析了,很简单的.
最终我将完整的算法转成C语言后,是这样的:
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <map>
inline int numShift(unsigned int &num)
{

    //num = num + ((((((num * 13) << 4) + num) << 8)) - num) * 4 + 0x269ec3; 原始翻译过来是这样的
    num = 0x343FD * num + 0x269EC3;
    return num & 0x7FFFFFFF;


}

bool keyGenerator(unsigned char *keyset, const unsigned char *reg_hex_num, unsigned int blockSize)
{
    unsigned int counter;
    unsigned int num_;
    unsigned int num;
    unsigned char n;
    memcpy(keyset, reg_hex_num, blockSize);
    for (int i = 0; i < (signed int)blockSize; ++i)
    {
      num = reg_hex_num;
      counter = 0;
      do
      {
            num_ = numShift(num);
            memcpy(&n, &num_, 4);
            keyset ^= n ^ n;
            ++counter;
      } while (counter < blockSize);
    }
    return true;
}

void str2hex(unsigned char *dest, unsigned char src)
{
    if (src <= '9' && src >= '0')
    {
      *dest = src - 0x30;
      *dest <<= 4;
    }
    else if (src <= 'F'&&src >= 'A')
    {
      *dest = src - 0x37;
      *dest <<= 4;
    }
    else if (src <= 'f'&&src >= 'a')
    {
      *dest = src - 0x57;
      *dest <<= 4;
    }

    if (src <= '9' && src >= '0')
    {
      *dest += src - 0x30;

    }
    else if (src <= 'F'&&src >= 'A')
    {
      *dest += src - 0x37;
    }
    else if (src <= 'f'&&src >= 'a')
    {
      *dest += src - 0x57;
    }


}
int main()
{
    unsigned char regkeys[] = { "D6DCD3F1C6BC" }; //输入的注册码
    unsigned char keyset{0};
    unsigned char reg_hex_num;
    for (int i = 0; i < 6; ++i)//注册码长度必须是12
    {
      str2hex(reg_hex_num, regkeys);
    }
    keyGenerator(keyset, reg_hex_num, 6);//注册码长度必须是12
    for (size_t i = 0; i < 6; i++)
    {
      printf("%02X", keyset);//输出变换后的注册码,,,,它是用来和"encdata"比较的,在"encdata"中找到对应的,就注册成功了!
    }

}
encdata与变换后的注册码比较:


我希望谁能写出注册机,并给出完整的思路.
分析的不太好,请见谅.算法总结:将注册码经过一系列变换得到的结果与资源里面叫"encdata"的数据比较,
能在"encdata"找到变换后的注册码,那就注册成功了!
.看C代码很容易看懂的.顺便一提,软件是有自校验的.
当输入注册码"D6DCD3F1C6BC"时,可以验证:

朱朱你堕落了 发表于 2016-10-16 00:29

本帖最后由 hahacker 于 2016-10-16 00:31 编辑

想请教一下@红绡枫叶 大牛,对于这个VC6.0的程序,您是使用哪个lib制作的sig文件的。本意是我也试着也做个VC静态链接库的sig文件,就像你分析中的能识别MFC中的各种函数,
但是却没有成功,是不是我选择的lib库不对。。。。我选择的是这个。

结果好像不是,我又把mfc42.lib先弄成pat。但是好像也不对。
MFC32.lib解析出来的pat都是C函数,貌似都不是MFC类库函数。


结果生成sig文件后,IDA加载,找个了小程序,只解析了5个函数。
是不是因为制作的sig对不上号,只是C基础函数,所以在MFC对应的类库里找
不到,而只找到了5个对上号的,所以解析了5个。。。


请问,VC6 release生成的,应该用哪个lib或是dll文件用来制作sig呢?
初学IDA,请多多指点,谢谢。

朱朱你堕落了 发表于 2016-10-16 12:13

把MFC静态库全部放一起做一个sig文件, 这句的意思是把所有静态链接的库都集中在一起,做一个sig.

不是静态连接,解析MFC的dll可以识别程序中导入的函数名。。。。这句话没有看懂是啥意思。求解释。IDA本身识别?但是貌似它本身识别不出来。。

ajm3 发表于 2014-12-21 23:14

膜拜会算法分析的大大
支持http://bbs.sunwy.org/data/attachment/forum/201412/21/222556o2fx5fjaczfhjoiw.jpg

trustguan 发表于 2014-12-22 08:25

我什么时候才能像楼主这么腻害{:301_980:}

Jacve 发表于 2014-12-22 11:14

膜拜楼主ing

tangdragon 发表于 2014-12-22 17:49

算法都分析出来了,注册机还用得着求别人写吗?

红绡枫叶 发表于 2014-12-22 22:41

tangdragon 发表于 2014-12-22 17:49
算法都分析出来了,注册机还用得着求别人写吗?

这个算法求逆比分析它难多了....

Hmily 发表于 2015-1-8 01:04

分享挺详细,代码最好用代码框属性标记下方便阅读,加精鼓励,期待更多作品。

yyz219 发表于 2015-1-8 11:44

我看不明白 谢谢

红绡枫叶 发表于 2015-1-8 20:13

Hmily 发表于 2015-1-8 01:04
分享挺详细,代码最好用代码框属性标记下方便阅读,加精鼓励,期待更多作品。

已重新编辑....感谢Hmily版主的鼓励:)

bin3008 发表于 2015-1-8 21:33

谢谢分享爱那个
页: [1] 2
查看完整版本: Galaxy 3D screensaver算法分析