0%

WeGame补丁

自定义wegame入口

工具

调试工具: x64dbg

二进制编辑器:010 Editor

PE工具: Stud_PE

dump工具: Scylla

基本思路

修改wegame的入口点,优先执行自定义的dll导出的函数,验证通过后再跳转回原始的wegame入口点执行下去,如果验证不通过将直接exit掉

操作流程

  1. 使用Stud_PE添加"YourFatherServer.dll"导出的"_entry@0"函数

  2. 在wegame.exe的.text段中找一段足够长的空白区域来存放添加的新代码,这段代码这里可以把它叫做shellcode

    ​ 用010 Editor配合正则来从wegame.exe中搜索,找到空白区域在.text的相对偏移记录下来。要加入的shellcode大概有170多字节,所以最好找一块180字节的空白区域。空白指令可以用int3、nop或者0x00来定位,正则如下:

    1
    (?<!\xcc)(\xcc){180}或(?<!\x00)(\x00){180}

    ​ 要注意的是空白区域得是.text段的,找到后把这块区域的起始文件偏移减去.text段的起始文件偏移,得到这块空白区域在.text段的相对偏移。进入调试模式后,把.text段的起始虚拟地址加上这个偏移量就可以得到空白区域在内存的虚拟地址了。如果没有找到这么大的空白区域可以用Stud_PE加入一个新的.text段,把shellcode放到这里

  3. 使用x32dbg挂起wegame.exe,运行到wegame.exe的OEP处,并把OEP记录下来

  4. 查看进程的内存布局获取进程的基址,把这个地址跟第二步记录的空白区域偏移地址相加,得到空白区域的虚拟地址,把这个地址叫做fill_point

  5. 跳转到fill_point,把shellcode复制到这里,这段shellcode分成两部分,一部分是数据另一部分是代码,数据放着两个字符串,一个是"YourFatherServer.dll",另一个是"_entry@0",这在shellcode获取entry地址的时候要用。把代码部分的开始部分的地址记录下来,叫做shellcode_entry

  6. 回到OEP,往下找到第一个call指令,并把它的跳转地址记录下来,叫做origin_routine。然后把跳转地址修改为shellcode_entry

  7. 再跳到shellcode_enetry,并把最后一条jmp指令的跳转地址修改为origin_routine

  8. 打开Scylla,填入之前记录的OEP,点击重新查找导入表,再点击获取导入表,最后点击转存出去

shellcode依赖kernelbase.dll的函数的入口RVA

GetModuleHandleA:0x11CCE0

GetProcAddress: 0x1171C0

exit: 0x16E0D0

shellcode栈帧分布

1
2
3
4
5
6
dword ptr [ebp - 4]    # 进程基地址
dword ptr [ebp - 8] # kernelbase.dll模块基地址
dword ptr [ebp - 0xC] # GetModuleHandleA虚拟地址
dword ptr [ebp - 0x10] # GetProcAddress虚拟地址
dword ptr [ebp - 0x14] # "YourFatherServer.dll"字符串虚拟地址
dword ptr [ebp - 0x18] # "_entry@0"字符串虚拟地址

shellcode需要根据实际情况调整的地方

shellcode最后的jmp的跳转地址要在调试时根据实际情况调整

shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#
# 数据区域
#

YourFatherServerModuleName BYTE 'YourFatherServer.dll', 0
EntryFuncName BYTE '_entry@0', 0, 0, 0

#
# 代码区域
#

# 缓冲区24字节
push ebp
mov ebp, esp
sub esp, 0x18

push edi
push esi
push ecx
push eax

# int3(0xCC)指令初始化缓冲区
lea edi, [ebp - 0x18]
mov ecx, 0x6
mov eax, 0xCCCCCCCC
rep stosd

# 获取PEB基地址
mov eax, dword ptr fs:[0x30]

# PEB结构的ImageBaseAddress字段记录了映射到内存的基地址,相对偏移量为8字节
mov ecx, dword ptr ds:[eax + 0x8]
mov dword ptr [ebp - 4], ecx

# PEB_LDR_DATA基地址
mov eax, dword ptr ds:[eax + 0xC]

# 首个item记录的是kernelbase.dll,获取它的LDR_DATA_TABLE_ENTRY结构体基地址
mov esi, dword ptr ds:[eax + 0x1C]
lodsd

# 提取kernelbase.dll基地址
mov eax, dword ptr ds:[eax + 0x8]
mov dword ptr [ebp - 8], eax

# 计算GetModuleHandleA绝对地址
mov eax, 0x11CCE0
add eax, dword ptr [ebp - 8]
mov dword ptr [ebp - 0xC], eax

# 计算GetProcAddress绝对地址
mov eax, 0x1171C0
add eax, dword ptr [ebp - 8]
mov dword ptr [ebp - 0x10], eax

# 获取当前指令eip
call _MarkPoint
_MarkPoint:
pop eax

# 计算"YourFatherServer.dll"字符串绝对地址
mov ecx, eax
sub ecx, 0x6D
mov dword ptr [ebp - 0x14], ecx

# 计算"_entry@0"字符串绝对地址
mov ecx, eax
sub ecx, 0x58
mov dword ptr [ebp - 0x18], ecx

# 获取YourFatherServer.dll模块句柄
mov eax, dword ptr [ebp - 0x14]
push eax
call dword ptr [ebp - 0xC]
mov ecx, eax

# 获取_entry@0入口地址
mov eax, dword ptr [ebp - 0x18]
push eax
push ecx
call dword ptr [ebp - 0x10]

# 判断地址是否获取成功
cmp eax, 0
jne _SUCCESS

# 计算exit入口地址
mov eax, 0x16E0D0
add eax, dword ptr [ebp - 8]
push 0

# 调用entry函数
_SUCCESS:
call eax

# 恢复栈平衡
pop eax
pop ecx
pop esi
pop edi
add esp, 0x18
pop ebp

# 这里不直接返回而是跳转到原本正常的首个call指令的跳转地址,需要根据实际情况调整跳转地址
# ret
jmp 0xXXXXXXXX

shellcode的机器指令十六进制流

直接用这串来在调试的时候拷贝进去

1
596F75724661746865725365727665722E646C6C005F656E7472794030000000558BEC83EC18575651508D7DE8B906000000B8CCCCCCCCF3AB64A1300000008B4808894DFC8B400C8B701CAD8B40088945F8B8E0CC11000345F88945F4B8C07111000345F88945F0E800000000588BC883E96D894DEC8BC883E958894DE88B45EC50FF55F48BC88B45E85051FF55F083F800750AB8D0E016000345F86A00FFD058595E5F83C4185DE97440FFFF

请我喝瓶肥仔快乐水?

欢迎关注我的其它发布渠道