代码示例

MyTest.inc

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
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc
include msvcrt.inc
include psapi.inc

includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib
includelib msvcrt.lib
includelib psapi.lib

DlgProc PROTO :HWND,:UINT,:WPARAM,:LPARAM

.const
MY_MSG db "click",0
MY_WND_NAME db "计算机",0
MY_USER32 db "USER32.dll",0
MY_MSGBOX db "MessageBoxA",0

IDD_DIALOG1 equ 101
BTN_INJECT equ 1001

;#########################################################################

.data?

hInstance dd ?
MyMsg dd ?

;#########################################################################

MyTest.asm

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive

include MyTest.inc
.code
INJECT_CODE_START:
;int 3
push ebp
mov ebp,esp
push 0
push 0
push 0
push 0
label1:
mov eax,12345678h
call eax
;call [ebp+8]
;leave
mov esp,ebp
pop ebp
retn 4
INJECT_CODE_END:

Inject proc
LOCAL @hWnd:HWND
LOCAL @dwPid:DWORD
LOCAL @hProcess:HANDLE
LOCAL @lpBuf:LPVOID
LOCAL @dwBytes:dword
LOCAL @hUser32:dword
LOCAL @old:dword
LOCAL @hMods[1024]:HMODULE
LOCAL @cbNeeded:dword
LOCAL @szModName[260]:byte
LOCAL @hRemoteBase:dword
LOCAL @lpLocalAddr:dword
;push 0
;push 0
;push 0
;push 0
;call MessageBoxA
;call dword ptr [MyMsg] ;==>call xxxxxxxx USER32.DLL call dword ptr ds:[40201c]

invoke FindWindow,NULL,OFFSET MY_WND_NAME
MOV @hWnd,eax
;check

invoke GetWindowThreadProcessId,@hWnd,addr @dwPid
;check

invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,@dwPid
mov @hProcess,eax
;check

;1.对方进程申请内存
invoke VirtualAllocEx,@hProcess,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov @lpBuf,eax
;check

;代码重定位

;修改内存属性
invoke VirtualProtect,offset INJECT_CODE_START,1000h,PAGE_EXECUTE_READWRITE,addr @old
;遍历模块基址
invoke EnumProcessModules,@hProcess,@hMods,sizeof @hMods,addr @cbNeeded
mov ebx,0
mov edx,@cbNeeded
shr edx,2
mov @cbNeeded,edx
.while ebx < @cbNeeded
invoke GetModuleFileNameEx,@hProcess,dword ptr [@hMods+ebx*4],addr @szModName,sizeof @szModName
invoke crt_strstr,addr @szModName,offset MY_USER32
.if eax!=0
mov eax,dword ptr[@hMods+ebx*4]
mov @hRemoteBase,eax
.break
.endif
inc ebx
.endw
;check

invoke LoadLibrary,offset MY_USER32
mov @hUser32,eax
;check

invoke GetProcAddress,@hUser32,offset MY_MSGBOX
mov @lpLocalAddr,eax
;check remoteBase+(MessageBoxA-@hUser32)
sub eax,@hUser32
add eax,@hRemoteBase
mov dword ptr [label1+1],eax

;2.写入INJECT_CODE的代码
mov ebx,offset INJECT_CODE_END
sub ebx,offset INJECT_CODE_START
invoke WriteProcessMemory,@hProcess,@lpBuf,offset INJECT_CODE_START,ebx,addr @dwBytes
;check;

;3.创建远程线程
invoke CreateRemoteThread,@hProcess,NULL,0,@lpBuf,NULL,0,NULL
;check

ret

Inject endp

start:
;invoke crt_strcpy,NULL,NULL
push 0
call INJECT_CODE_START

invoke GetModuleHandle,NULL
mov hInstance,eax

invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;########################################################################
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

mov eax,uMsg
.if eax==WM_INITDIALOG
.elseif eax==WM_COMMAND
mov eax,wParam
.if ax==BTN_INJECT
invoke MessageBox,NULL,offset MY_MSG,NULL,MB_OK
.endif
.elseif eax==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start

代码分析

这是一个Windows汇编程序,用于将特定的代码注入到另一个进程中。

1
2
3
.386
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
  • .386:指定程序使用 386 及以上的处理器指令集。
  • .model flat, stdcall:设定扁平内存模型,并使用 stdcall 调用约定,符合Windows API的标准。
  • option casemap :none:指定对大小写敏感。
