一段ShellCode

 
 
//此代码出自《0day安全:软件漏洞分析技术》
//由于一开始读不懂,就慢慢一点点的加入了注释
//欢迎各位拍砖
//注释中实际内存地址,就是虚拟地址。
#include <stdlib.h>
#include <stdio.h>

int main()
{
	_asm
	{
		CLD
		push 0x1e380a6a	;MessageBoxA
		push 0x4fd18963	;ExitProcess
		push 0x0c917432	;LoadLibraryA
		mov esi, esp	;esi指向的是LoadLibraryA
		lea edi, [esi - 0xc]	;edi指向的是未使用的栈地址起始位置,用来保存函数地址

		xor ebx, ebx	;ebx = 0
		mov bh, 0x04	;ebx = 0x4
		sub esp, ebx	;栈地址提升4个偏移

		mov bx, 0x3233	;32
		push ebx
		push 0x72657375	;user
		push esp		;将栈中放入user32
		xor edx, edx	;清edx

		mov ebx, fs:[edx + 0x30]	;ebx进程环境块PEB的地址
		mov ecx, [ebx + 0xc]		;ecx存放PEB_LDR_DATA结构体的指针
		mov ecx, [ecx + 0x1c]		;ecx中存放指向模块初始化链表的头指针InInitializationOrderModuleList
		mov ecx, [ecx]				;InInitializationOrderModuleList中按顺序存放着PE装入运行时初始化模块的信息,
									;第一个是ntdll.dll,ntdll.dll所指向的就是kernel32.dll
		mov ebp, [ecx + 0x8]		;ecx + 0x8就是kernel32.dll在内存中的加载基地址

find_lib_functions:
		lodsd						;lodsd是把DS:ESI所指向的地址处的数据放入eax中,也就是0x0c917432
									;然后ESI+4,此时ESI指向的是ExitProcess
		cmp eax, 0x1e380a6a			;此处用来判断是否把所有需要的函数地址都获取完成,MessageBoxA
		jne find_functions			;由于MessageBoxA在user32.dll中,因此,需要先加载user32.dll
		xchg eax, ebp				;使用ebp保存MessageBoxA的hash
		call [edi - 0x8]			;LoadLibraryA 参数在上面,push esp那里
		xchg eax, ebp				;此时ebp为user32.dll的基地址,eax为MessageBoxA的hash

find_functions:
		pushad						;Push(EAX);0x0c917432  LoadLibraryA
									;Push(ECX);kernel32.dll链表节点
									;Push(EDX);为0
									;Push(EBX);进程环境块PEB的地址
									;Push(ESP);该ESP为执行pushad之前的ESP
									;Push(EBP);kernel32.dll在内存中的加载基地址
									;Push(ESI);esi指向的是ExitProcess
									;Push(EDI);edi指向的是未使用的栈地址起始位置,用来保存函数地址
									;此处入栈顺序必须记得,后面会使用到
		mov eax, [ebp + 0x3c]		;Kernel32.dll基地址 + 0x3c是PE头
		mov ecx, [ebp + eax + 0x78]	;此时ecx存放的是函数导出表的指针
		add ecx, ebp				;导出表的此次加载实际内存地址
		mov ebx, [ecx + 0x20]		;偏移0x20处指向导出函数函数名的列表
		add ebx, ebp				;函数名的列表此次加载的实际内存地址
		xor edi, edi				;edi清0,用来计数

next_function_loop:
		inc edi
		mov esi, [ebx + edi * 4]	;取出每个函数名的地址
		add esi, ebp				;函数名的实际内存地址
		cdq							;把EDX的所有位都设成EAX最高位的值,可以暂时简单理解为把EDX清0

hash_loop:							;此处循环计算hash
		movsx eax, byte ptr[esi]
		cmp al, ah					;判断是否把函数名计算完成
		jz compare_hash
		ror edx, 7
		add edx, eax
		inc esi
		jmp hash_loop

compare_hash:
		cmp edx, [esp + 0x1c]	;由于刚刚的pushad,把寄存器都压入栈中,此时esp指向的是栈中的EDI,
								;esp + 1c指向的则是EAX,LoadLibraryA
		jnz next_function_loop
		mov ebx, [ecx + 0x24]	;在导出表结构中,偏移0x24是导出函数序号的相对虚拟地址
		add ebx, ebp			;导出函数序号的虚拟地址
		mov di, [ebx + 2 * edi]	;获取第edi个函数的函数序号,由于函数序号是一个WORD型的数据,因此edi * 2
		mov ebx, [ecx + 0x1c]	;此处为导出函数的相对虚拟地址
		add ebx, ebp			;导出函数的虚拟地址
		add ebp, [ebx + 4 * edi];获取函数序号所对应的函数地址,这就是我们最终所需要的函数地址。
		xchg eax, ebp			;eax为所需要的函数地址
		pop edi					;EDI出栈,用来保存LoadLibraryA的函数地址
		stosd					;mov [edi],eax    函数地址保存
								;add edi,4
		
		push edi				
		popad					;此时,相对于pushad的时候,只有edi变化了,增加了4,其它寄存器均未变化

		cmp eax, 0x1e380a6a		;当所有地址都获取完成后
		jne find_lib_functions

function_call:
		xor ebx, ebx
		push ebx
		push 0x2121216E			;
		push 0x3167304C			;
		mov eax, esp
		push ebx				;压入参数
		push eax
		push eax
		push ebx
		call [edi - 0x04]		;调用MessageBoxA
		push ebx
		call [edi - 0x08]		;调用ExitProcess
		nop
		nop
		nop
		nop
	}
	system("pause");
	return 0;
}


你可能感兴趣的:(一段ShellCode)