现在接着分析生成的DLL文件在做些什么。首先,我们到系统目录下,把生成的2个文件复制出来。
使用OD载入JDPlayer.exe后,直接F9运行.使用OD带的插件StrongOD把生成的DLL注入,看有什么变化。
在模块窗口可以看该DLL已经注入,注入后,我们支看下DLL里有哪些字符值得我们参考的。
字符串可以看到很多有用的信息,我们把这些关键地方都下一个断点,重新加载播放器注入,看它会断在哪个地方。
断在加载DLL处,继续F8往下走,我们发现它对 CreateWindowExA函数进行了内存属性更改,并直接HOOK了该函数
[Asm] 纯文本查看 复制代码 10010662 |. FF15 50D00110 call dword ptr ds:[<&KERNEL32.GetCurrent>; [GetCurrentProcess
10010668 |. 33C9 xor ecx,ecx
1001066A |. 894D F4 mov [local.3],ecx
1001066D |. 894D F8 mov [local.2],ecx
10010670 |. 8D4D F4 lea ecx,[local.3]
10010673 |. 51 push ecx ; /pOldProtect
10010674 |. 6A 40 push 0x40 ; |NewProtect = PAGE_EXECUTE_READWRITE
10010676 |. 57 push edi ; |Size
10010677 |. 53 push ebx ; |Address
10010678 |. 50 push eax ; |hProcess
10010679 |. 8945 FC mov [local.1],eax ; |
1001067C |. FF15 4CD00110 call dword ptr ds:[<&KERNEL32.VirtualPro>; \VirtualProtectEx
10010682 |. 85C0 test eax,eax
10010684 |. 0F84 E1000000 je sunflove.1001076B
1001068A |. 8B45 FC mov eax,[local.1]
1001068D |. 8D55 F8 lea edx,[local.2]
10010690 |. 52 push edx ; /pOldProtect
10010691 |. 6A 40 push 0x40 ; |NewProtect = PAGE_EXECUTE_READWRITE
10010693 |. 6A 6C push 0x6C ; |Size = 6C (108.)
10010695 |. 56 push esi ; |Address
10010696 |. 50 push eax ; |hProcess
10010697 |. FF15 4CD00110 call dword ptr ds:[<&KERNEL32.VirtualPro>; \VirtualProtectEx
HOOK:
[Asm] 纯文本查看 复制代码 10010717 |. E8 04F9FFFF call sunflove.10010020 ;修改函数头5字节
10010706 |. 50 push eax ; /RegionSize
10010707 |. 8D46 0C lea eax,dword ptr ds:[esi+0xC] ; |
1001070A |. 50 push eax ; |RegionBase
1001070B |. 51 push ecx ; |hProcess
1001070C |. FF15 48D00110 call dword ptr ds:[<&KERNEL32.FlushInstr>; \FlushInstructionCache 刷新
10010712 |. 8D4E 0C lea ecx,dword ptr ds:[esi+0xC]
10010715 |> 8BC3 mov eax,ebx
10010717 |. E8 04F9FFFF call sunflove.10010020
1001071C |. 8B55 0C mov edx,[arg.2]
1001071F |. 57 push edi ; /RegionSize
10010720 |. 8D46 2C lea eax,dword ptr ds:[esi+0x2C] ; |
10010723 |. 50 push eax ; |RegionBase
10010724 |. 8B45 FC mov eax,[local.1] ; |
10010727 |. 50 push eax ; |hProcess
10010728 |. 897E 04 mov dword ptr ds:[esi+0x4],edi ; |
1001072B |. 891E mov dword ptr ds:[esi],ebx ; |
1001072D |. 8956 08 mov dword ptr ds:[esi+0x8],edx ; |
10010730 |. FF15 48D00110 call dword ptr ds:[<&KERNEL32.FlushInstr>; \FlushInstructionCache
往下走,就完了。这个CALL就是加载DLL后,对CreateWindowExA进行了HOOK。现在我们进入HOOK的函数,看它会做些什么
[Asm] 纯文本查看 复制代码 [color=#ff0000]75AAD23E >- E9 5D3F569A jmp sunflove.100111A0[/color]
关键位置:100111A0
[Asm] 纯文本查看 复制代码 100111A0 . 55 push ebp
100111A1 . 8BEC mov ebp,esp
100111A3 . 56 push esi
100111A4 . 8B35 74D00110 mov esi,dword ptr ds:[<&KERNEL32.OutputD>; kernel32.OutputDebugStringA
100111AA . 68 E0010210 push sunflove.100201E0 ; /HookCreateWindowExA\r\n
100111AF . FFD6 call esi ; \OutputDebugStringA
100111B1 . A1 38990410 mov eax,dword ptr ds:[0x10049938]
100111B6 . 8338 00 cmp dword ptr ds:[eax],0x0
100111B9 . 74 27 je Xsunflove.100111E2
100111BB . 68 2CAA0410 push sunflove.1004AA2C ; ,
100111C0 . E8 DBF5FFFF call sunflove.100107A0
100111C5 . 83C4 04 add esp,0x4
100111C8 . 68 C4010210 push sunflove.100201C4 ; Unhook CreateWindowExA!\r\n
100111CD . FFD6 call esi
100111CF . 68 B8010210 push sunflove.100201B8 ; 正正补丁!\r\n
100111D4 . FFD6 call esi
100111D6 . E8 C5FDFFFF call sunflove.10010FA0
100111DB . 68 AC010210 push sunflove.100201AC ; 补丁完成!\r\n
100111E0 . FFD6 call esi
100111E2 > 5E pop esi
100111E3 . 5D pop ebp
100111E4 .- FF25 2CAA0410 jmp dword ptr ds:[0x1004AA2C]
只有在创建新窗口的时候它才会调用该函数,执行这里面的代码。到此,我们F9让程序运行起来。用播放器添加视频文件,会弹出新对话框,看它会不会调用该函数执行HOOK代码。
并未调用该函数,看来注入时机太晚了。要在创建主窗口时就要注入了。
通过前面的分析,我们知道DLL在注入时就会对 窗口函数进行HOOK,并未对播放器操作所以就不会检测和报错,那么我们直接OD载入播放器后,把DLL注入进行,F9运行。此时就看到DLL直接对窗口函数进行了HOOK,并在创建窗口时,执行了HOOK的代码。来到了地址:100111A0 里
[Asm] 纯文本查看 复制代码 100111A0 . 55 push ebp
100111A1 . 8BEC mov ebp,esp
100111A3 . 56 push esi
100111A4 . 8B35 74D00110 mov esi,dword ptr ds:[<&KERNEL32.OutputD>; kernel32.OutputDebugStringA
100111AA . 68 E0010210 push sunflove.100201E0 ; /HookCreateWindowExA\r\n
100111AF . FFD6 call esi ; \OutputDebugStringA
100111B1 . A1 38990410 mov eax,dword ptr ds:[0x10049938]
100111B6 . 8338 00 cmp dword ptr ds:[eax],0x0
100111B9 . 74 27 je Xsunflove.100111E2
100111BB . 68 2CAA0410 push sunflove.1004AA2C ; ,
100111C0 . E8 DBF5FFFF call sunflove.100107A0
100111C5 . 83C4 04 add esp,0x4
100111C8 . 68 C4010210 push sunflove.100201C4 ; Unhook CreateWindowExA!\r\n
100111CD . FFD6 call esi
100111CF . 68 B8010210 push sunflove.100201B8 ; 正正补丁!\r\n
100111D4 . FFD6 call esi
100111D6 . E8 C5FDFFFF call sunflove.10010FA0
100111DB . 68 AC010210 push sunflove.100201AC ; 补丁完成!\r\n
100111E0 . FFD6 call esi
100111E2 > 5E pop esi
100111E3 . 5D pop ebp
100111E4 .- FF25 2CAA0410 jmp dword ptr ds:[0x1004AA2C]
看到上面功能就是输出调试语句,中间有一个关键的CALL:
100111D6 . E8 C5FDFFFF call sunflove.10010FA0 进入看到是原先找字符串时下过的断
[Asm] 纯文本查看 复制代码 10010FA0 /$ 55 push ebp
10010FA1 |. 8BEC mov ebp,esp
10010FA3 |. 83EC 38 sub esp,0x38
10010FA6 |. 6A 00 push 0x0 ; /pModule = NULL
10010FA8 |. FF15 58D00110 call dword ptr ds:[<&KERNEL32.GetModuleH>; \GetModuleHandleA
10010FAE |. 50 push eax
10010FAF |. E8 4CFFFFFF call sunflove.10010F00
10010FB4 |. 83C4 04 add esp,0x4
10010FB7 |. FF15 44D00110 call dword ptr ds:[<&KERNEL32.GetCurrent>; [GetCurrentProcessId
10010FBD |. 85C0 test eax,eax
10010FBF |. 79 07 jns Xsunflove.10010FC8
10010FC1 |. 83C8 FF or eax,0xFFFFFFFF
10010FC4 |. 8BE5 mov esp,ebp
10010FC6 |. 5D pop ebp
10010FC7 |. C3 retn
往下看,这是一个关键的CALL,F9运行断下后,F8往下走。
[Asm] 纯文本查看 复制代码 [color=#0000ff]10010FD1 |. 8B1D 38990410 mov ebx,dword ptr ds:[0x10049938] ; JDPlayer.0040B50B[/color]
10010FD7 |. 8B35 78D00110 mov esi,dword ptr ds:[<&KERNEL32.VirtualPro>; kernel32.VirtualProtect
10010FDD |. 83C4 04 add esp,0x4
10010FE0 |. 8D45 FC lea eax,[local.1]
10010FE3 |. 50 push eax ; /pOldProtect
10010FE4 |. 6A 40 push 0x40 ; |NewProtect = PAGE_EXECUTE_READWRITE
10010FE6 |. 6A 06 push 0x6 ; |Size = 6
10010FE8 |. 53 push ebx ; |Address => JDPlayer.0040B50B
10010FE9 |. FFD6 call esi ; \VirtualProtect
代码中对播放器一个地址:0040B50B 进行了属性更改,F8往下走看到关键地方:
[Asm] 纯文本查看 复制代码 1001102F |. 68 94010210 push sunflove.10020194 ; /patch
10011034 |. 68 84010210 push sunflove.10020184 ; |/sunflover.dll
10011039 |. C745 C8 609C8>mov [local.14],0xFA839C60 ; ||
10011040 |. C745 CC 00741>mov [local.13],0x83177400 ; ||
10011047 |. C745 D0 7AFC0>mov [local.12],0x750EFC7A ; ||
1001104E |. C745 D4 11807>mov [local.11],0x127A8011 ; ||
10011055 |. C745 D8 2D750>mov [local.10],0x520B752D ; ||
1001105C |. C645 DC B8 mov byte ptr ss:[ebp-0x24],0xB8 ; ||
10011060 |. C745 E1 FFD08>mov dword ptr ss:[ebp-0x1F],0xC483D0FF ; ||
10011067 |. C745 E5 049D6>mov dword ptr ss:[ebp-0x1B],0x8B619D04 ; ||
1001106E |. C745 E9 44240>mov dword ptr ss:[ebp-0x17],0x85042444 ; ||
10011075 |. 66:C745 ED FF>mov word ptr ss:[ebp-0x13],0x68FF ; ||
1001107B |. C645 F3 C3 mov byte ptr ss:[ebp-0xD],0xC3 ; ||
1001107F |. FF15 58D00110 call dword ptr ds:[<&KERNEL32.GetModuleHand>; |\GetModuleHandleA
10011085 |. 50 push eax ; |hModule
10011086 |. FF15 54D00110 call dword ptr ds:[<&KERNEL32.GetProcAddres>; \GetProcAddress
1001108C |. 8D53 06 lea edx,dword ptr ds:[ebx+0x6]
1001108F |. 8945 DD mov dword ptr ss:[ebp-0x23],eax
10011092 |. 8955 EF mov dword ptr ss:[ebp-0x11],edx
10011095 |. B9 0B000000 mov ecx,0xB
1001109A |. 8D75 C8 lea esi,[local.14]
1001109D |. 68 70010210 push sunflove.10020170 ; /inject complete!\r\n
100110A2 |. F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[e>; |
100110A4 |. FF15 74D00110 call dword ptr ds:[<&KERNEL32.OutputDebugSt>; \OutputDebugStringA
从汇编代码来看,它对一个地址(新申请的一空间)进行了填充,并加载DLL调用了本身内的函数:patch(这是一个导出函数,使用工具可以看到DLL确实导出了该函数)
现在我们直接来到播放器位置:0040B50B
[Asm] 纯文本查看 复制代码 0040B50B 68 00003800 push 0x380000
0040B510 C3 retn
上面汇编代码中,进行属性更改后,也对该地址进行了HOOK,当执行到该地址就进入到0x380000 ,我们看下该地址有什么内容
[Asm] 纯文本查看 复制代码 00380000 60 pushad
00380001 9C pushfd
00380002 83FA 00 cmp edx,0x0
00380005 74 17 je X0038001E
00380007 837A FC 0E cmp dword ptr ds:[edx-0x4],0xE
0038000B 75 11 jnz X0038001E
0038000D 807A 12 2D cmp byte ptr ds:[edx+0x12],0x2D
00380011 75 0B jnz X0038001E
00380013 52 push edx
00380014 B8 C0100110 mov eax,sunflove.patch
00380019 FFD0 call eax
0038001B 83C4 04 add esp,0x4
0038001E 9D popfd
0038001F 61 popad
00380020 8B4424 04 mov eax,dword ptr ss:[esp+0x4]
00380024 85FF test edi,edi
00380026 68 11B54000 push 0x40B511
0038002B C3 retn
代码是不是很熟悉?对,就是前面它填充的数据。 这里有一个地方就是上面获取的一个导出函数地址:
[Asm] 纯文本查看 复制代码 00380014 B8 C0100110 [color=#ff0000]mov eax,0x100110C0;patch[/color]
00380019 FFD0 call eax
说了那么多,我们直接跟一下,在上面关键位置下断,F9运行后,添加视频,看是否会断在此处。
一直断在了00380000 60 pushad
我们直接取消断点,让播放器加载起来后,选中加密视频后,在下断。断下后,该处在一直做判断。先判断EDX是否为0,是0返回执行到播放器自身处理,还有比较EDX-4,EDX+0X12的内空是否为0XE,0X2D,检测这个主要是当EDX的值是机器码内容时,就检测它的位数和第5位是不是-,我们的机器码是:1111-2222-3333.通过这些判断后,确定是机器码后,执行HOOK的代码,进入到0x100110C0 对EDX的值进行替换。
替换的关键处:
[Asm] 纯文本查看 复制代码 10012674 > \8B448E E4 mov eax,dword ptr ds:[esi+ecx*4-0x1C]
10012678 . 89448F E4 mov dword ptr ds:[edi+ecx*4-0x1C],eax
1001267C > 8B448E E8 mov eax,dword ptr ds:[esi+ecx*4-0x18]
10012680 . 89448F E8 mov dword ptr ds:[edi+ecx*4-0x18],eax
10012684 > 8B448E EC mov eax,dword ptr ds:[esi+ecx*4-0x14]
10012688 . 89448F EC mov dword ptr ds:[edi+ecx*4-0x14],eax
1001268C > 8B448E F0 mov eax,dword ptr ds:[esi+ecx*4-0x10]
10012690 . 89448F F0 mov dword ptr ds:[edi+ecx*4-0x10],eax
10012694 > 8B448E F4 mov eax,dword ptr ds:[esi+ecx*4-0xC]
10012698 . 89448F F4 mov dword ptr ds:[edi+ecx*4-0xC],eax
1001269C > 8B448E F8 mov eax,dword ptr ds:[esi+ecx*4-0x8]
100126A0 . 89448F F8 mov dword ptr ds:[edi+ecx*4-0x8],eax
100126A4 > 8B448E FC mov eax,dword ptr ds:[esi+ecx*4-0x4]
100126A8 . 89448F FC mov dword ptr ds:[edi+ecx*4-0x4],eax
100126AC . 8D048D 000000>lea eax,dword ptr ds:[ecx*4]
100126B3 . 03F0 add esi,eax
100126B5 . 03F8 add edi,eax
100126B7 > FF2495 C02601>jmp dword ptr ds:[edx*4+0x100126C0]
替换完后,在执行HOOK前的代码回到播放器空间中
[Asm] 纯文本查看 复制代码
00380020 8B4424 04 mov eax,dword ptr ss:[esp+0x4]
00380024 85FF test edi,edi
[color=#ff0000]00380026 68 11B54000 push 0x40B511
0038002B C3 retn
到此该DLL替换机器码的功能就分析完了。
运行播放器后,机器码也成功替换,输入密码就能直接播放样本视频。
总结:
1、DLL启动直接对函数:CreateWindowExA 进行了HOOK,当播放器执行到该函数后,对播放器地址:0040B50B 进行了HOOK
2、HOOK的关键函数功能是:先判断EDX是否为0,是0返回执行到播放器自身处理,还有比较EDX-4,EDX+0X12的内空是否为0XE,0X2D,检测这个主要是当EDX的值是机器码内容时,就检测它的位数是否为:0XE和第5位是不是-,我们的机器码是:1111-2222-3333.通过这些判断确定是机器码后,执行HOOK的代码,进入到0x100110C0 对EDX的值进行替换。
3、分析发现机器码信息有CPU,硬盘,网卡,当机器码加密都选中了,该DLL才能使用。还有2种可能,单独选一种硬件或选2种硬件生成的机器码时该DLL就无效了;不过分析这方法后,我们对替换的代码手动修改下就能实现另外的2种机器码的替换了。4、作者把执行的每一步都做了调试输出,可以很方便看到执行到哪步。大家可以用工具查看到。
5、该套工具的功能就都分析完了,能力有限,菜鸟水平,不足的地方还请见谅。 |