1
include MyTest.inc
  • 包含头文件 MyTest.inc,可能包含常量、数据类型和函数原型的定义。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.code
INJECT_CODE_START:
;int 3
push ebp
mov ebp,esp
push 0
push 0
push 0
push 0
label1:
mov eax,12345678h
call eax
;call [ebp+8]
;leave
mov esp,ebp
pop ebp
retn 4
INJECT_CODE_END:
  • 定义了 INJECT_CODE_STARTINJECT_CODE_END 的代码块,这段代码将在被注入到目标进程中执行。
    • push ebp / mov ebp, esp:保存调用者的栈帧,并设置当前栈帧指针 ebp
    • push 0 * 4:将4个 0 压入堆栈,可能是为了填充或准备参数。
    • label1::定义一个标签,作为跳转目的地。
    • mov eax, 12345678h:将地址 12345678h 加载到 eax 中。
    • call eax:调用 eax 指向的函数,这里 eax 指向的地址会在后面动态更新为 MessageBoxA 的地址。
    • mov esp, ebp / pop ebp:恢复栈帧指针。
    • retn 4:返回到调用方并从栈中移除4个字节(传递的参数)。

注入函数

1
2
3
4
5
6
7
8
9
10
11
12
13
Inject proc	
LOCAL @hWnd:HWND
LOCAL @dwPid:DWORD
LOCAL @hProcess:HANDLE
LOCAL @lpBuf:LPVOID
LOCAL @dwBytes:dword
LOCAL @hUser32:dword
LOCAL @old:dword
LOCAL @hMods[1024]:HMODULE
LOCAL @cbNeeded:dword
LOCAL @szModName[260]:byte
LOCAL @hRemoteBase:dword
LOCAL @lpLocalAddr:dword
  • 定义 Inject 过程,声明多个局部变量:
    • @hWnd:存储目标窗口句柄。
    • @dwPid:存储目标进程的ID。
    • @hProcess:存储目标进程句柄。
    • @lpBuf:存储目标进程中分配的内存地址。
    • @dwBytes:存储写入的字节数。
    • @hUser32:存储 user32.dll 的模块句柄。
    • @old:存储旧的内存保护属性。
    • @hMods:存储进程中的模块句柄数组。
    • @cbNeeded:存储模块数组的大小。
    • @szModName:存储模块名称的字符数组。
    • @hRemoteBase:存储目标进程中模块的基址。
    • @lpLocalAddr:存储本地进程中函数地址。
1
2
3
invoke FindWindow,NULL,OFFSET MY_WND_NAME	
MOV @hWnd,eax
;check
  • invoke FindWindow, NULL, OFFSET MY_WND_NAME:查找窗口名称为 MY_WND_NAME 的窗口句柄,并将其存储到 @hWnd
  • MOV @hWnd, eax:将返回的窗口句柄存入局部变量 @hWnd
1
2
invoke GetWindowThreadProcessId,@hWnd,addr @dwPid	
;check
  • invoke GetWindowThreadProcessId, @hWnd, addr @dwPid:获取窗口句柄 @hWnd 所属的进程ID,并将其存储到 @dwPid
1
2
3
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,@dwPid	
mov @hProcess,eax
;check
  • invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, @dwPid:打开目标进程,获取句柄,存储到 @hProcess
1
2
3
4
;1.对方进程申请内存	
invoke VirtualAllocEx,@hProcess,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov @lpBuf,eax
;check
  • invoke VirtualAllocEx, @hProcess, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE:在目标进程中分配 0x1000 大小的内存区域,具有读写执行权限,返回的地址存储到 @lpBuf
1
2
3
;代码重定位	
;修改内存属性
invoke VirtualProtect, offset INJECT_CODE_START, 1000h, PAGE_EXECUTE_READWRITE, addr @old
  • invoke VirtualProtect, offset INJECT_CODE_START, 1000h, PAGE_EXECUTE_READWRITE, addr @old:修改当前进程中注入代码的内存区域权限为可读、可写、可执行。
1
2
;遍历模块基址	
invoke EnumProcessModules,@hProcess,@hMods,sizeof @hMods,addr @cbNeeded
  • invoke EnumProcessModules, @hProcess, @hMods, sizeof @hMods, addr @cbNeeded:枚举目标进程中加载的模块,结果存储在 @hMods 数组中。
