滴水初级win32学习笔记
0x00前言
很惭愧,在刚放寒假立下的FLAG现在才来实践,不过好在假期还没结束还有时间,由于在学校的时候有接触过一点所以就直接从第5节开始了,b站链接在这里,本文的目的也是为了自己更好的学习,所以不会写的很详细只会写一些自己认为比较有用且需要重点记忆的内容,,学长推荐的教材是《Windows程序设计(第5版 珍藏版)》,有机会我希望能够仔细拜读一下。我也是个初学者,如有任何错误,敬请各位读者斧正。
0x01进程与进程创建(5-6)
1,进程与线程关系:首先举个例子,进程就可以比喻成房屋,线程可以比喻成房屋里的人,人可以做到我们想要的事情,房屋(进程)可以给人(线程)提供场所(空间)和装备(资源),想要做成一件事情就一定要有人去完成(一个进程里至少要有一个线程),当然要做大事一个人力量是不够的,这个时候就可以叫兄弟帮忙了(多线程),在一个房屋内一起写项目(多个线程共用进程资源与空间)。这个例子简单的讲述了进程与线程的关系,虽然有可能有问题的地方,不过还是比较通俗易懂了。
2,进程内存空间地址分配:首先看张图从这张图中可以很直观的看出一个进程在内存空间里面有着4GB的虚拟内存空间,不过这4GB并不全部都是拿来放本进程可以拿来使用的,我们可以分为高2G和低2G,高2G是内核区域,内核区域所有进程是一模一样的。所以在不同进程之间主要有区别的是在用户模式区。! 这里 !有着比较详细的资料后期必仔细拜读。
3,(1)进程的创建:任何的进程都是由进程创建的,我们在桌面上双击程序所创建的进程都是由(explorer.exe)这个进程创建的,而explorer.exe这个进程是由系统创建的。 (2)创建过程:我们还是直接看图来的更直观一点主要是懒。 在第五步映射DLL之前有一个傀儡进程的操作最好去原视频看看(32:02),具体实现原理等后期学到在补坑。
4,CreateProcess()函数:这个函数和LoadLibrary()一样是有区分不同的编码方式。(其实大多数的win32API都有这个设定)
#ifdef UNICODE
#define CreateProcess CreateProcessW
#else
#define CreateProcess CreateProcessA
#endif // !UNICODE
以下是这个函数的定义
BOOL
WINAPI
CreateProcessW(
_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
不要看上面定义的参数那么多,目前我们只需要了解前俩个和后俩个参数,下面让我们慢慢学习。先说前俩个参数,他们要接受的都是TCHAR类型的数组,唯一的区别是lpApplicationName要的是不可变(const)参数,而lpCommandLine需要的是可变参数。对于后俩个参数,lpStartupInfo所要接受的是一个数组代码如下
typedef struct _STARTUPINFOW {
DWORD cb;
LPWSTR lpReserved;
LPWSTR lpDesktop;
LPWSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOW, *LPSTARTUPINFOW;
````
这个数组主要的信息是父进程给所产生的新进程写入的信息,不过修改次数组内容并不会对新进程产生影响。最后一个参数也是传入一个数组代码如下
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, PPROCESS_INFORMATION, LPPROCESS_INFORMATION;
这个数组里是线程和进程的ID及句柄,具体什么是句柄下次再说。
**最后有个反调试的操作**,就是对lpStartupInfo数组内容进行判别,直接双击打开和通过OD等调试器打开里面初始化的参数内容是不一样的,就此可以进行简单的反调试。
## 0x02句柄表(7-8)
1,内核对象:首先我们来了解一下什么是内核对象,像进程、线程、文件、互斥体、事件等在内核都有一个对应的结构体,这些结构体由内核负责管理。我们管这样的对象叫做内核对象。我们可以看CreateXXX函数的定义,其中有类型为**LPSECURITY_ATTRIBUTES**的参数,则这个函数创建的对象为内核对象。
2,句柄表:***只有每个进程有一个句柄表***。设置句柄表是为了避免使用地址来访问内核对象,因为涉及R0的东西一旦出现地址出差就会没有访问权限从而导致电脑蓝屏,句柄就很好的隔离开内核层与用户层。句柄表通过进程的内核结构体中的一个指针指向。
3,句柄表的继承:用我的话来说就是双重保险,第一层是创建内核对象时在LPSECURITY_ATTRIBUTES里有一个参数可以选择**本内核对象**是否允许继承。第二层是在创建子进程时使用CreateProcess()函数时第五个参数也可以选择是否继承**句柄表**。
4,句柄值:因为我们之前称的句柄表都是进程中私有的,仅在本进程可以使用,这样就照成了很多麻烦,所以就出现了全局句柄表的值(句柄值)也就是我们常说的PID。通过PID就可以实现在不同进程中互相调用的效果。
5,工作路径和模块路径:模块路径就是我们传统意义上EXE所在的位置的整个路径,而工作路径就是由父进程所编写的路径。工作路径的作用就是在工作路径内的文件想要访问或打开,可以不需要写完整路径,只要写文件名和拓展名即可。
## 0x03 线程(9-12)
1,创建线程:使用CreateThread()函数创建线程
2,线程控制函数:Sleep()程序执行到此次暂停多少秒(让自己停下来);SuspendThread()传入线程句柄可以使该线程挂起暂停(让别人停下来)与之对应的就是ResumeThread()恢复线程状态使之继续进行。
3,线程状态等待:WaitForSingleObject()等待单个线程结束(INFINITE)或者在某一个时间后接着进行【WaitForMultipleObjects()和这个类似就是可以等待多个线程】;GetExitCodeThread() 线程一段代码结束后会有一个返回值可以用这个函数获取,可以根据自己的需求进行修改返回值。
4,多线程操作中不同线程间寄存器数值的存放函数 使用SetThreadContext()修改 和 GetThreadContext()进行读取。[Dll注入新姿势:SetThreadContext注入](https://www.anquanke.com/post/id/86786)
5,多线程安全问题:前提条件:1.有全局变量,2.对全局变量进行读操作。
解决办法:临界区(对临界资源(全局变量)进行访问的代码段)通过线程锁 EnterCriticalSection(**令牌地址**) 和 LeaveCriticalSection(**令牌地址**)这俩个函数之间的代码就是临界区,令牌记得需要用InitializeCriticalSection(**令牌地址**)初始化。
6,互斥体:内核级的令牌,在不同进程的线程中起作用;互斥体起作用的条件(任意一个即可):有信号或者线程的拥有者。可以通过创建互斥体函数(CreateMutex())的返回值用来放置多开程序。
## 0x04 事件(13)
1,通知类型:在使用CreateEvent()的对第二个参数进行设定可以选择是用通知模式还是互斥模式(此次互斥模式和上文的互斥体类似,简单的来说就是*有我没你,有你没我*)。通知模式下WaitForSingleObject()之类线程等待函数不会改变事件状态(信号有无),简单的说就是可以通知多个线程。
2,线程同步:同步的前提是互斥,互斥是无序的(某个线程连着好几次使用同一个临界资源)。互斥 + 有序 = 同步。方法(1)可以使用互斥体不过会浪费CPU时间片造成资源浪费。方法(2)使用俩个事件进行相互通讯,SetEvent(另外一个事件)挂起自己通知别人。
## 0x05 窗口的本质(14)
1,HWND与HANDLE的区别:HWND是HANDLE的一种,但是HANDLE并不是HWND,HANDLE是操作系统一系列内核对象的句柄,HWND仅是窗口对象的句柄。
2,绘制窗口的步骤:(1)设置设备对象(即在哪画,NULL默认是桌面)。
(2)获取设备对象上下文(HDC),因为我们都是先画到设备对象的内存里所以需要中国设备对象上下文。
(3)创建图形对象,可以使用GetStockObject()函数获取系统默认图形对象。
(4)关联上下文和图形对象SelectObject()。
(5)开始画(使用LineTo(),Rectangle()等函数,起始点默认为(0,0),可以用MoveToEx()修改画线初始点)。
(6)释放资源(一般Create类函数用DeleteObject()类函数,Get类函数用ReleaseDC()类函数)。
## 0x05 消息机制(15)
1,每个线程只有一个消息队列,消息队列是存放在R0层线程对象的结构体里的。
2,鼠标的消息如何到达消息队列:首先在R0层每个窗口都有一个窗口对象,这个窗口对象(WinObject)里面记录了窗口的起始位置,大小,层级关系,所在线程等等;其次鼠标点击后会把鼠标所在位置,左键还是右键等打包成一个结构体(消息);最后通过位置找到消息所属的窗口对象指向的线程加入到消息队列里。
3,每个线程可以有多个窗口,一个窗口只能属于一个线程。
## 0x06 结束