效果图

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
format PE GUI 4.0
;声明文件类型
entry start
;程序入口

include 'win32ax.inc'
;文件包头

struct GdiplusStartupInput
GdiplusVersion dd ?
DebugEventCallback dd ?
SuppressBackgroundThread dd ?
SuppressExternalCodecs dd ?
ends

DebugEventCallback 指向回调函数的指针,在调试中为声明和警告调用该回调函数(默认True)

SuppressBackgroundThread 若为True则结构体将返回指向hook函数的指针和指向unhook挂钩函数的指针(默认False)

SuppressExternalCodecs 是否抑制外部图像编解码器

GdiplusVersion dd ? 第三个位置用于表示默认值,如果该位置为 ?则说明这是一个没有初始化的数据

.idata部分

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
section '.idata' import data readable writeable

library gdi, 'GDI.DLL',\
gdiplus, 'GDIPLUS.DLL',\
kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL'

include 'api\gdi32.inc'
include 'api\kernel32.inc'
include 'api\user32.inc'

import gdiplus,\
GdiplusShutdown,'GdiplusShutdown',\
GdipCreateFromHDC, 'GdipCreateFromHDC',\
GdipCreatePen1, 'GdipCreatePen1',\
GdipDeleteGraphics, 'GdipDeleteGraphics',\
GdipDeletePen, 'GdipDeletePen',\
GdipGetPenBrushFill, 'GdipGetPenBrushFill',\
GdipFillEllipseI, 'GdipFillEllipseI',\
GdipDrawLineI, 'GdipDrawLineI',\
GdipSetSmoothingMode, 'GdipSetSmoothingMode',\
GdiplusStartup, 'GdiplusStartup',\
GdipDrawEllipseI, 'GdipDrawEllipseI',\
GdipDrawArcI, 'GdipDrawArcI',\
GdipDrawRectangleI, 'GdipDrawRectangleI',\
GdipFillEllipse, 'GdipFillEllipse'

.data部分

1
2
3
4
5
6
7
8
9
10
11
12
section '.data' data readable writeable

_class TCHAR 'FASMWIN32',0
_title TCHAR 'Example',0
_error TCHAR 'Startup failed.',0

wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class
;注册一个windows类

msg MSG
input GdiplusStartupInput 1 ; GdiplusVersion = 1
token dd ?

该部分用于存放全局变量/常量等,用ida查看字符串可以看到上面这三个全局变量

WNDCLASS是一个系统支持的结构,用于储存窗口信息

.code部分

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
section '.code' code readable executable

start:

invoke GetModuleHandle,0
mov [wc.hInstance],eax
invoke LoadIcon,0,IDI_APPLICATION
mov [wc.hIcon],eax
invoke LoadCursor,0,IDC_IBEAM;IDC_ARROW
mov [wc.hCursor],eax
invoke RegisterClass,wc
test eax,eax
jz error

invoke GdiplusStartup, token, input, NULL
test eax, eax
jnz error

invoke CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_CAPTION+WS_SYSMENU+WS_THICKFRAME,128,100,740,576,NULL,NULL,[wc.hInstance],NULL
test eax,eax
jz error

msg_loop:
invoke GetMessage,msg,NULL,0,0
cmp eax,1
jb end_loop
jne msg_loop
invoke TranslateMessage,msg
invoke DispatchMessage,msg
jmp msg_loop

error:
invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK

end_loop:
invoke GdiplusShutdown, [token]
invoke ExitProcess,[msg.wParam]

GetModuleHandle 用于获取一个应用程序或动态链接库的模块句柄,前提是目的模块已映射到调用该函数的进程内

LoadCursor 从一个与相关的可执行文件中载入指定的光标资源

test用于将两个操作数进行逻辑与运算,并根据结果设置标志位(不影响进行运算的两个数本身

窗口进程相关

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
proc WindowProc hwnd,umsg,wparam,lparam
local ps:PAINTSTRUCT

push ebx esi edi

cmp [wmsg],WM_DESTROY
je .wmdestroy

cmp [wmsg], WM_PAINT
je .wmpaint

.defwndproc:
invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
jmp .finish

.wmpaint:
invoke BeginPaint, [hwnd], addr ps

stdcall draw, eax, $FF000000
invoke EndPaint, [hwnd], addr ps
jmp .finish_ret_0

.wmdestroy:
invoke PostQuitMessage,0

.finish_ret_0:
xor eax,eax

.finish:
pop edi esi ebx
ret
endp

WindowProc 是一个用于处理发送给窗口消息的函数

其中的参数 hwnd 指向窗口的句柄,uMsg 为消息类型,wparamlparam 为其它的消息

画画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
proc draw hdc, color
local pGraphics:DWORD, pPen:DWORD, pBrush:DWORD

invoke GdipCreateFromHDC, [hdc], addr pGraphics
invoke GdipCreatePen1, [color], 3.0, 2, addr pPen
invoke GdipGetPenBrushFill, [pPen], addr pBrush
;创建场景句柄/画笔兑现/刷子对象

invoke GdipSetSmoothingMode, [pGraphics], 2

push ebx
mov ebx, 1
invoke GdipDrawRectangleI, [pGraphics], [pPen], addr 250+ebx*8, addr 100+ebx*8, addr 200+ebx*8, addr 200+ebx*8
invoke GdipFillEllipseI, [pGraphics], [pBrush], addr 300+ebx*8, addr 140+ebx*8, addr 10+ebx*8, addr 25+ebx*8
;以下省略n条画画步骤
;[pGraphics]参数是指向对象的指针,[pPen]参数是指向画线的“笔”的指针,后面参数用于画图对象的具体长宽高等
pop ebx
ret
endp

invoke 用于使用第一个参数间接调用进程(fastcall用于直接调用)

lea Load effective address 加载有效地址,将有效地址传送到指定的寄存器

参考资料:
FASM官方文档