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"时,可以验证:
本帖最后由 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,请多多指点,谢谢。
把MFC静态库全部放一起做一个sig文件, 这句的意思是把所有静态链接的库都集中在一起,做一个sig.
不是静态连接,解析MFC的dll可以识别程序中导入的函数名。。。。这句话没有看懂是啥意思。求解释。IDA本身识别?但是貌似它本身识别不出来。。 膜拜会算法分析的大大
支持http://bbs.sunwy.org/data/attachment/forum/201412/21/222556o2fx5fjaczfhjoiw.jpg 我什么时候才能像楼主这么腻害{:301_980:} 膜拜楼主ing 算法都分析出来了,注册机还用得着求别人写吗? tangdragon 发表于 2014-12-22 17:49
算法都分析出来了,注册机还用得着求别人写吗?
这个算法求逆比分析它难多了.... 分享挺详细,代码最好用代码框属性标记下方便阅读,加精鼓励,期待更多作品。 我看不明白 谢谢 Hmily 发表于 2015-1-8 01:04
分享挺详细,代码最好用代码框属性标记下方便阅读,加精鼓励,期待更多作品。
已重新编辑....感谢Hmily版主的鼓励:) 谢谢分享爱那个
页:
[1]
2