<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【技術分享】】深入理解win32(十一)

    VSole2022-03-30 08:55:40

    前言

    在上一節里面我們實現了進程的遍歷以及初步了解了線程,在這一節里面我們繼續來對線程控制來進行探究。

    線程控

    我們首先來看兩個api

    SuspendThread

    用來掛起線程,如果函數成功, 傳回線程目前的掛起次數。如果失敗, 則傳回0xFFFFFFFF

    case IDC_BUTTON2:                        {                        ::SuspendThread(hThread);        
    return TRUE;                }
    

    ResumeThread

    用來恢復線程,如果函數成功, 則傳回線程的前一個掛起次數。如果失敗, 則傳回0xFFFFFFFF。這個函數允許調用端指定一個線程睡眠(掛起)。直到又有人調用了ResumeThread(), 線程才會醒來。因此,睡眠中的線程不可能喚醒自己。

    case IDC_BUTTON3:                        {                        ::ResumeThread(hThread);        
    return TRUE;                }
    

    這里我們編寫一個win32程序并設置兩個按鈕,一個按鈕為掛起線程,另一個按鈕為恢復線程進行測試,實現代碼如下

    // thread CONTEXT.cpp : Defines the entry point for the application.//
    #include "stdafx.h"
    HWND hEdit;HANDLE hThread;
    DWORD WINAPI ThreadProc1(LPVOID lpParameter)                    {                        TCHAR szBuffer[10];                    DWORD dwIndex = 0;                    DWORD dwCount;                
    while(dwIndex<1000)                    {                GetWindowText(hEdit,szBuffer,10);                    sscanf( szBuffer, "%d", &dwCount );                    dwCount++;        
            Sleep(200);
            memset(szBuffer,0,10);                    sprintf(szBuffer,"%d",dwCount);                    SetWindowText(hEdit,szBuffer);                    dwIndex++;                }                
    return 0;                }                    
    BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)                    {                    BOOL bRet = FALSE;                
    switch(uMsg)                    {                case WM_CLOSE:                        {                        EndDialog(hDlg,0);        break;                }            case WM_INITDIALOG:                        {                        hEdit = GetDlgItem(hDlg,IDC_EDIT);                    SetWindowText(hEdit,"0");        
    break;                }            case WM_COMMAND:                
    switch (LOWORD (wParam))                    {            case IDC_BUTTON1:                        {        //創建線程                hThread = ::CreateThread(NULL, 0, ThreadProc1,     NULL, 0, NULL);
    return TRUE;                }        case IDC_BUTTON2:                        {        //掛起線程                ::SuspendThread(hThread);    
    return TRUE;                }case IDC_BUTTON3:                        {        //恢復線程                ::ResumeThread(hThread);        
    return TRUE;                }        }            break ;                }                
    return bRet;                }                    
    int APIENTRY WinMain(HINSTANCE hInstance,                     HINSTANCE hPrevInstance,                     LPSTR     lpCmdLine,int       nCmdShow){// TODO: Place code here.    DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);                
    return 0;}
    

    演示效果如下

    終止線程

    我們知道在線程結束的時候是有一個返回值的,在正常結束線程的情況下,返回值為0

    那在這里我們加一段代碼,這里為終止線程的第一種方法,使用到ExitThread這個api,這種方法終止線程的同時會清理堆棧

    ExitThread

    void ExitThread(  [in] DWORD dwExitCode);
    ::ExitThread(DWORD dwExitCode);
    

    這里相當于如果手動中止線程的話就會返回8

    同步調用&異步調用

    同步調用的通俗理解就是比如有三個程序要執行,必須第一個程序被觸發,執行結束了之后,才輪到其他程序執行

    異步調用則是所有程序的執行不需要同步,可以多個觸發,互相獨立的執行相應的指令

    在同步調用中關閉線程之后會得到操作系統的返回值,在往下執行代碼

    這里終止線程的第二種方法就是使用TerninateThread這個api,結構如下

    TerninateThread

    BOOL TerminateThread(  [in, out] HANDLE hThread,  [in]      DWORD  dwExitCode);
    

    在異步調用中如果光使用如下代碼,在得到關閉線程指令后不會等待線程關閉的消息返回,而是直接往下執行


    ::TerminateThread(hThread,2);
    

    所以這里就需要再加上一行等待的代碼


    ::WaitForSingleObject(hThread,INFINITE);
    

    這里使用的異步調用是不清理堆棧的

    CONTEXT結構

    每個線程在執行的時候,都會獨自占用一個CPU,當系統中的線程數量 > CPU的數量時,就會存在多個線程共用一個CPU的情況。但CPU每次只能運行一個線程,Windows每隔20毫秒會進行線程的切換,那比如線程A執行到地址:0x2345678 eax:1 ecx:2 edx:3 ebx:4…還有eflag標志寄存器中的值等等

    此時,線程執行時間到了,被切換到了線程B。當線程B的時間片也到了,再切換會線程A時,系統是如何知道該從哪個地址開始執行呢?被切換前用到的各種寄存器的值該如何恢復呢?

    這里在進行線程的切換的時候要對原線程中的寄存器的值進行保存,這時候就會用到CONTEXT這個結構體

    CONTEXT結構如下


    CONTEXT:
    該結構包含了特定處理器的寄存器數據。
    typedef struct _CONTEXT {
    //                            // The flags values within this flag control the contents of                            // a CONTEXT record.                            //                            // If the context record is used as an input parameter, then                            // for each portion of the context record controlled by a flag                            // whose value is set, it is assumed that that portion of the                            // context record contains valid context. If the context record                            // is being used to modify a threads context, then only that                            // portion of the threads context will be modified.                            //                            // If the context record is used as an IN OUT parameter to capture                            // the context of a thread, then only those portions of the thread's                            // context corresponding to set flags will be returned.                            //                            // The context record is never used as an OUT only parameter.                            //                            
        DWORD ContextFlags;                            
    //                            // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is                            // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT                            // included in CONTEXT_FULL.                            //                            
        DWORD   Dr0;                                DWORD   Dr1;                                DWORD   Dr2;                                DWORD   Dr3;                                DWORD   Dr6;                                DWORD   Dr7;                            
    //                            // This section is specified/returned if the                            // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.                            //                            
        FLOATING_SAVE_AREA FloatSave;                            
    //                            // This section is specified/returned if the                            // ContextFlags word contians the flag CONTEXT_SEGMENTS.                            //                            
        DWORD   SegGs;                                DWORD   SegFs;                                DWORD   SegEs;                                DWORD   SegDs;                            
    //                            // This section is specified/returned if the                            // ContextFlags word contians the flag CONTEXT_INTEGER.                            //                            
        DWORD   Edi;                                DWORD   Esi;                                DWORD   Ebx;                                DWORD   Edx;                                DWORD   Ecx;                                DWORD   Eax;                            
    //                            // This section is specified/returned if the                            // ContextFlags word contians the flag CONTEXT_CONTROL.                            //                            
        DWORD   Ebp;                                DWORD   Eip;                                DWORD   SegCs;              // MUST BE SANITIZED                                DWORD   EFlags;             // MUST BE SANITIZED                                DWORD   Esp;                                DWORD   SegSs;                            
    //                            // This section is specified/returned if the ContextFlags word                            // contains the flag CONTEXT_EXTENDED_REGISTERS.                            // The format and contexts are processor specific                            //                            
        BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];                            
    } CONTEXT;
    

    那么這里測試一下,代碼如下


    case IDC_BUTTON2:                        {                        ::SuspendThread(hThread);    
                    CONTEXT context;
    //設置要獲取的類型                    
                    context.ContextFlags = CONTEXT_CONTROL;                    
    //獲取                    
                    BOOL ok = ::GetThreadContext(hThread,&context);                    
    //設置                    
                    context.Eip = 0x401000;                    
                    SetThreadContext(hThread,&context);    
                    ::ResumeThread(hThread);
    return TRUE;                }
    

    演示效果如下,這里因為eip的值是隨便設置的所以掛掉了

    GetExitCodeThread

    用來判斷判斷線程是否結束,此函數調用成功返回TRUE,失敗返回FALSE,只表示這個函數是否調用成功而己。不能根據返回值來判斷一個線程是否結束,而要根據 lpExitCode的值來確定,lpExitCodeSTILL_ACTIVE時表示線程正在運行。若線程己經結束,則lpExitCode中存儲指定線程的返回值,結構如下

    BOOL GetExitCodeThread(        HANDLE hThread,        LPDWORD lpExitCode        );
    hThread[in] Handle to the thread. Windows NT/2000/XP: The handle must have THREAD_QUERY_INFORMATION access. For more information, see Thread Security and Access Rights.
    lpExitCode[out] Pointer to a variable to receive the thread termination status.
    

    其中返回值為一個指針,實現代碼如下

    case IDC_BUTTON5:            {                DWORD dwExitCode = 0;                ::GetExitCodeThread(hThread, &dwExitCode);
                    OutputDebugStringF("The process ID is:%d",dwExitCode);
    return TRUE;            }
    

    實現效果如下,這里的259就是16進制103,STILL_ACTIVE,證明線程還存在沒有終止

    首先點擊Start,然后GetID為259

    點擊Hang即掛起,GetID還是259證明還是線程還是處于STILL_ACTIVE狀態

    點擊Stop即終止線程,GetID為2證明線程已經終止

    這里再測試一下最終效果,代碼如下

    // thread CONTEXT.cpp : Defines the entry point for the application.//
    #include "stdafx.h"
    HWND hEdit;HANDLE hThread;
    DWORD WINAPI ThreadProc1(LPVOID lpParameter)                    {                        TCHAR szBuffer[10];                    DWORD dwIndex = 0;                    DWORD dwCount;                
    while(dwIndex<1000)                    {                GetWindowText(hEdit,szBuffer,10);                    sscanf( szBuffer, "%d", &dwCount );                    dwCount++;        
            Sleep(200);
            memset(szBuffer,0,10);                    sprintf(szBuffer,"%d",dwCount);                    SetWindowText(hEdit,szBuffer);                    dwIndex++;                }                
    return 0;                }                    
    BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)                    {                    BOOL bRet = FALSE;                
    switch(uMsg)                    {                case WM_CLOSE:                        {                        EndDialog(hDlg,0);        break;                }            case WM_INITDIALOG:                        {                        hEdit = GetDlgItem(hDlg,IDC_EDIT);                    SetWindowText(hEdit,"0");        
    break;                }            case WM_COMMAND:                
    switch (LOWORD (wParam))                    {            case IDC_BUTTON1:                        {        //創建線程                hThread = ::CreateThread(NULL, 0, ThreadProc1,     NULL, 0, NULL);
    return TRUE;                }        case IDC_BUTTON2:                        {        //掛起線程                ::SuspendThread(hThread);    
    /*CONTEXT context;
                    //設置要獲取的類型                    
                    context.ContextFlags = CONTEXT_CONTROL;                    
                    //獲取                    
                    BOOL ok = ::GetThreadContext(hThread,&context);                    
                    //設置                    
                    context.Eip = 0x401000;                    
                    SetThreadContext(hThread,&context);    
                    ::ResumeThread(hThread);*/
    return TRUE;                }case IDC_BUTTON3:                        {        //恢復線程                ::ResumeThread(hThread);        
    return TRUE;                }case IDC_BUTTON4:                        {                            ::TerminateThread(hThread,2);                                ::WaitForSingleObject(hThread,INFINITE);                
    return TRUE;                }case IDC_BUTTON5:            {                DWORD dwExitCode = 0;                ::GetExitCodeThread(hThread, &dwExitCode);
                    OutputDebugStringF("The process ID is:%d",dwExitCode);
    return TRUE;            }        }            break ;                }                
    return bRet;                }                    
    int APIENTRY WinMain(HINSTANCE hInstance,                     HINSTANCE hPrevInstance,                     LPSTR     lpCmdLine,int       nCmdShow){// TODO: Place code here.    DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);                
    return 0;}
    

    我們知道程序窗口是主線程,我們又自己創建了一個線程,觀察線程返回ID和線程數的變化

    首先我們啟動程序,因為主程序啟動了所以只有1個線程

    點擊start之后,因為使用CreateThread又創建了一個線程,所以為兩個線程

    然后點擊Hang即掛起,使用GetID查看為259,線程仍然存在,處于STILL_ACTIVE狀態,任務管理器里面也可以看到線程仍然為2

    點擊Stop,使用GetID查看為2,證明線程已經終止,任務管理器里面的線程也變為了1

    dword線程
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    全局鉤子注入在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    Dll注入
    2021-11-08 14:57:41
    最近太忙啦XDM,又在做一些列的分析復現工作量有點大,更新要慢一點了。一致,也不會覆蓋其他的進程信息。
    前言在PE文件中,存在iat導入表,記錄了PE文件使用的API以及相關的dll模塊。可以看到使用了MessageBox這個API殺軟會對導入表進行查殺,如果發現存在惡意的API,比如VirtualAlloc,CreateThread等,就會認為文件是一個惡意文件。自定義API函數FARPROC GetProcAddress;定義:typedef int ();HMODULE LoadLibraryA; // 成功返回句柄 失敗返回NULL. 這里GetModuleHandle和LoadLibrary作用是一樣的,獲取dll文件。HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );printf; pMessageBox MyMessageBox = GetProcAddress; MyMessageBox; return 0;}. 程序可以正常運行:查看其導入表:User32.dll和MessageBox都不存在。實戰測試用創建進程的方式加載shellcode。
    進程和線程
    2021-09-26 16:32:12
    看雪論壇作者ID:L0x1c
    APC機制初探
    2022-05-31 16:10:02
    本質 線程是不能被“殺掉”、“掛起”、“恢復”的,線程在執行的時候自己占據著CPU,別人怎么可能控制它呢?舉個極端的例子:如果不調用API,屏蔽中斷,并保證代碼不出現異常,線程將永久占用CPU,何談控制呢?所以說線程如果想“死”,一定是自己執行代碼把自己殺死,不存在“他殺”這種情況!那如果想改變一個線程的行為該怎么辦呢?可以給他提供一個函數,讓它自己去調用,這個函數就是APC,即異步過程調用。
    用來恢復線程,如果函數成功, 則傳回線程的前一個掛起次數。如果失敗, 則傳回0xFFFFFFFF。
    在對它的調用時候,壓入的兩個參數除了NotifyRoutine和Remove以外,還壓入了一個0。
    線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    全局鉤子注入-獲取QQ密碼實現 全局鉤子注入-獲取QQ密碼實現 水一篇????? SetWindowsHookExA 將應用程序定義的掛鉤過程安裝到掛鉤鏈中。您將安裝一個掛鉤程序來監視系統中某些類型的事件。這些事件與特定線程或與調用線程相同的桌面中的所有線程相關聯。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类