字符编码
标准的ASCII码
范围是0-127
ASCII码的拓展
范围是128-255
GB2312或GB2312-80
这两个编码是由两张ASCII码的拓展的编码组成
也就是说两个字节表示一个中文
字节1的范围是 128-255
字节2的范围是 128-255
UNICODE编码
UNICODE编码包含全世界所有文字的一个编码表
Unicode编码范围是:0-0x10FFFF,可以容纳100多万个符号!
Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
如何存储UNICODE
UTF-16
UTF-16编码以16位无符号整数为单位,注意是16位为一个单位,不表示一个字符就只有16位。这个要看字符的unicode编码处于什么范围而定,有可能是2个字节,也可能是4个字节现在机器上的unicode编码一般指的就是UTF-16
意思是utf-16是以两个字节来划分字符编码的,假如一个字符需要4个字节来表示,UTF-16形式存储会将4字节中的前两个字节编码和后两个字节编码分别存放.
UTF-8:
1 2 3 4 5 Unicode编码(16进制) ║ UTF-8 字节流(二进制) 000000 - 00007F ║ 0xxxxxxx 000080 - 0007FF ║ 110xxxxx 10xxxxxx 000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
000000 - 00007F 范围用1个字节存放
000080 - 0007FF 范围用2个字节存放
000800 - 00FFFF 范围用3个字节存放
010000 - 10FFFF 范围用4个字节存放
BOM(Byte Order Mark):
UTF-8 编码存放的文件十六进制开头是EF BB BF
UTF-16LE UTF-16小端存放开头是 FF FE
UTF-16BE UTF-16大端存放开头 FE FF
1 2 3 UTF-8 ║ EF BB BF UTF-16LE ║ FF FE UTF-16BE ║ FE FF
C语言中的宽字符
1 2 3 char szStr[] = "中国"; wchar_t swzStr[] = L"中国";
如果字符串前面不加L,默认是以IDE项目设置的字符集形式,可能是ASCII码
字符串前面加上L,表示用UNICODE字符集存放
wchar_t是宽字符类型,两个字节
打印宽字符
1 2 3 4 5 6 7 #include <locale.h> //要包含这个头文件 setlocale(LC_ALL,"");//使用控制台默认的编码 wchar_t swzStr[] = L“中国”; wprintf(L"%s\n",x1);
字符串长度
1 2 3 4 char szStr[] = "中国"; wchar_t swzStr[] = L"中国"; strlen(szStr); //取得多字节字符串中字符长度,不包含 00 wcslen(swzStr); //取得多字节字符串中字符长度,不包含 00 00
常用函数
1 2 3 4 5 6 7 8 9 10 11 char wchar_t //多字节字符类型 宽字符类型 printf wprintf //打印到控制台函数 strlen wcslen //获取长度 strcpy wcscpy //字符串复制 strcat wcscat //字符串拼接 strcmp wcscmp //字符串比较 strstr wcsstr //字符串查找
Win32 API中的宽字符
什么是Win32 API? 有哪些? 在哪里?
主要是存放在 C:\WINDOWS\system32 下面所有的dll
如果是64位系统,system32存放的是64位的DLL,SysWOW64存放的是32位的DLL
几个重要的DLL:
<1> Kernel32.dll:最核心的功能模块,比如管理内存、进程和线程相关的函数等。
<2> User32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等。
<3> GDI32.dll:全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数。
常用数据类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 汇编: byte BYTE PBYTE word WORD PWORD dword DWORD PDWORD C语言: char CHAR PCHAR unsigned char UCHAR PUCHAR short SHORT PSHORT unsigned short USHORT PUSHORT int INT PINT unsigned int UINT PUINT C++语言: bool BOOL
在Win32中使用字符串:
字符类型:
最好使用TCHAR类型,因为TCHAR类型会根据IDE设置的字符集来选择用哪个类型,如果IDE使用的是ASCII字符集,选择的就是CHAR类型,否则选择的是WCHAR类型。
1 2 3 CHAR szStr[] = “中国”; WCHAR swzStr[] = L“中国”; TCHAR stzSr[] = TEXT(“中国”);
TEXT(“中国”),会自动将字符串转换为当前IDE设置的字符集
字符串指针:
PTSTR也是会根据IDE设置的字符集来选择什么类型的字符串指针
1 2 3 PSTR pszStr = “中国”; PWSTR pwszStr = L“中国”; PTSTR ptszStr = TEXT(“中国”);
第一个Win32 API的使用:
写Win32程序要包含windows.h头文件
1 2 3 CHAR szTitle[] = "标题"; CHAR szContent[] = "欢迎大家来的Win32 API世界!"; MessageBoxA(0,szContent,szTitle,MB_OK);
1 2 3 WCHAR swzTitle[] = L"标题"; WCHAR swzContent[] = L"欢迎大家来的Win32 API世界!"; MessageBoxW(0,swzContent,swzTitle,MB_OK);
1 2 3 TCHAR stzTitle[] = TEXT("标题"); TCHAR stzContent[] = TEXT("欢迎大家来的Win32 API世界!"); MessageBox(0,stzContent,stzTitle,MB_OK);
MessageBox 会根据平台使用的字符集来选择调用MessageBoxA还是MessageBoxW,其实MessageBoxA内部也是调用MessageBoxW的,只不过会将字符串参数转换为Unicode编码,然后再调用
进程
什么是进程
进程提供程序所需的资源,如:数据、代码等等。
进程内存空间的地址划分
分区
x86 32位Windows
空指针赋值区
0x00000000 - 0x0000FFFF
用户模式区
0x00010000 - 0x7FFEFFFF
64KB禁入区
0x7FFF0000 - 0x7FFFFFFF
内核
0x80000000 - 0xFFFFFFFF
进程的组成部分:模块
在OD中查看进程所包含的模块。
每个模块都是一个可执行文件,遵守相同的格式,即PE结构。
进程的创建:
任何进程都是别的进程创建的:CreateProcess()
进程的创建过程
1、映射EXE文件
2、创建内核对象EPROCESS
3、映射系统DLL(ntdll.dll)
4、创建线程内核对象ETHREAD
5、系统启动线程
映射DLL(ntdll.LdrInitializeThunk)
线程开始执行
创建进程
CreateProcess
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, // 指定主窗口的外观及默认的I/O属性 LPPROCESS_INFORMATION lpProcessInformation // 接收新进程和其主线程的信息 );
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 <windows.h> #include <tchar.h> int _tmain(int argc, _TCHAR* argv[]){ LPCTSTR command = _T("C:\\路径\\到\\你的\\可执行文件.exe" ); PROCESS_INFORMATION processInfo; ZeroMemory(&processInfo, sizeof (PROCESS_INFORMATION)); STARTUPINFO startupInfo; ZeroMemory(&startupInfo, sizeof (STARTUPINFO)); startupInfo.cb = sizeof (STARTUPINFO); if (CreateProcess( NULL , command, NULL , NULL , FALSE, 0 , NULL , NULL , &startupInfo, &processInfo )) { WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } else { _tprintf(_T("无法创建进程,错误码:%d\n" ), GetLastError()); } return 0 ; }
STARTUPINFO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 typedef struct _STARTUPINFO { DWORD cb; // 结构体的大小,用于指定结构体版本 LPTSTR lpReserved; // 保留,必须为NULL LPTSTR lpDesktop; // 指定新进程的桌面,通常为NULL LPTSTR lpTitle; // 指定新进程的控制台窗口标题,通常为NULL DWORD dwX; // 指定新进程窗口的初始X坐标 DWORD dwY; // 指定新进程窗口的初始Y坐标 DWORD dwXSize; // 指定新进程窗口的初始宽度 DWORD dwYSize; // 指定新进程窗口的初始高度 DWORD dwXCountChars; // 指定新进程窗口的初始宽度(字符单位) DWORD dwYCountChars; // 指定新进程窗口的初始高度(字符单位) DWORD dwFillAttribute; // 控制新进程窗口的文本和背景颜色 DWORD dwFlags; // STARTF_* 标志位,用于指定 STARTUPINFO 结构体的标志 WORD wShowWindow; // 指定新进程窗口的显示状态 WORD cbReserved2; // 保留,必须为0 LPBYTE lpReserved2; // 保留,必须为NULL HANDLE hStdInput; // 指定新进程的标准输入句柄 HANDLE hStdOutput; // 指定新进程的标准输出句柄 HANDLE hStdError; // 指定新进程的标准错误句柄 } STARTUPINFO, *LPSTARTUPINFO;
1 2 3 4 5 6 typedef struct _PROCESS_INFORMATION { HANDLE hProcess; // 新进程的句柄,用于操作新进程 HANDLE hThread; // 新进程的主线程的句柄,用于操作新进程的主线程 DWORD dwProcessId; // 新进程的进程标识符 DWORD dwThreadId; // 新进程的主线程标识符 } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
GetLastError
GetLastError 函数返回一个 DWORD 类型的错误代码,表示最近一次发生的错误。
通常,当一个 Windows API 函数调用失败时,可以使用 GetLastError 函数获取详细的错误信息。错误代码可以通过查阅 Windows API 文档或使用 FormatMessage 函数转换为可读的错误消息。
1 DWORD GetLastError(void);
ZeroMemory
ZeroMemory 不是一个单独的函数,而是一个宏(macro),通常用于将内存区域的内容全部设置为零。以下是 ZeroMemory 宏的定义和简要中文注释:
1 #define ZeroMemory(Destination, Length) memset((Destination), 0, (Length))
ZeroMemory 宏使用 memset 函数将目标内存区域的内容全部设置为零。
Destination:指向要清零的内存区域的指针。
Length:要清零的内存区域的字节数。
这个宏的作用等同于使用 memset 函数将内存清零,但它是一种简化的写法。在实际使用中,可以选择使用 memset 函数或 ZeroMemory 宏,两者的效果是相同的。
GetStartupInfo
GetStartupInfo 是一个用于获取当前进程的 STARTUPINFO 结构体信息的 Windows API 函数。以下是该函数的原型和简要中文注释:
1 2 3 void GetStartupInfo( LPSTARTUPINFO lpStartupInfo // 指向 STARTUPINFO 结构体的指针,用于接收当前进程的启动信息 );
调试器填写的STARTUPINFO 成员和系统打开进程填写的成员,内容是不一样的
CloseHandle
CloseHandle 是一个 Windows API 函数,用于关闭一个打开的内核对象的句柄。以下是该函数的原型和简要中文注释:
1 2 3 BOOL CloseHandle( HANDLE hObject // 要关闭的内核对象的句柄 );
句柄表
进程内核对象下有个句柄表,句柄表存放了句柄编号,当前内核对象是否允许被继承和内核对象的地址,我们用的就是句柄编号,通过句柄编号来使用对应的内核对象.
句柄表是自己进程私有的
多个进程可以共享同一个内核对象
刚创建一个内核对象,默认计数器是1,如果再次打开同一个内核对象,计数器就是加1,也就是2
只有计数器是0的时候,该内核对象才真正被释放
关闭线程内核对象,必须关闭该线程,才能关闭线程内核对象.
句柄表有一列包含了当前内核对象是否可以被继承
如何使当前对象可以被继承?
需要用到SECURITY_ATTRIBUTES 结构体成员
1 2 3 4 5 6 7 8 9 typedef struct _SECURITY_ATTRIBUTES { // sa DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; //当前内核对象是否可以被继承,true代表可以 } SECURITY_ATTRIBUTES;
例如CreateEvent函数参数中就有LPSECURITY_ATTRIBUTES,这里代表传入一个SECURITY_ATTRIBUTES结构体,代表当前创建出来的内核对象可以被继承.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) ;
1 2 3 4 SECURITY_ATTRIBUTES sa; sa.nLength = sizeof (sa); HANDLE h = CreateEvent(&sa,FALSE,FALSE,NULL );
ID与句柄
句柄编号是进程私有的,id是系统全局句柄表中的索引,该表存放了该系统的所有进程和线程内核对象
以挂起的形式创建进程
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_INFORMATION lpProcessInformation ) ;
dwCreationFlags 设置成CREATE_SUSPENDED,并用ResumeThread函数唤醒线程。
1 DWORD WINAPI ResumeThread(_In_ HANDLE hThread);
模块目录与工作目录
获取当前模块目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 DWORD WINAPI GetModuleFileName( _In_opt_ HMODULE hModule, _Out_ LPTSTR lpFilename, _In_ DWORD nSize ); hModule Long 一个模块的句柄。可以是一个DLL模块,或者是一个应用程序的实例句柄。如果该参数为NULL, 该函数返回该应用程序全路径。 lpFileName String 指定一个字串缓冲区,要在其中容纳文件的用NULL字符中止的路径名,hModule模块就是从这个文件装载进来的 nSize Long 装载到缓冲区lpFileName的最大字符数量 返回值 Long,如执行成功,返回复制到lpFileName的实际字符数量;零表示失败。使用GetLastError可以打印错误信息。
获取当前工作目录
1 2 3 4 5 6 7 8 9 10 DWORD GetCurrentDirectory( DWORD nBufferLength, LPTSTR lpBuffer ); nBufferLength 缓冲区的长度 lpBuffer 指定一个预定义字串,用于装载当前目录 返回值 调用成功 返回装载到lpBuffer的字节数。 使用GetLastError函数可获得错误信息。
EnumProcesses
获取系统所有进程id
1 2 3 4 5 6 7 8 9 10 11 12 头文件 Psapi.h BOOL WINAPI EnumProcesses(_Out_ DWORD * pProcessIds,_In_ DWORD CB,_Out_ DWORD * pBytesReturned); pProcessIds 接收进程id的数组 cb 数组的大小 pBytesReturned 数组返回的字节数 返回值 成功返回非零数,失败返回零,可以使用函数 GetLastError获取错误信息.
GetCommandLine
获取命令行
1 LPTSTR GetCommandLine(void);
获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程 建立一个快照。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等 DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0 ); dwFlags 指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个。 TH32CS_INHERIT(0x80000000) - 声明快照句柄是可继承的。 TH32CS_SNAPALL - 在快照中包含系统中所有的进程和线程。 TH32CS_SNAPHEAPLIST(0x00000001) - 在快照中包含在th32ProcessID中指定的进程的所有的堆。 TH32CS_SNAPMODULE(0x00000008) - 在快照中包含在th32ProcessID中指定的进程的所有的模块。 TH32CS_SNAPPROCESS(0x00000002) - 在快照中包含系统中所有的进程。 TH32CS_SNAPTHREAD(0x00000004) - 在快照中包含系统中所有的线程。 H32CS_SNAPALL = (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE) 返回值 调用成功,返回快照的句柄,调用失败,返回INVALID_HANDLE_VALUE 。
1 2 3 4 5 BOOL WINAPI Process32First( HANDLE hSnapshot,//_in LPPROCESSENTRY32 lppe//_out ); process32First是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用process32First函数来获得第一个进程的句柄。
1 2 3 4 5 6 7 8 9 BOOLWINAPIProcess32Next( __inHANDLEhSnapshot, __outLPPROCESSENTRY32lppe ); hSnapshot 从CreateToolhelp32Snapshot 返回的句柄。 lppe 指向PROCESSENTRY32结构的指针。
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 #include <iostream> #include <windows.h> #include <tlhelp32.h> using namespace std ;int main () { HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL | TH32CS_SNAPMODULE | TH32CS_SNAPHEAPLIST, 0 ); if (h == INVALID_HANDLE_VALUE) { cout << "CreateToolhelp32Snapshot Error: " << GetLastError() << endl ; return -1 ; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof pe32; BOOL bMore = ::Process32First(h, &pe32); int i = 0 ; while (bMore) { printf ("进程名称:%ls--" , pe32.szExeFile); printf ("进程ID:%u\t" , pe32.th32ProcessID); bMore = ::Process32Next(h, &pe32); if (++i % 2 == 0 ) { cout << endl << endl ; } } cout << endl << endl << i; CloseHandle(h); }