Win32API
字符编码 ASCII 码表
ASCII值
控制字符
ASCII值
控制字符
ASCII值
控制字符
ASCII值
控制字符
0
NUL
32
(space)
64
@
96
、
1
SOH
33
!
65
A
97
a
2
STX
34
”
66
B
98
b
3
ETX
35
#
67
C
99
c
4
EOT
36
$
68
D
100
d
5
ENQ
37
%
69
E
101
e
6
ACK
38
&
70
F
102
f
7
BEL
39
‘
71
G
103
g
8
BS
40
(
72
H
104
h
9
HT
41
)
73
I
105
i
10
LF
42
*
74
J
106
j
11
VT
43
+
75
K
107
k
12
FF
44
,
76
L
108
l
13
CR
45
-
77
M
109
m
14
SO
46
.
78
N
110
n
15
SI
47
/
79
O
111
o
16
DLE
48
0
80
P
112
p
17
DCI
49
1
81
Q
113
q
18
DC2
50
2
82
R
114
r
19
DC3
51
3
83
X
115
s
20
DC4
52
4
84
T
116
t
21
NAK
53
5
85
U
117
u
22
SYN
54
6
86
V
118
v
23
TB
55
7
87
W
119
w
24
CAN
56
8
88
X
120
x
25
EM
57
9
89
Y
121
y
26
SUB
58
:
90
Z
122
z
27
ESC
59
;
91
[
123
{
28
FS
60
<
92
\
124
|
29
GS
61
=
93
]
125
}
30
RS
62
>
94
^
126
~
31
US
63
?
95
—
127
DEL
ASCII扩展码表
十进制
十六进制
字符
十进制
十六进制
字符
128
80
Ç
192
C0
└
129
81
ü
193
C1
┴
130
82
é
194
C2
┬
131
83
â
195
C3
├
132
84
ä
196
C4
─
133
85
à
197
C5
┼
134
86
å
198
C6
╞
135
87
ç
199
C7
╟
136
88
ê
200
C8
╚
137
89
ë
201
C9
╔
138
8A
è
202
CA
╩
139
8B
ï
203
CB
╦
140
8C
î
204
CC
╠
141
8D
ì
205
CD
═
142
8E
Ä
206
CE
╬
143
8F
Å
207
CF
╧
144
90
É
208
D0
╨
145
91
æ
209
D1
╤
146
92
Æ
210
D2
╥
147
93
ô
211
D3
╙
148
94
ö
212
D4
Ô
149
95
ò
213
D5
╒
150
96
û
214
D6
╓
151
97
ù
215
D7
╫
152
98
ÿ
216
D8
╪
153
99
Ö
217
D9
┘
154
9A
Ü
218
DA
┌
155
9B
¢
219
DB
█
156
9C
£
220
DC
▄
157
9D
¥
221
DD
▌
158
9E
?
222
DE
?
159
9F
ƒ
223
DF
?
160
A0
á
224
E0
α
161
A1
í
225
E1
ß
162
A2
ó
226
E2
Γ
163
A3
ú
227
E3
π
164
A4
ñ
228
E4
Σ
165
A5
Ñ
229
E5
σ
166
A6
ª
230
E6
µ
167
A7
º
231
E7
τ
168
A8
¿
232
E8
Φ
169
A9
?
233
E9
Θ
170
AA
¬
234
EA
Ω
171
AB
½
235
EB
δ
172
AC
¼
236
EC
∞
173
AD
¡
237
ED
φ
174
AE
«
238
EE
ε
175
AF
»
239
EF
∩
176
B0
?
240
F0
≡
177
B1
?
241
F1
±
178
B2
▓
242
F2
≥
179
B3
│
243
F3
≤
180
B4
┤
244
F4
?
181
B5
╡
245
F5
?
182
B6
╢
246
F6
÷
183
B7
╖
247
F7
≈
184
B8
╕
248
F8
≈
185
B9
╣
249
F9
?
186
BA
║
250
FA
·
187
BB
╗
251
FB
√
188
BC
╝
252
FC
?
189
BD
╜
253
FD
²
190
BE
╛
FE
■
191
BF
┐
255
FF
ÿ
Unicode字符集 UNICODE编码方案,世界上所有的文字符号都能从这张表中找到。Unicode编码范围是0~0x10FFFF,能容纳1114111个字符。
但是Unicode只是一个字符集,它规定了每个字符对应的二进制,但是没有规定如何存储。
Unicode的存储方式有UTF-8和UTF-16
UTF-8 UTF-8是变长字符编码。
Unicode编码(HEX)
UTF-8字节流(BIN)
000000 - 00007F
0xxxxxxx
000080 - 0007FF
110xxxxx 10xxxxxx
000800 - 00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-16 UTF-16编码以16位无符号整数为单位,(是以16位为一个单位,不表示一个字符就有16位)。这要看字符的Unicode编码处于什么范围而定,有可能是2个字节,有可能是4个字节。现在机器上的Unicode编码一般指的是UTF-16编码
BOM BOM,BYTE Order Mark,字符排列标志。
编码方式
BOM
UTF-8
EF BB BF
UTF-16LE(小端对齐
FF FE
UTF-16BE(大端对齐
FE FF
C语言中的宽字符 宽字符
打印宽字符:
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> #include <locale.h> int main (void ) { setlocale(LC_ALL, "" ); wchar_t wch[] = L"宽字符" ; wprintf(L"%s\n" , wch); return 0 ; }
相关函数
char
wchar_t
多字节字符型 / 宽字节字符型
printf
wprintf
打印到控制台
strlen
wcslen
获取长度
strcpy
wcscpy
字符串复制
strcat
wcscat
字符串拼接
strcmp
wcscmp
字符串比较
strstr
wcsstr
字符串查找
Win32 API中的宽字符 Win32 API API,Application Process Interface应用程序接口,也就是Windows提供的封装好的一些函数
几个重要DLL:
<1>Kernel32.dll最核心的功能模块,比如管理内存、进程和线程相关的函数等。 <2>User32.dll是Vindows用户界面相关应用程序接口,如创建窗口和发送消息等。 <3>GDl32.dll全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数。
在Win32中写代码最好用TCHAR来写,利于跨平台使用
1 2 3 4 CHAR ch[] = "" ; WCHAR wch[] = L"" ; TCHAR tch[] = TEXT("" );
1 2 3 4 5 指针类型 PSTR pszStr = "" ; PWSTR pwszStr = L"" ; PTSTR ptszStr = TEXT("" );
MessageBox API 1 2 3 4 5 6 7 8 9 10 11 12 13 CHAR chTitle[] = "标题" ; CHAR chText[] = "内容" ; MessageBoxA(0 ,chText,chTitle,MB_YESNO); WCHAR wchTitle[] = L"标题" ; WCHAR wchText[] = L"内容" ; MessageBoxW(0 ,wchText,wchTitle,MB_YESNO); TCHAR chTitle[] = TEXT("标题" ); TCHAR chText[] = TEXT("内容" ); MessageBox(0 ,chText,chTitle,MB_YESNO);
进程 进程的创建 任何进程都是别的进程创建的:由CreateProcess()这个函数创建
进程创建过程
1 2 3 4 5 6 7 1、映射EXE文件 2、创建内核对象EPROCESS 3、映射系统DLL(ntdll.dll) 4、创建线程内核对象ETHREAD 5、系统启动线程 映射DLL(ntdl.LdrInitializeThunk) 线程开始执行
创建进程 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 #include <stdio.h> #include <windows.h> BOOL CreateChildProcess (PTCHAR szChildProcessName, PTCHAR szCommandLine) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof (pi)); ZeroMemory(&si, sizeof (si)); si.cb = sizeof (si); if (!CreateProcess( szChildProcessName, szCommandLine, NULL , NULL , FALSE, 0 , NULL , NULL , &si, &pi) ) { printf ("CreatChildProcess Error:%d \n" , GetLastError); return FALSE; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return TRUE; } int main (void ) { TCHAR szApplicationName[] = TEXT("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" ); TCHAR szCmdline[] = TEXT("https://baidu.com" ); CreateChildProcess(szApplicationName, NULL ); return 0 ; }
句柄表 句柄表存储的就是一种映射关系,每个进程的内核对象对应一个句柄,用户不能直接访问内核对象(如果用户给了一个错误的内核地址会蓝屏)。为了防止访问错误的内核对象地址,Windows在用户层和内核层中间加了一个句柄表,用户通过访问句柄表来访问内核对象,句柄表相当于一道内核层外的防火墙。
句柄表是一张私有的表,只针对当前的进程才有意义。
如果是CloseHandle的话,内核对象不会死,而是内核对象的计数器减一(当有多个对象都运行了内核对象A)。如果所有的进程都把内核对象杀掉,也就是内核对象的计数变成0的时候,这个内核对象就会被销毁。
进程里有线程,线程不死,进程就不会死;如果进程里的唯一线程死了,进程就死了。
进程相关API ID与句柄表 进程ID——PID,使用程序将进程ID打印出来为16进制,比如0x2914,转换成十进制是10516,也就是这里的资源管理器
句柄表
句柄表是一个程序私有的,如果通过另一个程序关掉另一个程序,不能使用被关闭进程的句柄表,因为这个句柄表是私有的,程序A无法通过程序B的句柄表关闭程序B。
比如,程序A的句柄是0xf0,
此时如果用程序B来关闭程序A:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <windows.h> int main (void ) { HANDLE hProcess; hProcess = (HANDLE)0xf0 ; if (!TerminateProcess(hProcess, 1 )) { printf ("终止程序失败 %d \n" , GetLastError()); } }
输出:
返回这个值说明句柄无效,因为0xf0是A的私有句柄
但是,进程ID(dwProcessID)是公有的,所以还是可以通过使用PID来终止程序。
以挂起形式创建进程 1 2 3 4 5 6 7 8 9 10 11 12 BOOL CreateProcess ( LPCTSTR lpApplicationName. LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, **DWORD dwCreationFlags,** LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFOMATION lpProcessInformation )
在dwCreationFlags中,如果值为0,那么父进程和子进程是共用一个控制台的,如果要父进程和子进程分别打开一个控制台,那么需要将dwCreationFlags设置为CREATE_NEW_CONSOLE
。**其中有一个值为CREATE_SUSPENDED:
**以挂起的形式创建一个进程
模块目录与工作目录 两个API:
1 2 GetModuleFileName GetCurrentDirectory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <windows.h> #pragma warning (disable:6031) int main (void ) { char strModule[256 ]; GetModuleFileName(NULL , strModule, 256 ); char strWork[1000 ]; int i = 1000 ; GetCurrentDirectory(1000 , strWork); printf ("模块路径:%s\n工作路径:%s\n" , strModule, strWork); getchar(); return 0 ; }
输出
1 2 模块路径:C:\Users\23394\Desktop\code\C C++\win32AP\Debug\win32AP.exe 工作路径:C:\Users\23394\Desktop\code\C C++\win32AP
模块路径就是exe所在位置,工作路径由创建这个模块的父进程填写
线程 线程 线程是附属在进程上的执行实体,是代码的执行流程
一个进程可以包含多个线程,但一个进程至少包含一个线程
创建线程 CreateThread
1 2 3 4 5 6 7 8 HANDLE CreateThread ( [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] SIZE_T dwStackSize, [in] LPTHREAD_START_ROUTINE lpStartAddress, [in, optional] __drv_aliasesMem LPVOID lpParameter, [in] DWORD dwCreationFlags, [out, optional] LPDWORD lpThreadId ) ;
创建一个线程:
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 #include <stdio.h> #include <windows.h> DWORD WINAPI ThreadProc (LPVOID lpParameter) { for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("++++++++++%d\n" , i); } return 0 ; } int main (void ) { HANDLE hThread; hThread = CreateThread(NULL ,0 , ThreadProc,NULL ,0 ,NULL ); CloseHandle(hThread); for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("----------%d\n" , i); } return 0 ; }
其中DWORD WINAPI ThreadProc(LPVOID lpParameter)
这个函数可以没有返回值和参数,但是在使用时需要强制转换成(LPTHREAD_START_ROUTINE)
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 #include <stdio.h> #include <windows.h> void WINAPI ThreadProc () { for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("++++++++++%d\n" , i); } return 0 ; } int main (void ) { HANDLE hThread; hThread = CreateThread(NULL ,0 , (LPTHREAD_START_ROUTINE)ThreadProc,NULL ,0 ,NULL ); CloseHandle(hThread); for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("----------%d\n" , i); } return 0 ; }
线程也可以传入参数,参数可以是任何类型,只要自己强制转换一个类型即可
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 #include <stdio.h> #include <windows.h> DWORD WINAPI ThreadProc (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(500 ); printf ("++++++++++%d\n" , i); } return 0 ; } int main (void ) { int n; n = 10 ; int * np = &n; HANDLE hThread; hThread = CreateThread(NULL ,0 , ThreadProc,(LPTHREAD_START_ROUTINE)np, 0 , NULL ); CloseHandle(hThread); for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("----------%d\n" , i); } return 0 ; }
以上函数通过闯传入一个int类型指针,先强转成LPTHREAD_START_ROUTINE类型,传入一个指针,再在线程中将这个指针转换成自己需要的类型即可。
线程控制 让自己停下来Sleep()
,停止当前线程。 让别的线程停下来SuspendThread(hThread)
线程恢复ResumeThread
线程挂起几次就要继续几次,如果挂起两次,就要继续两次才能继续执行。
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 #include <stdio.h> #include <windows.h> struct handles { HANDLE h1; HANDLE h2; }; DWORD WINAPI ThreadProc1 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(500 ); printf ("----------%d\n" , i+1 ); } return 0 ; } DWORD WINAPI ThreadProc2 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(500 ); printf ("++++++++++%d\n" , i+1 ); } return 0 ; } DWORD WINAPI ThreadProc3 (LPVOID lpParameter) { struct handles * hp = (handles*)lpParameter; SuspendThread(hp->h1); Sleep(5000 ); ResumeThread(hp->h1); SuspendThread(hp->h2); Sleep(5000 ); ResumeThread(hp->h2); return 0 ; } int main (void ) { int n; n = 10 ; int * np = &n; HANDLE hThread1; hThread1 = CreateThread(NULL ,0 , ThreadProc1,(LPTHREAD_START_ROUTINE)np, 0 , NULL ); int l; l = 30 ; int * lp = &l; HANDLE hThread2; hThread2 = CreateThread(NULL , 0 , ThreadProc2, (LPTHREAD_START_ROUTINE)lp, 0 , NULL ); struct handles Handles ; Handles.h1 = hThread1; Handles.h2 = hThread2; struct handles * ph = &Handles; HANDLE hThread3; hThread3 = CreateThread(NULL , 0 , ThreadProc3, (LPTHREAD_START_ROUTINE)ph, 0 , NULL ); Sleep(200000000 ); CloseHandle(hThread1); CloseHandle(hThread2); return 0 ; }
以上是一个线程交替的实例。
输出:
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 ++++++++++1 ++++++++++2 ++++++++++3 ++++++++++4 ++++++++++5 ++++++++++6 ++++++++++7 ++++++++++8 ++++++++++9 ----------1 ----------2 ----------3 ----------4 ----------5 ----------6 ----------7 ----------8 ----------9 ++++++++++10 ----------10 ++++++++++11 ++++++++++12 ++++++++++13 ++++++++++14 ++++++++++15 ++++++++++16 ++++++++++17 ++++++++++18 ++++++++++19 ++++++++++20 ++++++++++21 ++++++++++22 ++++++++++23 ++++++++++24 ++++++++++25 ++++++++++26 ++++++++++27 ++++++++++28 ++++++++++29 ++++++++++30
等待线程中的API 1 2 3 <1 >WaitForSingleObject(); <2 >WaitForMultiplePbjects(); <3 >GetExitCodeThread();
WaitForSingleObject等待单个线程 1 2 3 4 DWORD WaitForSingleObject ( HANDLE hHandle, DWORD dwMilliseconds )
执行这个函数的时候,这个函数所在的当前线程会阻塞,等待WaitForSingleObject中传入的线程执行完毕后,WaitForSingleObject后面的程序才会执行。
1 2 3 4 5 6 DWORD WaitForMultipleObjects ( DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds ) ;
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 #include <stdio.h> #include <windows.h> #pragma warning (disable:6387) DWORD WINAPI ThreadProc1 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("----------%d\n" , i+1 ); } return 0 ; } DWORD WINAPI ThreadProc2 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("++++++++++%d\n" , i+1 ); } return 0 ; } int main (void ) { int n; n = 100 ; int * np = &n; HANDLE harrThreadArray[2 ]; harrThreadArray[0 ] = CreateThread(NULL , 0 , ThreadProc1, (LPTHREAD_START_ROUTINE)np, 0 , NULL ); int l; l = 50 ; int * lp = &l; harrThreadArray[1 ] = CreateThread(NULL , 0 , ThreadProc2, (LPTHREAD_START_ROUTINE)lp, 0 , NULL ); WaitForMultipleObjects(2 , harrThreadArray, TRUE, INFINITE); printf ("线程执行完毕\n" ); printf ("其他程序..." ); CloseHandle(harrThreadArray[0 ]); CloseHandle(harrThreadArray[1 ]); return 0 ; }
GetExitCodeThread读取线程返回值 1 2 3 4 BOOL GetExitCodeThread ( HANDLE hThread, LPDWORD lpExitCode ) ;
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 #include <stdio.h> #include <windows.h> #pragma warning (disable:6387) DWORD WINAPI ThreadProc1 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("----------%d\n" , i+1 ); } return 1 ; } DWORD WINAPI ThreadProc2 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("++++++++++%d\n" , i+1 ); } return 2 ; } int main (void ) { int n; n = 100 ; int * np = &n; HANDLE harrThreadArray[2 ]; harrThreadArray[0 ] = CreateThread(NULL , 0 , ThreadProc1, (LPTHREAD_START_ROUTINE)np, 0 , NULL ); int l; l = 50 ; int * lp = &l; harrThreadArray[1 ] = CreateThread(NULL , 0 , ThreadProc2, (LPTHREAD_START_ROUTINE)lp, 0 , NULL ); DWORD dwResultArray[2 ]; WaitForMultipleObjects(2 , harrThreadArray, TRUE, INFINITE); GetExitCodeThread(harrThreadArray[0 ], &dwResultArray[0 ]); GetExitCodeThread(harrThreadArray[1 ], &dwResultArray[1 ]); printf ("线程执行完毕\n" ); printf ("线程1执行完毕返回:%d;线程2执行完毕返回:%d\n" , dwResultArray[0 ], dwResultArray[1 ]); printf ("其他程序..." ); CloseHandle(harrThreadArray[0 ]); CloseHandle(harrThreadArray[1 ]); return 0 ; }
CONTEXT线程上下文 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 typedef struct DECLSPEC_NOINITALL _CONTEXT { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT;
这个结构体中存储了所有的寄存器,这也就是为什么当单核CPU能够循环执行多个线程,因为被挂起的线程会在挂起时将所有寄存器的数据都存在这个结构体中。
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 #include <stdio.h> #include <windows.h> #pragma warning (disable:6387) DWORD WINAPI ThreadProc1 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("----------%d\n" , i+1 ); } return 1 ; } DWORD WINAPI ThreadProc2 (LPVOID lpParameter) { int * p = (int *)lpParameter; for (int i = 0 ; i < *p; i++) { Sleep(25 ); printf ("++++++++++%d\n" , i+1 ); } return 2 ; } int main (void ) { int n; n = 100 ; int * np = &n; HANDLE harrThreadArray[2 ]; harrThreadArray[0 ] = CreateThread(NULL , 0 , ThreadProc1, (LPTHREAD_START_ROUTINE)np, 0 , NULL ); int l; l = 50 ; int * lp = &l; harrThreadArray[1 ] = CreateThread(NULL , 0 , ThreadProc2, (LPTHREAD_START_ROUTINE)lp, 0 , NULL ); Sleep(100 ); SuspendThread(harrThreadArray[0 ]); CONTEXT context; context.ContextFlags = CONTEXT_INTEGER; GetThreadContext(harrThreadArray[0 ], &context); printf ("%x %x\n" , context.Eax, context.Ecx); ResumeThread(harrThreadArray[0 ]); DWORD dwResultArray[2 ]; WaitForMultipleObjects(2 , harrThreadArray, TRUE, INFINITE); GetExitCodeThread(harrThreadArray[0 ], &dwResultArray[0 ]); GetExitCodeThread(harrThreadArray[1 ], &dwResultArray[1 ]); printf ("线程执行完毕\n" ); printf ("线程1执行完毕返回:%d;线程2执行完毕返回:%d\n" , dwResultArray[0 ], dwResultArray[1 ]); printf ("其他程序..." ); CloseHandle(harrThreadArray[0 ]); CloseHandle(harrThreadArray[1 ]); return 0 ; }
SetThreadContext 在挂起线程时,还可以使用SetThreadContext改变寄存器中的数值。
线程安全 临界资源 当两个线程同时要访问一个全局变量的时候,可能出现同时访问的情况,导致线程安全受影响。
临界区 一段使用临界资源的代码称为临界区。
线程锁 Windows实现线程锁的方法:
调用API 临界区之实现线程锁:
<1>创建全局变量
<2>初始化全局变量
1 InitializeCriticalSection(&cs);
<3>实现临界区
1 2 3 EnterCriticalSection(&cs); LeaveCriticalSection(&cs);
比如这样一段买票的代码:
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 #include <stdio.h> #include <windows.h> int ticket = 20 ;DWORD WINAPI ThreadProcess (LPVOID lpParameter) { while (ticket > 0 ) { printf ("还有%d张票 " , ticket); ticket--; printf ("卖出一张,还剩%d张\n" , ticket); } return 0 ; } int main (void ) { HANDLE hThreadArr[2 ]; hThreadArr[0 ] = CreateThread(NULL , 0 , ThreadProcess, NULL , 0 , NULL ); hThreadArr[1 ] = CreateThread(NULL , 0 , ThreadProcess, NULL , 0 , NULL ); WaitForMultipleObjects(2 , hThreadArr, TRUE, INFINITE); CloseHandle(hThreadArr[0 ]); CloseHandle(hThreadArr[1 ]); return 0 ; }
有些时候会出现这么个情况
说明两个线程A在阻塞时线程B停在了它不该停的地方
更改代码如下:
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 #include <stdio.h> #include <windows.h> CRITICAL_SECTION cs; int ticket = 20 ;DWORD WINAPI ThreadProcess (LPVOID lpParameter) { int * pt = (int *)lpParameter; EnterCriticalSection(&cs); while (ticket > 0 ) { printf ("Thread:%d 还有%d张票 \n" , *pt, ticket); ticket--; printf (" 卖出一张,还剩%d张\n" , ticket); printf ("--------------------------\n" ); } LeaveCriticalSection(&cs); return 0 ; } int main (void ) { InitializeCriticalSection(&cs); HANDLE hThreadArr[2 ]; int ThreadC[2 ]; ThreadC[0 ] = 1 ; ThreadC[1 ] = 2 ; int * pThread[2 ]; pThread[0 ] = &ThreadC[0 ]; pThread[1 ] = &ThreadC[1 ]; hThreadArr[0 ] = CreateThread(NULL , 0 , ThreadProcess, (LPTHREAD_START_ROUTINE)pThread[0 ], 0 , NULL ); hThreadArr[1 ] = CreateThread(NULL , 0 , ThreadProcess, (LPTHREAD_START_ROUTINE)pThread[1 ], 0 , NULL ); WaitForMultipleObjects(2 , hThreadArr, TRUE, INFINITE); CloseHandle(hThreadArr[0 ]); CloseHandle(hThreadArr[1 ]); return 0 ; }
当线程1执行时进入临界区,那么线程2就无法访问临界资源,直到线程1离开了临界区(归还令牌)后,线程2才能够访问临界资源,但是当线程2再拿着ticket=0进来的时候,已经不满足条件,就直接跳过了。
互斥体 互斥体 内核级临界资源怎么办?
假设A进程的B线程和C进程的D线程,同时使用的是内核级的临界资源(内核对象:线程、文件、进程…)该怎么让这个访问是安全的?使用线程锁的方式明显不行,因为线程锁仅能控制同进程中的多线程。
那么这时候我们就需要一个能够放在内核中的令牌 来控制,而实现这个作用的,我们称之为互斥体 。
创建互斥体 1 2 3 4 5 HANDLE CreateMutex ( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName ) ;
由线程A创建了一个互斥体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <windows.h> int main (void ) { HANDLE cm = CreateMutex(NULL ,TRUE,"nMutex" ); WaitForSingleObject(cm, INFINITE); for (int i = 0 ; i < 13 ; i++) { printf ("Process:A Thread:X ->%d++++++++++\n" , i + 1 ); Sleep(1000 ); } ReleaseMutex(cm); return 0 ; }
在B进程中使用了这个互斥体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> #include <windows.h> int main (void ) { HANDLE tMutex = CreateMutex(NULL , TRUE, L"nMutex" ); WaitForSingleObject(tMutex, INFINITE); for (int i = 0 ; i < 12 ; i++) { printf ("Process:B Thread:Y ->%d----------\n" , i + 1 ); Sleep(1000 ); } return 0 ; }
这两个不同进程的不同线程通过nMutex
这个互斥体名字进行访问。
当进程A启动后再启动进程B:
发现进程A先执行,进程B被阻塞了。
互斥体实现禁止多开 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 #include <windows.h> int main (int argc, char * argv[]) { HANDLE cm = CreateMutex(NULL , TRUE, "XYZ" ); if (cm != NULL ) { if (GetLastError() == ERROR_ALREADY_EXISTS) { printf ("该程序已经开启了,请勿再次开启!" ); getchar(); } else { WaitForSingleObject(cm, INFINITE); for (int i = 0 ; i < 5 ; i++) { printf ("Process: A Thread: B -- %d \n" , i); Sleep(1000 ); } ReleaseMutex(cm); } } else { printf ("CreateMutex 创建失败! 错误代码: %d\n" , GetLastError()); } return 0 ; }
当已经运行一个程序时,再打开第二个会显示程序已开启。
事件 通知类型 1 2 3 4 5 6 HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) ;
生产者与消费者 要求:生产者生产一个产品,消费者消耗一个产品。如果不使用通知实现,使用互斥体的话:
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 #include <stdio.h> #include <windows.h> HANDLE hEven; HANDLE hMutex; int produce;DWORD WINAPI ProduceThread (LPVOID lpParameter) { int * pProduceCount = (int *)lpParameter; for (int i = 0 ; i < *pProduceCount; i++) { WaitForSingleObject(hMutex, INFINITE); produce = 1 ; long ProduceThreadID = GetCurrentThreadId(); printf ("生产者%ul生产了一个产品\n" , ProduceThreadID); ReleaseMutex(hMutex); } return 0 ; } DWORD WINAPI ConsumptionThread (LPVOID lpParameter) { int * pProduceCount = (int *)lpParameter; for (int i = 0 ; i < *pProduceCount; i++) { WaitForSingleObject(hMutex, INFINITE); produce = 0 ; long ConsumptionThreadID = GetCurrentThreadId(); printf (" 消费者%ul消费了一个产品\n" , ConsumptionThreadID); ReleaseMutex(hMutex); } return 0 ; } int main (void ) { int produceCount = 10 ; int * pProduce = &produceCount; hMutex = CreateMutex(NULL , FALSE, NULL ); HANDLE hPCThread[2 ]; hPCThread[0 ] = CreateThread(NULL , 0 , ProduceThread, (LPTHREAD_START_ROUTINE)pProduce, 0 , NULL ); hPCThread[1 ] = CreateThread(NULL , 0 , ConsumptionThread, (LPTHREAD_START_ROUTINE)pProduce, 0 , NULL ); WaitForMultipleObjects(2 , hPCThread, TRUE, INFINITE); CloseHandle(hPCThread[0 ]); CloseHandle(hPCThread[1 ]); CloseHandle(hMutex); }
1 2 3 4 5 6 1.先创建一个互斥体 2.创建两个线程 3.在线程中等待互斥体 4.互斥体锁线程后就能实现生产一次,消费一次 但是,这样生产和消费的顺序可能搞反
出现了先消费、后生产的情况:
因此可以加条件判断现在有没有产品:
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 #include <stdio.h> #include <windows.h> HANDLE hEven; HANDLE hMutex; int produce;DWORD WINAPI ProduceThread (LPVOID lpParameter) { int * pProduceCount = (int *)lpParameter; for (int i = 0 ; i < *pProduceCount; i++) { WaitForSingleObject(hMutex, INFINITE); if (produce == 0 ) { produce = 1 ; long ProduceThreadID = GetCurrentThreadId(); printf ("生产者%ld生产了一个产品\n" , ProduceThreadID); } else { i--; } ReleaseMutex(hMutex); } return 0 ; } DWORD WINAPI ConsumptionThread (LPVOID lpParameter) { int * pProduceCount = (int *)lpParameter; for (int i = 0 ; i < *pProduceCount; i++) { WaitForSingleObject(hMutex, INFINITE); if (produce == 1 ) { produce = 0 ; long ConsumptionThreadID = GetCurrentThreadId(); printf (" 消费者%ul消费了一个产品\n" , ConsumptionThreadID); } else { i--; } ReleaseMutex(hMutex); } return 0 ; } int main (void ) { int produceCount = 10 ; int * pProduce = &produceCount; hMutex = CreateMutex(NULL , FALSE, NULL ); HANDLE hPCThread[2 ]; hPCThread[0 ] = CreateThread(NULL , 0 , ProduceThread, (LPTHREAD_START_ROUTINE)pProduce, 0 , NULL ); hPCThread[1 ] = CreateThread(NULL , 0 , ConsumptionThread, (LPTHREAD_START_ROUTINE)pProduce, 0 , NULL ); WaitForMultipleObjects(2 , hPCThread, TRUE, INFINITE); CloseHandle(hPCThread[0 ]); CloseHandle(hPCThread[1 ]); CloseHandle(hMutex); return 0 ; }
这样就可以实现先生产、后消费。
但是,如果在else{i–}这里查看到底浪费了多少次循环:
输出———的地方就是浪费的时间,占用了计算资源
线程同步 所以通过通知来优化这个程序:
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 #include <windows.h> #include <stdio.h> int container = 0 ;int count = 10 ;HANDLE eventA; HANDLE eventB; DWORD WINAPI ThreadProc (LPVOID lpParameter) { for (int i = 0 ; i < count; i++) { WaitForSingleObject(eventA, INFINITE); int threadId = GetCurrentThreadId(); container = 1 ; printf ("Thread: %d, Build: %d \n" , threadId, container); SetEvent(eventB); } return 0 ; } DWORD WINAPI ThreadProcB (LPVOID lpParameter) { for (int i = 0 ; i < count; i++) { WaitForSingleObject(eventB, INFINITE); int threadId = GetCurrentThreadId(); printf ("Thread: %d, Consume: %d \n" , threadId, container); container = 0 ; SetEvent(eventA); } return 0 ; } int main (int argc, char * argv[]) { eventA = CreateEvent(NULL , FALSE, TRUE, NULL ); eventB = CreateEvent(NULL , FALSE, FALSE, NULL ); HANDLE hThread[2 ]; hThread[0 ] = CreateThread(NULL , NULL , ThreadProc, NULL , 0 , NULL ); hThread[1 ] = CreateThread(NULL , NULL , ThreadProcB, NULL , 0 , NULL ); WaitForMultipleObjects(2 , hThread, TRUE, INFINITE); CloseHandle(hThread[0 ]); CloseHandle(hThread[1 ]); CloseHandle(eventA); CloseHandle(eventB); return 0 ; }
这样通过事件先让生产者执行,然后让生产者告诉消费者来消费,再由消费者告诉生产者生产的顺序执行,就合理利用了计算资源。
窗口 窗口的本质
ntoskrnl.exe和win32k.exe是系统提供的两个模块,kernel32.dll、user32.dl、gdi32.dll可以看成是接口
如果要使用user32.dll绘制窗口,就是GUI,使用gdi32.dll绘制窗口就是GDI。
在创建窗口中使用的句柄是HWND
,这个句柄是全局句柄,是公有的。
绘图 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 #include <stdio.h> #include <windows.h> #pragma warning (disable:6031) int main (void ) { HWND hwnd; HDC hdc; HPEN hpen; HBRUSH hbrush; hwnd = (HWND)NULL ; hdc = GetDC(hwnd); hpen = CreatePen(PS_SOLID, 5 , RGB(0xff , 0x45 , 0x10 )); hbrush = (HBRUSH)GetStockObject(DC_BRUSH); SelectObject(hdc, hpen); SetDCBrushColor(hdc, RGB(0xc0 , 0x30 , 0x00 )); LineTo(hdc, 2560 , 1600 ); Rectangle(hdc, 200 , 200 , 600 , 800 ); getchar(); DeleteObject(hpen); ReleaseDC(hwnd, hdc); return 0 ; }
Windows程序 创建
入口函数:
1 2 3 4 5 6 7 8 9 10 #include <windows.h> int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { }
窗口程序没有控制台打印输出,可以输出到调试里面
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <windows.h> #pragma warning (disable:4996) int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { char szOutBuff[0x80 ]; DWORD dwAddr = (DWORD)hInstance; sprintf (szOutBuff, "Buff: %d\n" , dwAddr); OutputDebugString(szOutBuff); }
第一步,定义窗口是什么样的
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 #include <stdio.h> #include <windows.h> #pragma warning (disable:4996) #pragma warning (disable:28251) LRESULT CALLBACK WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { char szOutBuff[0x80 ]; TCHAR className[] = TEXT("Window Application" ); WNDCLASS wndclass = { 0 }; wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND; wndclass.lpszClassName = className; wndclass.hInstance = hInstance; wndclass.lpfnWndProc = WindowProc; RegisterClass(&wndclass); HWND hwnd = CreateWindow( className, TEXT("My First Window" ), WS_OVERLAPPEDWINDOW, 200 , 100 , 600 , 400 , NULL , NULL , hInstance, NULL ); if (hwnd == NULL ) { sprintf (szOutBuff, "Error: %d\n" , GetLastError()); OutputDebugString(szOutBuff); return -10 ; } ShowWindow(hwnd,SW_SHOW); MSG msg; BOOL bRet; while ((bRet = GetMessage(&msg, NULL , 0 , 0 )) != 0 ) { if (bRet == -1 ) { sprintf (szOutBuff, "Error: %d\n" , GetLastError()); OutputDebugString(szOutBuff); } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0 ; } LRESULT CALLBACK WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hwnd, uMsg, wParam, lParam); }
消息处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 LRESULT CALLBACK WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: { PostQuitMessage(0 ); return 0 ; } case WM_CHAR: { char szOutBuff[0x80 ]; sprintf (szOutBuff, "按键:%x - %x - %c\n" , uMsg, wParam, wParam); OutputDebugString(szOutBuff); } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
子窗口控件 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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 #include <stdio.h> #include <windows.h> #pragma warning (disable:4996) #pragma warning (disable:28251) #define IDC_EDIT_1 0x100 #define IDC_BUTTON_1 0x101 #define IDC_BUTTON_2 0x102 LRESULT CALLBACK WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; HINSTANCE g_hInstance; int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { g_hInstance = hInstance; char szOutBuff[0x80 ]; TCHAR className[] = TEXT("Window Application" ); WNDCLASS wndclass = { 0 }; wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND; wndclass.lpszClassName = className; wndclass.hInstance = hInstance; wndclass.lpfnWndProc = WindowProc; RegisterClass(&wndclass); HWND hwnd = CreateWindow( className, TEXT("My First Window" ), WS_OVERLAPPEDWINDOW, 200 , 100 , 600 , 400 , NULL , NULL , hInstance, NULL ); if (hwnd == NULL ) { sprintf (szOutBuff, "Error: %d\n" , GetLastError()); OutputDebugString(szOutBuff); return -10 ; } ShowWindow(hwnd, SW_SHOW); MSG msg; BOOL bRet; while ((bRet = GetMessage(&msg, NULL , 0 , 0 )) != 0 ) { if (bRet == -1 ) { sprintf (szOutBuff, "Error: %d\n" , GetLastError()); OutputDebugString(szOutBuff); } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0 ; } LRESULT CALLBACK WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { char szOutBuff[0x80 ]; switch (uMsg) { case WM_DESTROY: { PostQuitMessage(0 ); return 0 ; } case WM_CREATE: { CreateWindow( TEXT("EDIT" ), "" , WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE, 10 , 10 , 200 , 100 , hwnd, (HMENU)IDC_EDIT_1, g_hInstance, NULL ); CreateWindow( TEXT("BUTTON" ), TEXT("设置" ), WS_CHILD | WS_VISIBLE, 300 , 300 , 100 , 30 , hwnd, (HMENU)IDC_BUTTON_1, g_hInstance, NULL ); CreateWindow( TEXT("BUTTON" ), TEXT("获取" ), WS_CHILD | WS_VISIBLE, 300 , 250 , 100 , 30 , hwnd, (HMENU)IDC_BUTTON_2, g_hInstance, NULL ); break ; } case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BUTTON_1: { SetDlgItemText(hwnd, IDC_EDIT_1, TEXT("123" )); break ; } case IDC_BUTTON_2: { GetDlgItemText(hwnd, IDC_EDIT_1,szOutBuff,100 ); MessageBox(hwnd, szOutBuff, szOutBuff, MB_OK); break ; } default : break ; } } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
一个文本框、两个按钮。
文件系统 卷相关API 1 2 3 4 5 6 7 8 9 10 11 <1>获取卷 GetLogicalDrives() <2>获取一个所卷的盘符的字符串 GetLogicalDrives() <3>获取卷的类型 GetLogicalDrives() <4>获取卷的类型 GetVolumelnformation()
目录相关API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <1>创建目录 CreateDirectory(); <2>删除目录 RemoveDirectory <3>修改目录名称 MoveFile(); <4>获取程序当前目录 GetCurrentDirectory(); <5>设置程序当前目录 SetCurrentDirectory
动态链接库 动态链接库 动态链接库(Dynamic Link Library,缩写DLL),是微软咋Windows操作系统中,实现共享函数库的一种方式,这些库函数的扩展名是.dll或.ocx
创建动态链接库 首先要告诉编译器,如果定义两个函数,这两个函数是要给别人用的
1 extern "C" _declspec(dllexport) 调用约定 返回类型 函数名(参数列表)
比如这两个函数:
1 2 3 4 5 6 7 8 int Plus (int x, int y) { return X + y; } int Sub (int x, int y) { return x - y; }
如果是自己用这两个函数,那么声明函数的时候直接:
1 2 int Plus (int x, int y) ;int Sub (int x, int y) ;
如果是要创建DLL,那么声明的时候使用:
1 2 extern "C" _declspec(dllexport) __sdtcall int Plus (int x, int y) ;extern "C" _declspec(dllexport) __sdtcall int Sub (int x, int y) ;
声明时也可以使用.def文件:
1 2 3 EXPORTS 函数名 @编号 函数名 @编号 NONAME
使用序号导出的好处:名字是一段程序最精炼的注释,通过名字可能直接猜测到函数的功能,通过使用序号,可以达到隐藏的目的。
使用DLL 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 #include <stdio.h> #include <windows.h> typedef int (_cdecl* lpPlus) (int , int ) ;typedef int (_cdecl* lpSub) (int , int ) ;lpPlus myPlus; lpSub mySub; int main (void ) { HINSTANCE hModule = LoadLibrary("C:\\Users\\23394\\Desktop\\code\\C C++\\Dll1\\Debug\\Dll1.dll" ); myPlus = (lpPlus)GetProcAddress(hModule, "plus" ); mySub = (lpSub)GetProcAddress(hModule, "sub" ); int x = myPlus(1 , 2 ); int y = mySub(5 , 2 ); FreeLibrary(hModule); return 0 ; }
示例代码:
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 #include "pch.h" #include <stdio.h> int plus (int x, int y) { return x + y; } int sub (int x, int y) { return x - y; } BOOL APIENTRY DllMain ( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { printf ("DLL_PROCESS_ATTACH\n" ); break ; } case DLL_THREAD_ATTACH: { printf ("DLL_THREAD_ATTACH\n" ); break ; } case DLL_THREAD_DETACH: { printf ("DLL_THREAD_DETACH\n" ); break ; } case DLL_PROCESS_DETACH: { printf ("DLL_PROCESS_DETACH\n" ); break ; } } return TRUE; }
pch.h中添加:
1 2 extern "C" _declspec(dllexport) int plus (int x, int y) ;extern "C" _declspec(dllexport) int sub (int x, int y) ;
编译完成后生成一个DLL
创建一个新项目,使用这个DLL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> #include <windows.h> typedef int (_cdecl* lpPlus) (int , int ) ;typedef int (_cdecl* lpSub) (int , int ) ;lpPlus myPlus; lpSub mySub; int main (void ) { HINSTANCE hModule = LoadLibrary("C:\\Users\\23394\\Desktop\\code\\C C++\\Dll1\\Debug\\Dll1.dll" ); myPlus = (lpPlus)GetProcAddress(hModule, "plus" ); mySub = (lpSub)GetProcAddress(hModule, "sub" ); int x = myPlus(1 , 2 ); int y = mySub(5 , 2 ); FreeLibrary(hModule); return 0 ; }
隐式链接 步骤 步骤1:将.dll
.lib
文件放到工程目录下面
步骤2:将`#pragma comment(lib,”DLL名.lib”)添加到调用文件中
步骤3:加入函数的声明
链接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <windows.h> #pragma comment(lib,"C:\\Users\\23394\\Desktop\\code\\C C++\\win32AP\\Dll1.lib" ) extern "C" int __declspec(dllimport) plus(int x, int y);extern "C" int __declspec(dllimport) sub(int x, int y);int main (void ) { printf ("plus(1, 2):%d, sub(3, 1):%d" , plus(1 , 2 ), sub(3 , 1 )); return 0 ; }
DLL入口函数 1 2 3 BOOL APIENTRY DLLMain (HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
参数:DWORD ul_reason_for_call
1 2 3 4 5 6 7 1. 当LoadLibrary时,DLL_PROCESS_ATTACH (LoadLibrary)2. 当FreeLibrary时,DLL_PROCESS_DETACH (FreeLibrary)3. 当在线程中加载dll时,DLL_THREAD_ATTACH4. 当加载这个dll的线程结束的时候也会再次加载这个dll,此时传递的参数就是DLL_THREAD_DETACH
远程线程 CreateRemoteThread 创建远程线程,就是在另一个进程中创建一个新的线程。比如我的进程是Demo.exe,那么可以在一个IE.exe中创建一个新的线程让他跑起来。
远程线程函数 1 2 3 4 5 6 7 8 9 HANDLE CreateRemoteThread ( [in] HANDLE hProcess, [in] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] SIZE_T dwStackSize, [in] LPTHREAD_START_ROUTINE lpStartAddress, [in] LPVOID lpParameter, [in] DWORD dwCreationFlags, [out] LPDWORD lpThreadId ) ;
这个函数与创建线程的函数基本相同,只是多了第一个参数,也就是要在哪个进程中创建这个线程。
实现 首先创建一个程序,里面创建一个线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdio.h> #include <windows.h> DWORD WINAPI ThreadProc (LPVOID lpParameter) { for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("-----%d-----\n" , i); } return 0 ; } int main (void ) { HANDLE hThread; hThread = CreateThread(NULL , 0 , ThreadProc, NULL , 0 , NULL ); CloseHandle(hThread); getchar(); return 0 ; }
这个线程在getchar处阻塞。
再写远程线程的程序:
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 #include <stdio.h> #include <Windows.h> BOOL MyCreateRemoteThread (DWORD dwProcessId, DWORD dwProcessAddr) { DWORD dwThreadId; HANDLE hProcess; HANDLE hThread; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (hProcess == NULL ) { OutputDebugString("OpenProcess failed! \n" ); return FALSE; } hThread = CreateRemoteThread( hProcess, NULL , 0 , (LPTHREAD_START_ROUTINE)dwProcessAddr, NULL , 0 , &dwThreadId ); if (hThread == NULL ) { OutputDebugString("CreateRemoteThread failed! \n" ); CloseHandle(hProcess); return FALSE; } CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } int main (void ) { MyCreateRemoteThread(39772 , 0x6117B0 ); return 0 ; }
MyCreateRemoteThread函数中的第一个参数,传入的是要创建远程线程的程序的PID,也就是上面那个程序的PID;第二个参数是第一个程序中创建的线程的地址。
远程线程注入 注入 什么是注入 所谓注入就是在第三方进程不知道或者不允许的情况下将模块或者代码写入对方进程空间,并设法执行的技术。
在安全领域,“注入”是非常重要的一种技术手段,注入与反注入也一直处于不断变化的,而且也愈来愈激烈的对抗当中。
已知的注入方式:
远程线程注入、APC注入、消息钩子注入、注册表注入、导入表注入、输入法注入等等。
事例:
远程线程注入程序.exe:
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 139 140 141 142 #include <stdio.h> #include <windows.h> #include <tlhelp32.h> #include <stdbool.h> #pragma warning (disable:4996) #pragma warning (disable:6031) BOOL LoadDll (DWORD dwProcessID, const char * szDllPathName) { BOOL bRet; HANDLE hProcess; HANDLE hThread; DWORD dwLength; DWORD dwLoadAddr; LPVOID lpAllocAddr; DWORD dwThreadID; HMODULE hModule; bRet = 0 ; dwLoadAddr = 0 ; hProcess = 0 ; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL ) { OutputDebugString("OpenProcess failed! \n" ); return FALSE; } dwLength = strlen (szDllPathName) + 1 ; lpAllocAddr = VirtualAllocEx(hProcess, NULL , dwLength, MEM_COMMIT, PAGE_READWRITE); if (lpAllocAddr == NULL ) { OutputDebugString("VirtualAllocEx failed! \n" ); CloseHandle(hProcess); return FALSE; } bRet = WriteProcessMemory(hProcess, lpAllocAddr, reinterpret_cast<LPCVOID>(szDllPathName), dwLength, NULL ); if (!bRet) { OutputDebugString("WriteProcessMemory failed! \n" ); CloseHandle(hProcess); return FALSE; } hModule = GetModuleHandle("kernel32.dll" ); if (!hModule) { OutputDebugString("GetModuleHandle failed! \n" ); CloseHandle(hProcess); return FALSE; } dwLoadAddr = reinterpret_cast<DWORD>(GetProcAddress(hModule, "LoadLibraryA" )); if (!dwLoadAddr) { OutputDebugString("GetProcAddress failed! \n" ); CloseHandle(hModule); CloseHandle(hProcess); return FALSE; } hThread = CreateRemoteThread(hProcess, NULL , 0 , reinterpret_cast<LPTHREAD_START_ROUTINE>(dwLoadAddr), lpAllocAddr, 0 , &dwThreadID); if (!hThread) { OutputDebugString("CreateRemoteThread failed! \n" ); DWORD dwError = GetLastError(); printf ("CreateRemoteThread failed with error: %d\n" , dwError); CloseHandle(hModule); CloseHandle(hProcess); return FALSE; } CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } DWORD GetProcessIdByName (const char * processName) { HANDLE hSnap; PROCESSENTRY32 pe32; DWORD processId = 0 ; hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 ); if (hSnap == INVALID_HANDLE_VALUE) { printf ("Failed to create snapshot\n" ); return 0 ; } pe32.dwSize = sizeof (PROCESSENTRY32); if (!Process32First(hSnap, &pe32)) { printf ("Failed to get the first process\n" ); CloseHandle(hSnap); return 0 ; } while (true ) { if (strcmp (pe32.szExeFile, processName) == 0 ) { processId = pe32.th32ProcessID; break ; } if (!Process32Next(hSnap, &pe32)) { break ; } } CloseHandle(hSnap); return processId; } int main (int argc, char * argv[]) { const char * processName = "被注入程序.exe" ; DWORD processId = GetProcessIdByName(processName); if (processId == 0 ) { printf ("Failed to find the process\n" ); } else { printf ("Process ID: %lu\n" , processId); } LoadDll(processId, "./远程线程注入dll.dll" ); getchar(); return 0 ; }
被注入程序.exe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <windows.h> #pragma warning (disaboe:6031) DWORD WINAPI ThreadProc (LPVOID lpParameter) { for (int i = 0 ; i < 10 ; i++) { Sleep(1000 ); printf ("-----%d-----\n" , i + 1 ); } return 0 ; } int main (void ) { HANDLE hThread; hThread = CreateThread(NULL , 0 , ThreadProc, NULL , 0 , NULL ); CloseHandle(hThread); getchar(); return 0 ; }
注入的DLL:
pch.h头文件:
1 2 3 4 5 6 7 8 #ifndef PCH_H #define PCH_H #include "framework.h" #include <stdio.h> #endif
dllmain.cpp:
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 #include "pch.h" DWORD WINAPI ThreadProc (LPVOID lpParameter) { while (1 ) { printf ("The program is being injected\n" ); Sleep(1000 ); } return 0 ; } BOOL APIENTRY DllMain ( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CreateThread(NULL , 0 , ThreadProc, NULL , 0 , NULL ); break ; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break ; } return TRUE; }
模块隐藏 模块隐藏之断链 TEB(Thread Environment Block线程环境块) ,他记录线程相关的信息,每一个线程都有自己的TEB,FS:[0]
是当前线程的TEB。
PEB(Process Environment Block进程环境块)存放进程信息,每个进程都有自己的PEB信息,TEB偏移0x30即当前进程的PEB地址
TEB和PEB都在用户空间
在OD中使用指令dd [FS]
,跳转到TEB的位置:
比如,先打开kernel32.dll的位置,
当API函数遍历模块的时候就是查PEB中的表
PEB断链原码 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 #include <stdio.h> #include <Windows.h> typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, * PUNICODE_STRING; typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; }PEB_LDR_DATA, * PPEB_LDR_DATA; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializeationOrderModuleList; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; HANDLE SectionHandle; ULONG CheckSum; ULONG TimeDataStamp; }LDR_MODULE, * PLDR_MODULE; PEB_LDR_DATA* g_pPebLdr = NULL ; LDR_MODULE* g_pLdrModule = NULL ; LIST_ENTRY* g_pInLoadOrderModule; LIST_ENTRY* g_pInMemoryOrderModule; LIST_ENTRY* g_pInInitializeationOrderModule; void ring3BrokenChains (HMODULE hModule) { LIST_ENTRY* pHead = g_pInLoadOrderModule; LIST_ENTRY* pCur = pHead; do { pCur = pCur->Blink; g_pLdrModule = (PLDR_MODULE)pCur; if (hModule == g_pLdrModule->DllBase) { g_pLdrModule->InLoadOrderModuleList.Blink->Flink = g_pLdrModule->InLoadOrderModuleList.Flink; g_pLdrModule->InLoadOrderModuleList.Flink->Blink = g_pLdrModule->InLoadOrderModuleList.Blink; g_pLdrModule->InInitializeationOrderModuleList.Blink->Flink = g_pLdrModule->InInitializeationOrderModuleList.Flink; g_pLdrModule->InInitializeationOrderModuleList.Flink->Blink = g_pLdrModule->InInitializeationOrderModuleList.Blink; g_pLdrModule->InMemoryOrderModuleList.Blink->Flink = g_pLdrModule->InMemoryOrderModuleList.Flink; g_pLdrModule->InMemoryOrderModuleList.Flink->Blink = g_pLdrModule->InMemoryOrderModuleList.Blink; break ; } } while (pHead != pCur); } int main (int argc, char * argv[]) { __asm { mov eax, fs: [0x30 ] ; mov ecx, [eax + 0xC ]; mov g_pPebLdr, ecx; mov ebx, ecx; add ebx, 0xC ; mov g_pInLoadOrderModule, ebx; mov ebx, ecx; add ebx, 0x14 ; mov g_pInMemoryOrderModule, ebx; mov ebx, ecx; add ebx, 0x1C ; mov g_pInInitializeationOrderModule, ebx; } printf ("点任意按键开始断链" ); getchar(); ring3BrokenChains(GetModuleHandleA("kernel32.dll" )); printf ("断链成功\n" ); getchar(); return 0 ; }
比如:使用以上函数,将以下几个库都断开
1 2 ring3BrokenChains(GetModuleHandleA("kernel32.dll" )); ring3BrokenChains(GetModuleHandleA("ntdll.dll" ));
断开前:
断开后:
参见:[原创]超详细的3环和0环断链隐藏分析-软件逆向-看雪-安全社区|安全招聘|kanxue.com
代码注入 代码注入原则 四种代码不能注入的情况:
<1> 不能有全局变量 <2> 不能使用常量字符串 <3> 不能使用系统调用 <4> 不能嵌套其他函数
参数传递 有这么多限制该怎么办?假设我们要将代码进程的代码拷贝过去,这段代码的作用就是创建文件,那么它得流程可以如下图所示:
首先将代码进程的ThreadProc复制过去,然后将复制过去之后目标进程的地址给到CreateRemoteThread 函数,这样就解决了自定义函数的问题;
其次我们要创建文件的话就必须要使用CreateFile 函数,我们不能直接这样写,因为它依赖当前进程的导入表,当前进程和目标进程导入表的地址肯定是不一样的,所以不符合复制代码的编写原则 ;所以我们可以通过线程函数的参数来解决,我们先将所有用到的目标参数写到一个结构体中复制到目标进程,然后将目标进程结构体的地址作为线程函数的参数。
代码实现 传递参数进行远程注入代码的实现:
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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 #include <tlhelp32.h> #include <stdio.h> #include <windows.h> typedef struct { DWORD dwCreateAPIAddr; LPCTSTR lpFileName; DWORD dwDesiredAccess; DWORD dwShareMode; LPSECURITY_ATTRIBUTES lpSecurityAttributes; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes; HANDLE hTemplateFile; } CREATEFILE_PARAM; typedef HANDLE (WINAPI* PFN_CreateFile) ( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) ;DWORD _stdcall CreateFileThreadProc (LPVOID lparam) { CREATEFILE_PARAM* Gcreate = (CREATEFILE_PARAM*)lparam; PFN_CreateFile pfnCreateFile; pfnCreateFile = (PFN_CreateFile)Gcreate->dwCreateAPIAddr; pfnCreateFile( Gcreate->lpFileName, Gcreate->dwDesiredAccess, Gcreate->dwShareMode, Gcreate->lpSecurityAttributes, Gcreate->dwCreationDisposition, Gcreate->dwFlagsAndAttributes, Gcreate->hTemplateFile ); return 0 ; } BOOL RemotCreateFile (DWORD dwProcessID, char * szFilePathName) { BOOL bRet; DWORD dwThread; HANDLE hProcess; HANDLE hThread; DWORD dwThreadFunSize; CREATEFILE_PARAM GCreateFile; LPVOID lpFilePathName; LPVOID lpRemotThreadAddr; LPVOID lpFileParamAddr; DWORD dwFunAddr; HMODULE hModule; bRet = 0 ; hProcess = 0 ; dwThreadFunSize = 0x400 ; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (hProcess == NULL ) { OutputDebugString("OpenProcessError! \n" ); return FALSE; } lpFilePathName = VirtualAllocEx(hProcess, NULL , strlen (szFilePathName)+1 , MEM_COMMIT, PAGE_READWRITE); lpRemotThreadAddr = VirtualAllocEx(hProcess, NULL , dwThreadFunSize, MEM_COMMIT, PAGE_READWRITE); lpFileParamAddr = VirtualAllocEx(hProcess, NULL , sizeof (CREATEFILE_PARAM), MEM_COMMIT, PAGE_READWRITE); GCreateFile.dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; GCreateFile.dwShareMode = 0 ; GCreateFile.lpSecurityAttributes = NULL ; GCreateFile.dwCreationDisposition = OPEN_ALWAYS; GCreateFile.dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; GCreateFile.hTemplateFile = NULL ; hModule = GetModuleHandle("kernel32.dll" ); GCreateFile.dwCreateAPIAddr = (DWORD)GetProcAddress(hModule, "CreateFileA" ); FreeLibrary(hModule); GCreateFile.lpFileName = (LPCTSTR)lpFilePathName; dwFunAddr = (DWORD)CreateFileThreadProc; if (*((BYTE*)dwFunAddr) == 0xE9 ) { dwFunAddr = dwFunAddr + 5 + *(DWORD*)(dwFunAddr + 1 ); } WriteProcessMemory(hProcess, lpFilePathName, szFilePathName, strlen (szFilePathName) + 1 , 0 ); WriteProcessMemory(hProcess, lpRemotThreadAddr, (LPVOID)dwFunAddr, dwThreadFunSize, 0 ); WriteProcessMemory(hProcess, lpFileParamAddr, &GCreateFile, sizeof (CREATEFILE_PARAM), 0 ); hThread = CreateRemoteThread(hProcess, NULL , 0 , (LPTHREAD_START_ROUTINE)lpRemotThreadAddr, lpFileParamAddr, 0 , &dwThread); if (hThread == NULL ) { OutputDebugString("CreateRemoteThread Error! \n" ); CloseHandle(hProcess); CloseHandle(hModule); return FALSE; } CloseHandle(hProcess); CloseHandle(hThread); CloseHandle(hModule); return TRUE; } DWORD GetPID (char *szName) { HANDLE hProcessSnapShot = NULL ; PROCESSENTRY32 pe32 = {0 }; hProcessSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 ); if (hProcessSnapShot == (HANDLE)-1 ) { return 0 ; } pe32.dwSize = sizeof (PROCESSENTRY32); if (Process32First(hProcessSnapShot, &pe32)) { do { if (!strcmp (szName, pe32.szExeFile)) { return (int )pe32.th32ProcessID; } } while (Process32Next(hProcessSnapShot, &pe32)); } else { CloseHandle(hProcessSnapShot); } return 0 ; } int main () { RemotCreateFile(GetPID("进程名" ), "文件名" ); return 0 ; }