1
2
3
4
mov ebx, 0	
mov edx, @cbNeeded
shr edx, 2
mov @cbNeeded, edx
  • 计算模块数量:@cbNeeded 存储的是字节数,将其右移两位转换为模块数(每个模块句柄占4字节)。
1
2
3
4
5
6
7
8
9
10
11
.while ebx < @cbNeeded		
invoke GetModuleFileNameEx, @hProcess, dword ptr [@hMods+ebx*4], addr @szModName, sizeof @szModName
invoke crt_strstr, addr @szModName, offset MY_USER32
.if eax != 0
mov eax, dword ptr[@hMods + ebx * 4]
mov @hRemoteBase, eax
.break
.endif
inc ebx
.endw
;check
  • 遍历目标进程的模块列表,通过 GetModuleFileNameEx 获取每个模块的名称。
  • 使用 crt_strstr 检查是否包含 MY_USER32(即 user32.dll)。
  • 找到后记录模块基址 @hRemoteBase,并退出循环。
1
2
3
invoke LoadLibrary,offset MY_USER32	
mov @hUser32,eax
;check
  • invoke LoadLibrary, offset MY_USER32:在本地进程中加载 user32.dll,句柄存入 @hUser32
1
2
3
invoke GetProcAddress, @hUser32, offset MY_MSGBOX	
mov @lpLocalAddr, eax
;check remoteBase+(MessageBoxA-@hUser32)
  • invoke GetProcAddress, @hUser32, offset MY_MSGBOX:获取 user32.dllMessageBoxA 函数的地址,并存入 @lpLocalAddr
1
2
3
sub eax, @hUser32	
add eax, @hRemoteBase
mov dword ptr [label1 + 1], eax
  • 计算目标进程中 MessageBoxA 函数的实际地址:本地 MessageBoxA 地址减去 user32.dll 的本地基址,加上目标进程的 user32.dll 基址。将该地址写入注入代码的 label1 + 1 位置,使 call eax 执行 MessageBoxA
1
2
3
4
5
;2.写入INJECT_CODE的代码	
mov ebx, offset INJECT_CODE_END
sub ebx, offset INJECT_CODE_START
invoke WriteProcessMemory, @hProcess, @lpBuf, offset INJECT_CODE_START, ebx, addr @dwBytes
;check;
  • 计算注入代码段的大小,将其写入目标进程的分配内存中。
1
2
3
4
5
	;3.创建远程线程	
invoke CreateRemoteThread, @hProcess

, NULL, 0, @lpBuf, NULL, 0, NULL
;check
  • 在目标进程中创建一个线程,线程起始地址为 @lpBuf(即注入代码的地址)。
1
2
	ret
Inject endp
  • 结束 Inject 过程。

程序入口

1
2
3
4
start:	
;invoke crt_strcpy,NULL,NULL
push 0
call INJECT_CODE_START
  • 执行注入代码 INJECT_CODE_START,调用 12345678h 地址函数(无实际意义,因为此时未注入目标进程)。
1
2
invoke GetModuleHandle,NULL	
mov hInstance, eax
  • 获取当前模块句柄,存入 hInstance
1
invoke InitCommonControls	
  • 初始化公共控件。
1
invoke DialogBoxParam, hInstance, IDD_DIALOG1, NULL, addr DlgProc, NULL	
  • 显示对话框 IDD_DIALOG1,处理函数为 DlgProc
1
invoke ExitProcess, 0
  • 退出程序。

对话框处理函数

1
DlgProc proc hWin:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  • 定义 DlgProc 过程,处理窗口消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	mov eax, uMsg	
.if eax == WM_INITDIALOG
.elseif eax == WM_COMMAND
mov eax, wParam
.if ax == BTN_INJECT
invoke MessageBox, NULL, offset MY_MSG, NULL, MB_OK
.endif
.elseif eax == WM_CLOSE
invoke EndDialog, hWin, 0
.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
DlgProc endp
  • 根据不同的消息执行不同的操作:
    • WM_INITDIALOG:初始化对话框。
    • WM_COMMAND:响应按钮点击,显示消息框。
    • WM_CLOSE:关闭对话框。
    • 返回 TRUE 表示消息已处理,FALSE 表示未处理。
1
end start
  • 程序从 start 开始执行。