一、字符
1.字符編碼
(1)原始的ASCII表: 0—7F(0—127)
(2)擴展的ASCII表: 0—FF(0—255) GB2312或GB2312-80 <用2個字節編碼一個漢字>
(3)Unicode編碼:
編碼範圍:0—0x10FFFF
存在問題:只規定了符號的二進制代碼,沒有規定代碼如何存儲
(4)如何存儲Unicode:UTF-16/UTF-8是Unicode的實現方式
UTF-16:以16位無符號整數爲單位,以2個字節對齊
UTF-8: 變長編碼,以1/2/3/4字節爲單位
Unicode編碼(16進制) | UTF-8字節流(二進制) |
000000-00007F | 0xxxxxxx |
000080-0007FF | 110xxxxx 0xxxxxxx |
000800-00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
010000-10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
BOM(Byte Order Mark):表明文件的存儲格式,位於文件開頭
UTF-8 |
EF BB BF |
UTF-16LE(小端) |
FF FE |
UTF-16BE(大端) | FE FF |
字符:"A中"
UTF-16: 41 00 2D 4E (小端存儲)
01000001 00000000 00101101 01001110
UTF-8: 41 E4 B8 AD
01000001 1110(0100) 10(111000) 10(101101)
(0100) (111000) (101101) = 01001110 00101101 = 4E 2D
2.C語言的寬字符
(1)字符串在內存中的值
char szStr[] = "中國";
D6 D0 B9 FA 00 //拓展的ASCII,5字節
wchar_t swzStr[] = L"中國"; //以UTF-16存儲
2D 4E FD 56 00 00 //Unicode編碼,6字節
(2)在控制檯打印寬字符
#include<locale.h>
setlocale(LC_ALL,""); //使用控制檯默認的編碼
wchar_t swzStr[] = L"中國";
wprintf(L"%S\n",x1);
(3)字符串長度
char szStr[] = "abcde";
wchar_t swzStr[] = L"abcde";
strlen(szStr); //獲取字符長度,不包含00,返回5
wcslen(swzStr); //獲取字符長度,不包含00 00,返回5
//分別用於ASCII字符串和Unicode的字符串,不同的字符串要使用對應的函數,如果強制轉型使用錯誤的函數成功編譯,不能得到正確的結果
(4)常用函數
char wchar_t //多字節字符類型 寬字符類型
printf wprintf //打印到控制檯函數
strlen wcslen //獲取長度
strcpy wcscpy //字符串複製
strcat wcscat //字符串拼接
strcmp wcscmp //字符串比較
strstr wcsstr //字符串查找
3.API中的寬字符
(1)概念(幾個重要的DLL)
Kernel32.dll | 最核心的功能模塊,如:內存管理、進程/線程處理 |
User32.dll | Windows用戶界面相關應用程序接口,如:創建窗口、發送消息 |
GDI32.dll | 全稱Graphical Device Interface(圖形設備接口),包含:用於畫圖和顯示文本的函數 |
(2)在Win32中使用字符串
MessageBox()函數
LPCTSTR = LPCSTR = CONST CHAR* = const char*
字符類型:
CHAR szStr[] = "中國"; //ASCII
WCHAR swzStr[] = L"中國"; //Unicode
TCHAR stzStr[] = TEXT("中國"); //TCHAR是一個宏,根據項目默認使用的字符自動轉換字符編碼
字符串指針:
PSTR pszStr[] = "中國"; //char*
PWSTR pwszStr[] = L"中國"; //wchat_t*
PTSTR ptszStr[] = TEXT("中國"); //PTSTR是一個宏,有利於跨平臺
//建議使用TCHAR和PTSTR
二、多線程
1.進程的創建過程
(1)概念:進程爲程序提供所需的資源(代碼/數據),線程使用資源;
(2)進程內存空間的地址劃分
分區 | x86 32位Windows |
空指針賦值區 | 0x00000000 - 0x0000FFFF(從未使用) |
用戶模式區 | 0x00010000 - 0x7FFEFFFF |
64KB禁入區 | 0x7FFF0000 - 0x7FFFFFFF |
內核 | 0x80000000 - 0xFFFFFFFF(所有進程共用內核高2GB的內存空間) |
(3)進程的創建過程:任何進程都是由別的進程創建的,進程創建時總會創建一個線程,CreateProcess()
1.映射EXE文件 (A.exe)
2.創建內核對象EPROCESS,每個進程都有自己的EPROCESS對象
3.映射系統DLL(ntdll.dll)
4.創建系統內核對象ETHREAD
5.系統啓動線程:
//將A進程要執行的DLL換成B進程的,最終執行的是B
映射DLL(ntdll.LdrlnitializeThunk)
線程開始執行
2.創建進程的函數
BOOL CreateProcess
(
LPCTSTR lpApplicationName, //對象名稱,完整的路徑名
LPTSTR lpCommandLine, //命令行參數,可以不使用爲NULL
LPSECURITY_ATTRIBUTES lpProcessAttributes, //創建的進程是否允許被繼承
LPSECURITY_ATTRIBUTES lpThreadAttributes, //同時創建的線程是否允許被繼承
BOOL bInheritHandles, //是否要繼承父進程的句柄表
DWORD dwCreationFlags, //CREATE_SUSPENDED
LPVOID lpEnvironment, //環境塊
LPCTSTR lpCurrentDirectory, //當前進程的工作目錄
LPSTARTUPINFO lpStartupInfo, //新窗口如何顯示,STARTUPINFO結構存儲啓動狀態,IN參數
LPPROCESS_INFORMATION lpProcessInformation //PROCESS_INFORMATION結構,OUT參數
);
(1)命令行
(2)由父進程爲子進程填充STARTUPINFO結構
#include "stdio.h"
#include <windows.h>
void AntiDebug()
{
STARTUPINFO si; //每個進程創建都有這個結構體,值由父進程填充
GetStartupInfo(&si); //獲取啓動進程的信息
printf("%x %x %x %x %x %x %x %x\n", si.dwX, si.dwY, si.dwXCountChars,
si.dwYCountChars, si.dwFillAttribute, si.dwXSize, si.dwYSize,si.dwFlags);
}
BOOL CreateChildProcess(PTCHAR szChildProcessName, PTCHAR szCommandLine)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(si)); //把結構體初始值至0
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si); //此成員必須初始化
//創建子進程,返回成功與失敗
if (!CreateProcess(szChildProcessName, //對象名稱
szCommandLine, //命令行
NULL, //不繼承進程句柄
NULL, //不繼承線程句柄
FALSE, //不繼承句柄,子進程由獨立的句柄表;若爲True,子進程可獲取父進程可繼承的句柄表
0, //沒有創建標誌,dwFlags
NULL, //使用父進程環境變量
NULL, //使用父進程目錄作爲當前目錄,可以自己設置目錄
&si,
&pi))
{
printf("創建子進程失敗:%d \n", GetLastError()); return FALSE;
}
//釋放句柄
CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return TRUE;
}
int main(int argc, char* argv[])
{
TCHAR szApplicationName[] = TEXT("E://360瀏覽器//360se6//Application//360se.exe");
TCHAR szCmdLine[] = TEXT(" 61.135.169.121"); //注意空格
CreateChildProcess(szApplicationName, szCmdLine);
//printf("%s - %s \n", argv[0], argv[1]);
//AntiDebug();
getchar();
return 0;
}
3.句柄表
(1)內核對象:進程、線程、文件、互斥體、事件等在內核有一個對應的結構體,結構體由內核負責管理,稱爲內核對象;
(2)管理內核對象:句柄表是每個進程私有的一張表,句柄值在本進程內纔有意義;內核對象可用CloseHandle關閉; SuspendThread(); ResumeThread();
(3)多進程共享一個內核對象(方式一)
- 內核對象A使用計數爲2;CloseHandle只是將內核對象的使用計數減1,並不是關閉;當使用計數爲0,內核對象會被關閉;
- 結束一個線程句柄不會結束相關聯的線程,要關閉線程,既要結束線程,也要使使用計數爲0;只要線程不結束,進程就不會結束;
(4)句柄是否可以被繼承(共享內核方式二)
- 創建的內核對象中一定包含安全描述符(SECURITY_ATTRIBUTES結構);
- 句柄表表項:當前對象繼承標誌、索引、內核結構體地址;
- 下圖,父進程中2、4項可被子進程繼承,子進程獲取父進程句柄表,其中子進程可直接使用父進程的句柄值;
4.進程相關API
(1)ID與句柄
- 全局句柄表(操作系統所有):包含所有正在運行中的進程和線程;
- 進程ID/線程ID:全局句柄表的一個索引;
- 進程句柄:進程句柄表的索引;
(2)以掛起的形式創建進程:CREATE_SUSPENDED參數
//進程創建過程發生改變
1.映射EXE文件 (A.exe)
2.創建內核對象EPROCESS,每個進程都有自己的EPROCESS對象
3.映射系統DLL(ntdll.dll)
4.創建系統內核對象ETHREAD
5.進程以掛起的方式創建:
。。。進程進入等待狀態 //可在此注入自己的代碼
5.恢復以後再繼續執行:
映射DLL(ntdll.LdrlnitializeThunk)
線程開始執行
(3)模塊目錄、工作目錄
GetModuleFileName(); //獲取當前模塊(運行的exe)所在的路徑
GetCurrentDirectory(); //獲取當前工作路徑,工作路徑由父進程填充
(4)其他進程相關API
獲取進程PID:GetCurrentProcessId
獲取進程句柄:GetCurrentProcess
獲取命令行:GetCommandLine
獲取啓動信息:GetStartupInfo
遍歷進程ID:EnumProcess
進程快照:CreateToolhelp32Snapshot //獲取系統當前全部進程,進程擁有的模塊