• 有痕注入的分析和实现


    背景

    之前分析过某老哥的超级注入,最近突然来了兴致,简单的把它实现一下。

    开整

    首先说一下思路

    1、找到目标进程,并且暂停它的第一个线程;

    2、在目标进程中申请一个页大小的内存,放入shellcode,x86和x64分别做对应的处理(这里使用的是LdrLoadDll(加载之后痕迹比较明显),不满意可以自己映射,修复IAT);

    3、替换返回到r3的eip/rip为shellcode的起始地址,恢复线程等待线程执行;

    4、创建工作线程释放内存资源;

    看着也挺简单的,这里有几个坑需要注意一下。

    PsSuspendThread  PsResumeThread这两个函数不导出,所以这里写了一个搜索特征码的函数

    	UCHAR suspendOpCodeWin7[] = { 0x4C,0x8B,0xEA,0x48,0x8B,0xF1,0x33,0xFF,0x89,0x7C,'*','*',0x65,
    		'*','*','*','*','*','*','*','*',0x4C,0x89,'*','*','*','*','*','*',0x66,0x41,'*','*','*',
    		'*','*','*','*',0x48,'*','*','*','*','*','*',0x0F,'*','*',0x48,0x8B,0x01 };
    	UCHAR suspendOpCodeWin10[] = { 0x48,0x83,0xEC,'*',0x4C,0x8B,'*',0x48,0x8B,0xF9,0x83,0x64,0x24,
    		0x20,'*',0x65,0x48,0x8B,0x34,0x25,0x88,0x01,0x00,'*',0x48,0x89,0x74,0x24,0x70 };
    	g_PsSuspendThread = (PPsSuspendThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", suspendOpCodeWin7, sizeof(suspendOpCodeWin7), -21);
    	if (!g_PsSuspendThread)
    	{
    		g_PsSuspendThread = (PPsSuspendThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", suspendOpCodeWin10, sizeof(suspendOpCodeWin10), -17);
    		if(!g_PsSuspendThread)
    			return STATUS_UNSUCCESSFUL;
    	}
    
    	UCHAR resumeOpCode1[] = { 0x40,0x53,0x48,'*','*','*',0x48,0x8B,0xDA,0xE8,'*','*','*','*',0x48,
    		0x85,0xDB,0x74,'*',0x89,0x03,0x33,0xC0,0x48,'*','*','*',0x5B,0xC3 };
    	UCHAR resumeOpCode2[] = { 0xFF,0xF3,0x48,'*','*','*',0x48,0x8B,0xDA,0xE8,'*','*','*','*',0x48,
    		0x85,0xDB,0x74,'*',0x89,0x03,0x33,0xC0,0x48,'*','*','*',0x5B,0xC3 };
    	UCHAR resumeOpCode3[] = { 0x48,0x83,0xEC,'*',0x48,0x8B,0xDA,0x48,0x8B,0xF9,0xE8,'*','*','*',
    		'*',0x65,0x48,0x8B,0x14,0x25,'*','*','*','*',0x8B,0xF0,0x83,0xF8,0x01 };
    	UCHAR resumeOpCode4[] = { 0x48,0x89,0x54,'*','*',0x48,0x89,'*','*','*',0x53,0x56,0x57,0x41,
    		0x56,0x41,0x57 };
    
    	g_PsResumeThread = (PPsResumeThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", resumeOpCode1, sizeof(resumeOpCode1), 0);
    	if (!g_PsResumeThread)
    	{
    		g_PsResumeThread = (PPsResumeThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", resumeOpCode2, sizeof(resumeOpCode2), 0);
    		if (!g_PsResumeThread)
    		{
    			g_PsResumeThread = (PPsResumeThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", resumeOpCode3, sizeof(resumeOpCode3), -11);
    			if (!g_PsResumeThread)
    			{
    				g_PsResumeThread = (PPsResumeThread)SearchOPcode(pObj, L"ntoskrnl.exe", "PAGE", resumeOpCode4, sizeof(resumeOpCode4), 0);
    				if (!g_PsResumeThread)
    				{
    					return STATUS_UNSUCCESSFUL;
    				}
    			}
    		}
    	}

    ZwGetNextThread只在win10上导出(只有win102h的虚拟机和win7sp1的做测试),win7不导出,还是特征码的方式定位。

    	UNICODE_STRING ZwGetNextThreadString = RTL_CONSTANT_STRING(L"ZwGetNextThread");
    	g_ZwGetNextThread = (PZwGetNextThread)MmGetSystemRoutineAddress(&ZwGetNextThreadString);
    	if (!g_ZwGetNextThread)
    	{
    		UNICODE_STRING ZwGetNotificationResourceManagerString = RTL_CONSTANT_STRING(L"ZwGetNotificationResourceManager");
    		PUCHAR ZwGetNotificationResourceManager = (PUCHAR)MmGetSystemRoutineAddress(&ZwGetNotificationResourceManagerString);
    		if (ZwGetNotificationResourceManager)
    		{
    			PUCHAR starAddress = ZwGetNotificationResourceManager - 78;
    			for(; starAddress < ZwGetNotificationResourceManager - 8; starAddress++)
    			{
    				if (starAddress[0] == 0x48 && starAddress[1] == 0x8B && starAddress[2] == 0xC4)
    				{
    					g_ZwGetNextThread = (PZwGetNextThread)starAddress;
    					break;
    				}	
    			}
    		}
    		if(!g_ZwGetNextThread)
    			return STATUS_UNSUCCESSFUL;
    	}

    x64的shellcode有一个坑需要注意

    	PINJECT_BUFFER InjectBuffer = NULL;
    	UCHAR Code[] = {
    		0x41, 0x57,                             // push r15
    		0x41, 0x56,                             // push r14
    		0x41, 0x55,                             // push r13
    		0x41, 0x54,                             // push r12
    		0x41, 0x53,                             // push r11
    		0x41, 0x52,                             // push r10
    		0x41, 0x51,                             // push r9
    		0x41, 0x50,                             // push r8
    		0x50,                                   // push rax
    		0x51,                                   // push rcx
    		0x53,                                   // push rbx
    		0x52,                                   // push rdx
    		0x55,                                   // push rbp
    		0x54,                                   // push rsp
    		0x56,                                   // push rsi
    		0x57,                                   // push rdi
    		0x66, 0x9C,                             // pushf
    		0x48, 0x83, 0xEC, 0x26,                 // sub rsp, 0x28
    		0x48, 0x31, 0xC9,                       // xor rcx, rcx
    		0x48, 0x31, 0xD2,                       // xor rdx, rdx
    		0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0,     // mov r8, ModuleFileName   offset +38
    		0x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0,     // mov r9, ModuleHandle     offset +48
    		0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0,     // mov rax, LdrLoadDll      offset +58
    		0xFF, 0xD0,                             // call rax
    		0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0,     // mov rdx, COMPLETE_OFFSET offset +70
    		0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0,     // mov [rdx], CALL_COMPLETE 
    		0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0,     // mov rdx, STATUS_OFFSET   offset +86
    		0x89, 0x02,                             // mov [rdx], eax
    		0x48, 0x83, 0xC4, 0x26,                 // add rsp, 0x28
    		0x66, 0x9D,                             // popf
    		0x5F,                                   // pop rdi
    		0x5E,                                   // pop rsi 
    		0x5C,                                   // pop rsp
    		0x5D,                                   // pop rbp
    		0x5A,                                   // pop rdx
    		0x5B,                                   // pop rbx
    		0x59,                                   // pop rcx
    		0x58,                                   // pop rax
    		0x41, 0x58,                             // pop r8
    		0x41, 0x59,                             // pop r9
    		0x41, 0x5A,                             // pop r10
    		0x41, 0x5B,                             // pop r11
    		0x41, 0x5C,                             // pop r12
    		0x41, 0x5D,                             // pop r13
    		0x41, 0x5E,                             // pop r14
    		0x41, 0x5F,                             // pop r15
    		0x50,                                   // push rax
    		0x50,                                   // push rax 
    		0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0,     // mov rax, orgEip offset +130
    		0x48, 0x89, 0x44, 0x24, 0x08,           // mov [rsp+8],rax
    		0x58,                                   // pop rax
    		0xC3                                    // ret
    	};

    下面截图显示的是异常的时候rip执行的位置

     movaps会检测当前堆栈是否按照0x10对齐,所以那里的sub 0x26而不是0x28

    x32的地方也有一个坑,+0x1488之后的context的第一个结构需要在一个dowrd大小的padding

    typedef struct _WOW64_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 padding;
    	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.
    	//
    
    	WOW64_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[WOW64_MAXIMUM_SUPPORTED_EXTENSION];
    
    } WOW64_CONTEXT, *PWOW64_CONTEXT;

    最后释放内存的时候两种情况,线程执行了shellcode,线程没有执行,如果执行了,直接释放,没有执行则在超时之后还原原始的eip/rip

    VOID freeMemory(PVOID Parameter)
    {
    	ULONG counts = 0;
    	SIZE_T Size = PAGE_SIZE;
    	PEPROCESS pEprocess = NULL;
    	PFREEADDRESS freeAdd = (PFREEADDRESS)Parameter;
    	
    	if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)(freeAdd->pid), &pEprocess)))
    	{
    		KAPC_STATE kApc = { 0 };
    		while (TRUE)
    		{
    			KeStackAttachProcess(pEprocess, &kApc);
    			__try 
    			{
    				ProbeForRead((PVOID)freeAdd->allcateAddress,sizeof(freeAdd->allcateAddress),sizeof(CHAR));
    				if (freeAdd->allcateAddress->Complete || counts > MAXCOUNTS)
    				{
    					if (counts > MAXCOUNTS)
    					{
    						if (g_PsGetProcessWow64Process(pEprocess) != 0)
    						{
    							ProbeForRead((PVOID)freeAdd->allcateAddress->orgRipAddress, sizeof(freeAdd->allcateAddress->orgRipAddress), sizeof(CHAR));
    							*(ULONG*)freeAdd->allcateAddress->orgRipAddress = (ULONG)freeAdd->allcateAddress->orgRip;
    						}
    						else
    						{
    							if(MmIsAddressValid((PVOID)freeAdd->allcateAddress->orgRipAddress))
    								*(ULONG64*)freeAdd->allcateAddress->orgRipAddress = (ULONG64)freeAdd->allcateAddress->orgRip;
    						}
    					}
    					ZwFreeVirtualMemory((HANDLE)-1, (PVOID)& freeAdd->allcateAddress, &Size, MEM_DECOMMIT);
    					break;
    				}
    			}
    			__except (EXCEPTION_EXECUTE_HANDLER) 
    			{
    				break;
    			}
    			KeUnstackDetachProcess(&kApc);
    			Sleep(MSEC);
    			counts++;
    		}
    		KeUnstackDetachProcess(&kApc);
    		ObDereferenceObject(pEprocess);
    	}
    
    	ExFreePool(freeAdd);
    	freeAdd = NULL;
    	g_gameOver = TRUE;
    }
    

    代码

    放在GitHub

    还有一个简单的驱动加载代码

  • 相关阅读:
    C#2008与.NET 3.5 高级程序设计读书笔记(31) 构建ASP.NET网页
    从零开始学习jQuery (二) 万能的选择器
    C#2008与.NET 3.5 高级程序设计读书笔记(32) ASP.NET Web控件、主题和母版页
    从零开始学习jQuery (六) jQuery中的Ajax
    从零开始学习jQuery (五) 事件与事件对象
    从零开始学习jQuery(剧场版) 你必须知道的javascript
    从零开始学习jQuery (一) 开天辟地入门篇
    从零开始学习jQuery (三) 管理jQuery包装集
    DateTime 中的 ToString 格式化输出
    IHttpHandler中的IsReusable属性
  • 原文地址:https://www.cnblogs.com/csnd/p/16675576.html
Copyright © 2020-2023  润新知