问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
线程同步的八种利用方法小结
渗透测试
分享者才是学习中最大的受益者!!
线程同步 ==== 前言 -- 线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态 简单来说,就是线程之间的协调,线程之间的协同、协助、互相配合完成工作。 一、等待函数 ------ ### 解决的问题 问题一:不同线程函数的执行必须有先后顺序 问题二:同一线程函数中一段代码必须作为一个单元执行 ### 简单的案例 ![image-20211125203046330](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-ad6053ba1633b430e89e7dd2d2c6ae57e2ed6419.png) ### 等待函数 ```php DWORD WaitForSingleObject( HANDLE hHandle, //对象句柄 DWORD dwMilliseconds //等待时间,毫秒为单位,INFINITE一直等待 ); ``` **hHandle**:Event、Job、Memory resource notification、Mutex、Process、 Semaphore、Thread、Waitable timer等 返回值的结果: **WAIT\_ABANDONED 0x00000080**:当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放内核对象会返回此返回值。 **WAIT\_OBJECT\_0 0x00000000** :指定的对象发出有信号状态 **WAIT\_TIMEOUT 0x00000102**:等待超时 **WAIT\_FAILED 0xFFFFFFFF** :出现错误,可通过GetLastError得到错误代码 等待多个对象 ```php DWORD WaitForMultipleObjects( DWORD dwCount, //等待线程数量(最多MAXIMUM_WAIT_OBJECTS个,windows定义为64) CONST HANDLE* phObjects, //线程句柄数组指针 BOOL fWaitAll, //是否等待全部 DWORD dwMilliseconds //等待时间INFINITE ); ``` 当参数fWaitAll为TRUE时,若所有线程均变为已通知状态则函数返回值为`WAIT_OBJECT_0` 当参数fWaitAll为FALSE时,返回的值为线程内核对象数组的索引值 基本用法 ```php //定义两个全局的句柄,当然也可以定义成数组 HANDLE hThread[2]; int main(void) { //创建两个线程 hThread[0] = CreateThread(NULL, NULL, GetResult, NULL, 0, NULL); hThread[1] = CreateThread(NULL, NULL, GetNum, NULL, 0, NULL); //等待这两个线程执行完毕 WaitForMultipleObjects(2, hThread, TRUE, INFINITE); //关闭句柄 CloseHandle(hThread1); CloseHandle(hThread2); return 0; } ``` 二、原子访问-原子操作 ----------- ### 前言 避免多线程计算读写冲突 ### InterLocked函数-递增 ```php LONG InterlockedIncrement(LONG volatile* Addend) ``` ```php LONGLONG InterlockedIncrement64(LONGLONG volatile* Addend) ``` 参数 Addend:原子递增变量的指针。 返回值:变量递增后的值 ### InterLocked函数-递减 ```php LONG InterlockedDecrement(LONG volatile* Addend) ``` ```php LONGLONG InterlockedDecrement64(LONGLONG volatile* Addend) ``` 参数: Addend:原子递减变量的指针。 返回值:变量递减后的值 ### InterLocked函数-加/减法 ```php LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value) ``` ```php LONGLONG InterlockedExchangeAdd64( LONGLONG volatile* Addend, LONGLONG Value ) ``` 参数: Addend:原子加法变量的指针 Value:变量 返回值:变量变化后的值 ### InterLocked函数-变量赋值 ```php LONG InterlockedExchange(LONG volatile* Addend, LONG Value) ``` ```php LONGLONG InterlockedExchange64( LONGLONG volatile* Addend, LONGLONG Value ) ``` 参数: Addend:原子操作变量的指针。 Value:给Addend赋值的值。 返回值:变量**变化前**的值 ### InterLocked函数-指针赋值 ```php PVOID InterlockedExchangePointer(PVOID volatile *Target, PVOID Value); ``` 参数: Target:二重指针,需要替换的二重指针的值。 Value:用该地址值替换Target中的地址。 返回值:Target在被替换前的地址值 ### InterLocked函数-比较赋值 ```php LONG InterlockedCompareExchange( LONG* Destination, LONG ExChange, LONG Comperand ); ``` 参数: Destination:需要替换的变量地址。 ExChange:给Destination赋值的值。 Comperand:需要比较的值,如果与Destination相同,则替换。 返回值:**变化前**的值 ```php LONGLONG InterlockedCompareExchange64 ( LONGLONG volatile *Destination, LONGLONG Exchange, LONGLONG Comparand ); ``` 参数: Destination:需要替换的变量地址。 ExChange:给Destination赋值的值。 Comperand:需要比较的值,如果与Destination相同,则替换。 返回值:**变化前**的值 ### InterLocked函数-比较指针赋值 ```php PVOID InterlockedCompareExchangePointer( PVOID *Destination , PVOID Exchange , PVOID Comparand ); ``` 参数: Destination:需要替换的指针的地址。 ExChange:给Destination赋值的值。 Comperand:需要比较的值,如果与Destination相同,则替换。 返回值:**变化前**的值 ### 位运算 #### 与运算 ```php SHORT InterlockedAnd16(SHORT volatile *Destination, SHORT Value); LONG InterlockedAnd(LONG volatile *Destination, LONG Value); LONGLONG InterlockedAnd64(LONGLONG volatile *Destination, LONGLONG Value); ``` 参数: Destination:与Value做按位与运算,并保存结果。 Value:与Destination做按位与运算的值。 返回值:**变化前**的值。 #### 或运算 ```php SHORT InterlockedOr16(SHORT volatile *Destination, SHORT Value); LONG InterlockedOr(LONG volatile *Destination, LONG Value); LONGLONG InterlockedOr64(LONGLONG volatile *Destination, LONGLONG Value); ``` 参数: Destination:与Value做按位或运算,并保存结果。 Value:与Destination做按位或运算的值。 返回值:**变化前**的值 #### 异或运算 ```php SHORT InterlockedXor16(SHORT volatile *Destination, SHORT Value); LONG InterlockedXor(LONG volatile *Destination, LONG Value); LONGLONG InterlockedXor64(LONGLONG volatile *Destination, LONGLONG Value); ``` 参数: Destination:与Value做按位异或运算,并保存结果。 Value:与Destination做按位异或运算的值。 返回值:**变化前**的值 三、关键段-临界区 --------- ```php 定义关键段:CRITICAL_SECTION cs; 初始化关键段: InitializeCriticalSection(&cs); | InitializeCriticalSectionEx; | InitializeCriticalSectionAndSpinCount 进入关键段:EnterCriticalSection(&cs); 离开关键段:LeaveCriticalSection(&cs); 销毁关键段:DeleteCriticalSection(&cs); ``` ```php 初始化关键段: void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection //关键段地址 ); 销毁关键段: void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection //关键段地址 ); ``` ```php 进入关键段: void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); 离开关键段: void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); ``` ### 简单的例子 ```php CRITICAL_SECTION g_cs; //定义 int main(void) { InitializeCriticalSection(&g_cs); //初始化 HANDLE hThread[2]; hThread[0] = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ThreadProc, NULL, 0, NULL); hThread[1] = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ThreadProc, NULL, 0, NULL); Sleep(1000); g_bFlag = FALSE; WaitForMultipleObjects(2, hThread, TRUE, INFINITE); DeleteCriticalSection(&g_cs); //销毁 CloseHandle(hThread[0]); CloseHandle(hThread[1]); printf("\ng_iCount1 = %d\n", g_iCount1); printf("\ng_iCount2 = %d\n", g_iCount2); return 0; } DWORD WINAPI ThreadProc(LPVOID *lparam) { EnterCriticalSection(&g_cs); //进入关键段 while (g_bFlag) { g_iCount1++; g_iCount2++; } LeaveCriticalSection(&g_cs); //离开关键段 return 0; } ``` ### 工作原理 等待状态,消耗系统资源 ![image-20211126185744841](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-87acbf835c6ad7a29bb86d0ad9cbbdb2040d144c.png) ### 优化 在内核模式进行检测N次:关键段是否可用,而不会直接退出内核模式 当然,超过检测次数,还是要进入等待状态的 单核CPU无效 ![image-20211126190002535](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-394d3bac00ed2614c3f5a1664f02875bc52fcd18.png) 在用户模式进行检测关键段 与CPU内核数量无关(推荐使用) ![image-20211126190229956](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-4219c04ec4b84ce47c59ad495cef9789ff3977dc.png) 初始化关键段 ```php BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, //关键段地址 DWORD dwSpinCount //检测次数 ); ``` 初始化关键段 ```php BOOL WINAPI InitializeCriticalSectionEx( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags //CRITICAL_SECTION_NO_DEBUG_INFO 不会显示debug信息 ); ``` 重新设置检测次数,返回值为设置前的检测次数 ```php DWORD SetCriticalSectionSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount ); ``` 尝试进入关键段,关键段是否可用判断 ```php BOOL TryEnterCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection ); ``` ### 可能的错误 关键段初始化: InitializeCriticalSection(&cs); 返回值void 1.内存不足时可能失败 2.线程首次争抢时产生事件内核对象可能失败 ### 处理 关键段初始化: ```php BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount ); ``` 返回值可以判断关键段初始化是否成功 dwSpinCount:有效范围 0x00000000~0x00FFFFFF dwSpinCount:初始化即生成事件对象,最高位设置为1 0x80000000~0x80FFFFFF 四、读写锁SRWLOCK ------------ ### 前言 独占锁和共享锁中,**后者速度更快!!** ```php 定义读写锁:SRWLOCK sl; 初始化读写锁:InitializeSRWLock(&sl); //比如写操作 获取独占读写锁:AcquireSRWLockExclusive(&sl); 释放独占读写锁:ReleaseSRWLockExclusive(&sl); 不存在销毁读写锁的API //比如读操作 获取共享读写锁:AcquireSRWLockShared(&sl); 释放共享读写锁:ReleaseSRWLockShared(&sl); ``` ### 初始化读写锁 ```php VOID InitializeSRWLock( PSRWLOCK SRWLock ); ``` ### 进入独占锁 ```php VOID AcquireSRWLockExclusive( PSRWLOCK SRWLock ); ``` ### 释放独占锁 ```php VOID ReleaseSRWLockExclusive( PSRWLOCK SRWLock ); ``` ### 进入共享锁 ```php VOID AcquireSRWLockShared( PSRWLOCK SRWLock ); ``` ### 释放共享锁 ```php VOID ReleaseSRWLockShared( PSRWLOCK SRWLock ); ``` ### 简单的例子 ```php SRWLOCK g_sl; int main(void) { InitializeSRWLock(&g_sl); HANDLE hThread[3]; hThread[0] = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ThreadWrite, NULL, 0, NULL); hThread[1] = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ThreadWrite2, NULL, 0, NULL); hThread[2] = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ThreadRead, NULL, 0, NULL); Sleep(1000); g_bFlag = FALSE; WaitForMultipleObjects(3, hThread, TRUE, INFINITE); CloseHandle(hThread[0]); CloseHandle(hThread[1]); CloseHandle(hThread[2]); printf("\n--g_iCount1 = %d\n", g_iCount1); printf("\n--g_iCount2 = %d\n", g_iCount2); return 0; } DWORD WINAPI ThreadWrite(LPVOID *lparam) { while (g_bFlag) { AcquireSRWLockExclusive(&g_sl); g_iCount1++; g_iCount2++; ReleaseSRWLockExclusive(&g_sl); } return 0; } DWORD WINAPI ThreadWrite2(LPVOID *lparam) { while (g_bFlag) { AcquireSRWLockExclusive(&g_sl); g_iCount1++; g_iCount2++; ReleaseSRWLockExclusive(&g_sl); } return 0; } DWORD WINAPI ThreadRead(LPVOID *lparam) { while (g_bFlag) { AcquireSRWLockShared(&g_sl); printf("\ng_iCount1 = %d\n", g_iCount1); printf("\ng_iCount2 = %d\n", g_iCount2); ReleaseSRWLockShared(&g_sl); } return 0; } ``` 五、事件内核对象Event ============= ### 创建事件内核对象 内核对象可以共享 ```php HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性 BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName //当前内核对象的名称 ); ``` 重要参数: bManualReset:手动重置(TRUE),自动重置(FALSE) bInitialState:初始状态(TRUE:已触发,FALSE:未触发) 微软提出的扩展函数 ```php HANDLE CreateEventEx( LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性 LPCTSTR lpName, //当前内核对象的名称 DWORD dwFlags, DWORD dwDesiredAccess //权限访问 ); ``` 参数: dwFlags两个参数 对应bManualReset,bInitialState dwFlags:CREATE\_EVENT\_MANUAL\_RESET(1) ,CREATE\_EVENT\_INITIAL\_SET(2) bManualReset , bInitialState ### 打开事件内核对象 ```php HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName ); ``` ### 设置事件内核对象,设置状态为已触发 ```php BOOL SetEvent( HANDLE hEvent ); ``` ### 设置事件内核对象,设置状态为未触发 **当我们把Event创建为自动重置时,Event执行完成后,系统会自动把Event状态重置为未触发状态** ```php BOOL ResetEvent( HANDLE hEvent ); ``` ### 简单的例子 ```php HANDLE g_hEvent; int main(void) { g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //自动重置,初态为未触发 SetEvent(g_hEvent); //触发事件对象 HANDLE hThread; hThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)BaoShu1, NULL, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(g_hEvent); CloseHandle(hThread); return 0; } //线程运行完毕后自动设置为未触发 DWORD WINAPI BaoShu1(LPVOID *lparam) { WaitForSingleObject(g_hEvent, INFINITE); for (int i = 0; i < 10; i++) { printf("1\n"); } return 0; } ``` 六、可等待计时器WaitableTimer --------------------- ### 前言 ```php 创建可等待计时器:CreateWaitableTimer 创建可等待计时器:CreateWaitableTimerEx 打开可等待计时器:OpenWaitableTimer 设置可等待计时器:SetWaitableTimer 取消可等待计时器:CancelWaitableTimer ``` ### 创建可等待计时器 ```php HANDLE CreateWaitableTimer( LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCTSTR lpTimerName ); ``` 参数: lpTimerAttributes: 安全属性设置 bManualReset: TRUE:手动重置 lpTimerName: 计时器名称,为了跨线程,跨进程 创建可等待计时器: ```php HANDLE CreateWaitableTimerEx( LPSECURITY_ATTRIBUTES lpTimerAttributes, //安全属性设置 LPCTSTR lpTimerName, //计时器名称 DWORD dwFlags, DWORD dwDesiredAccess //访问权限 ); ``` 参数: dwFlags: ```php CREATE_WAITABLE_TIMER_MANUAL_RESET(0x00000001)手动重置 0x00000000 自动重置 ``` dwDesiredAccess: ![image-20211127174632825](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-49da1ee89d381594266868198f7d4d8222cc8882.png) ![image-20211127174705478](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-2183fa248de860a973d9305e1d3b5c4c7c30a9fb.png) ### 打开可等待计时器 ```php HANDLE OpenWaitableTimer( DWORD dwDesiredAccess, //访问权限 BOOL bInheritHandle, //是否可以继承句柄 LPCTSTR lpTimerName //计时器名称 ); ``` ### 设置可等待计时器 ```php BOOL SetWaitableTimer( HANDLE hTimer, //1.创建时的句柄 2.打开时的句柄 const LARGE_INTEGER *pDueTime, //首次触发 LONG lPeriod, //触发频率间隔 PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, BOOL fResume //是否提醒 ); ``` LARGE\_INTEGER说明: ```php typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER, *PLARGE_INTEGER; ``` pDueTime:首次触发时间 指定首次触发时间点: ```php struct { DWORD LowPart; LONG HighPart; }; ``` 指定启动后触发间隔:LONGLONG QuadPart; 单位:100ns 1秒 = 1000毫秒 1毫秒 = 1000微秒 1微秒 = 1000纳秒(ns) 1秒 = 10000000 \* 100ns ```php lPeriod:触发频率间隔,单位:毫秒,0:不频率性触发 ``` ```php pfnCompletionRoutine:APC函数 传入的是一个函数指针 APC (Asynchronous procedure call )异步过程调用函数 ``` ```php lpArgToCompletionRoutine:APC函数参数 ``` ### 取消可等待计时器 ```php BOOL CancelWaitableTimer( HANDLE hTimer ); ``` ### 简单例子 ```php int main() { HANDLE hTimer = NULL; LARGE_INTEGER liDueTime; liDueTime.QuadPart = -50000000LL; //设置时间间隔 hTimer = CreateWaitableTimer(NULL, TRUE, NULL); //创建可等待计时器,手动重置 if (NULL == hTimer) { printf("CreateWaitableTimer failed (%d)\n", GetLastError()); return 1; } printf("Waiting for 5 seconds...\n"); if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)) //设置可等待计时器 { printf("SetWaitableTimer failed (%d)\n", GetLastError()); return 2; } if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0) printf("WaitForSingleObject failed (%d)\n", GetLastError()); else printf("Timer was signaled.\n"); return 0; } ``` ### 与用户计时器的差别 ```php 用户计时器:需要在应用程序中使用大量的用户界面基础设施,消耗资源更多。 WM_TIMER优先级最低,只有当线程对象没有任何其它消息的时候才会被处理。 ``` ```php 可等待计时器:内核对象,可以多线程间共享,具备安全性。 如果计时器被触发且线程正在等待,那么系统将唤醒线程。 ``` 七、信号量semaphore -------------- ### 前言 ```php 创建信号量:CreateSemaphore 创建信号量:CreateSemaphoreEx 打开信号量:OpenSemaphore 释放信号量:ReleaseSemaphore ``` ### 创建信号量 ```php HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName ); ``` 参数: lpTimerAttributes: 安全属性设置 lInitialCount: 初始信号量资源数 lMaximumCount: 最大信号量资源数 lpName: 信号量名称 lInitialCount(初始信号量资源数 ) <= lMaximumCount(最大信号量资源数 ) 创建信号量 ```php HANDLE CreateSemaphoreEx( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //安全属性 LONG lInitialCount, //初始信号量资源数 LONG lMaximumCount, //最大信号量资源数 LPCTSTR lpName, //信号量名称 DWORD dwFlags, //保留参数-0 DWORD dwDesiredAccess //访问权限 ); ``` 参数: dwDesiredAccess ![image-20211128123625542](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-81e81f2d9a4359173733ca6b829522cd05808a80.png) ![image-20211128130023065](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-200860a44d008102e5ed0ffabfc27cdcae7ec981.png) ### 打开信号量: ```php HANDLE OpenSemaphore( DWORD dwDesiredAccess, //访问权限 BOOL bInheritHandle, //是否可以继承 LPCTSTR lpName //信号量名称 ); ``` ### 释放信号量 用来分配资源 释放信号量是累加的,是一个增量 ```php BOOL ReleaseSemaphore( HANDLE hSemaphore, //创建/打开信号量时的返回值 LONG lReleaseCount, //释放信号量的大小 LPLONG lpPreviousCount //可用信号量之前的大小 ); ``` ### 原理 ![image-20211128130855233](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-76b9acfdf906a45dc06d7ee0ae32b4264c7a0909.png) 八、互斥量mutex/互斥体/互斥锁 ------------------ ### 前言 ```php 创建互斥量:CreateMutex 创建互斥量:CreateMutexEx 打开互斥量:OpenMutex 释放互斥量:ReleaseMutex ``` ### 创建互斥量 ```php HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName ); ``` 参数: lpTimerAttributes: 安全属性设置 bInitialOwner: 设置互斥量初始归属,TRUE:属于当前线程;FALSE:无归属,为触发状态 lpName: 互斥量名称 创建互斥量 ```php HANDLE CreateMutexEx( LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性设置 LPCTSTR lpName, //互斥量名称 DWORD dwFlags, //设置互斥量初始归属 DWORD dwDesiredAccess //访问权限 ); ``` 参数: dwFlags: CREATE\_MUTEX\_INITIAL\_OWNER(0x00000001) 当前线程为所有者 0x00000000 当前无所有者 dwDesiredAccess: ![image-20211128142504415](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-dff358a50c05d39949612a2fb8d6d6947dee584a.png) ![image-20211128142517138](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-f81a1c4713c0dd44fa5d73447393bdc5adb34a39.png) ### 打开互斥量 ```php HANDLE OpenMutex( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName ); ``` ### 释放互斥量 返回值是一个BOOL 类型 所以,可能会失败 ```php BOOL ReleaseMutex( HANDLE hMutex ); ``` ### 简单例子 ```php HANDLE g_hMutex; int main(void) { g_hMutex = CreateMutex(NULL,FALSE, NULL); //FALSE:互斥量可触发,并且没有线程占用 HANDLE hThread; hThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)BaoShu, NULL, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(g_hMutex); CloseHandle(hThread); return 0; } DWORD WINAPI BaoShu(LPVOID *lparam) { //等待互斥量资源 WaitForSingleObject(g_hMutex, INFINITE); for (int i = 0; i < 10; i++) { printf("1\n"); } //释放互斥量资源 ReleaseMutex(g_hMutex); return 0; } ``` ### 原理 只有互斥量mutex和线程ID 挂钩 ![image-20211128204607840](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-e74e4a6048610aff1fa523bcbf770b2795436c00.png) 比如 ![image-20211128204906710](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-226ae8246dd0c250b72b1727dcf7d6ece3b9ffc5.png) 当占用互斥量的线程意外死亡? 操作系统本身是可以记录到mutex被哪一个线程所占用 如果线程意外死亡,系统就知道这个线程已经消失了 mutex就变成被遗弃的状态,再也没有办法释放它了 操作系统对象会将mutex的 引用计数置为0 当前线程ID置为0 使得mutex的资源变成一个可以利用的资源 其他系统就可以利用 mutex了,执行自己线程的逻辑 线程同步总结 ------ ### 八大类 ![image-20211128225007994](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-4ba48eea086e37ed784ddb9b631044b8aa80c33b.png) ### 简单分类 分为用户模式 / 内核模式 尽量使用用户模式 内核模式的主要作用是跨进程 ![image-20211128225107993](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-94c64bdba088911c7a2067c4e3978dcaebd05a17.png) ### 性能对比 ![image-20211128225306807](https://shs3.b.qianxin.com/attack_forum/2021/12/attach-ddab475f148186514fd9809c8885e76c4c5c46db.png) 原子访问:使用起来简单方便,维护性好 ### 其他线程同步函数 #### 等待界面程序 但是它对一些程序是没有用的 ```php DWORD WaitForInputIdle( HANDLE hProcess, DWORD dwMilliseconds ); ``` #### 等待消息处理 ```php DWORD MsgWaitForMultipleObjects( DWORD nCount, const HANDLE *pHandles, BOOL bWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask ); ``` ```php DWORD MsgWaitForMultipleObjectsEx( DWORD nCount, const HANDLE *pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags ); ``` #### 边触发边等待-SignalObjectAndWait ```php DWORD SignalObjectAndWait( HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL bAlertable ); ``` 参数: hObjectToSignal: 将该对象置为触发状态,必须是互斥量、信号量或者事件 hObjectToWaitOn: 等待该对象互斥量、信号量、事件、计时器、进程、线程、 作业、控制台输入及变更通知。 bAlertable: 表示当线程处于等待状态的时候,是否能够对添加到队列中的异步过程调用进行处理 ##### 场景 既需要将一个线程置为触发状态,又需要等另一个线程 向一个对象发送信号 等待 两者结合 就是两个线程的 原子操作 ##### 前言 ```php DWORD SignalObjectAndWait( HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL bAlertable ); ``` 参数: hObjectToSignal: 将该对象置为触发状态,必须是互斥量、信号量或者事件 hObjectToWaitOn: 等待该对象互斥量、信号量、事件、计时器、进程、线程、 作业、控制台输入及变更通知。 bAlertable: 表示当线程处于等待状态的时候,是否能够对添加到队列中的异 步过程调用进行处理。 ##### 简单例子 ```php #include <Windows.h> #include <process.h> #include <stdio.h.> #include <stdlib.h> DWORD WINAPI sayhello(LPVOID *lparam); HANDLE g_hEvent; int main(void) { g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //创建一个事件内核对象 HANDLE hThread; hThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)sayhello, NULL, 0, NULL); //之前的操作 //SetEvent(g_hEvent); //WaitForSingleObject(hThread, INFINITE); SignalObjectAndWait(g_hEvent, hThread, INFINITE, TRUE); for (int i = 0; i < 100; i++) { printf("world\n"); } CloseHandle(hThread); system("pause"); return 0; } DWORD WINAPI sayhello(LPVOID *lparam) { WaitForSingleObject(g_hEvent, INFINITE); for (int i = 0; i < 100; i++) { printf("hello\n"); } return 0; } ``` ok 就到这里 希望可以帮到各位师傅!
发表于 2021-12-02 09:35:57
阅读 ( 5012 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
略略略
36 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!