shellcode中动态定位API

 

定位API的原理:

所有的win_32程序都会加载ntdll.dll和kerner32.dll这两个最基础的动态链接库。如果想要在win_32平台下定位kernel32.dll中的API地址

  1,首先通过段选择字FS在内存中找到当前的线程环境快TEB。

  2,线程环境快偏移位置为0x30的地方存放着指向进程环境块PEB的指针。

  3,进程环境块中偏移位置为0x0C的地方存放着指向PEB_LDR_DATA结构体的指针,其中,存放着已经被进程装在的动态链接库的信息。

  4,PEB_LDR_DATA结构体偏移位置为0x1C的地方存放着指向模块初始化链表的头指针InInitizationOrderModuleList.

  5,模块初始化链表InInitizationOrderModuleList中按顺序存放着PE装入运行时初始化模块信息,第一个链表结点是ntdll.dll,第二个链表结点就是kernel32.dll。

  6,找到属于kernel32.dll的结点后,在其基础上再偏移0x08就是kernel32.dll在内存中的加载基地址。

  7,从kernel32.dll的加载基址算起,偏移0x3C的地方就是其PE头。

  8,PE头偏移0x78的地方存放着指向函数导出表的指针。

  9,导出表0x1C处的指针指向存储导出函数偏移地址(RVA)的列表

            导出表偏移0x20处的指针指向存储导出函数函数名的列表

            函数的RVA地址和名字按照顺序存放在上述两个列表中,我们可以在名称列表中定位到所需的函数是第几个,然后在地址列表中找到对应的RVA

             获得RVA后,再加上前面已经得到的动态链接库的加载基址,就获得了所需API此刻在内存中的虚拟地址。

shellcode中动态定位API_第1张图片

PE导出表结构如下:

shellcode中动态定位API_第2张图片

 

代码实现流程

shellcode中动态定位API_第3张图片

 

具体代码实现

 编译环境VS2013+x86 测试环境server 2008 r2 x64 

__asm
	{
			CLD
			push 0x1e380a6a				// 压入 MessageBoxA 字符串的hash
			push 0x4fd18963				// 压入 ExitProcess 字符串的hash
			push 0x0c917432;			// 压入 LoadLibraryA 字符串的hash
			mov esi, esp				// esi = LoadLibraryA 字符串 hash 地址
			lea edi, [esi - 0xc]			// 用于存放后边找到的 三个函数地址

			// 开辟一些栈空间
			xor ebx, ebx
			mov bh, 0x04
			sub esp, ebx

			// 压入一个指向 user32字符串的 栈指针
			mov bx, 0x3233
			push ebx
			push 0x72657375
			push esp
			xor edx, edx

			// 得到 kernel32.dll 的基地址
			mov ebx, fs:[edx + 0x30]		// FS得到当前线程环境块TEB TEB+0x30 是进程环境块 PEB
			mov ecx, [ebx + 0x0c]			// PEB+0x0c 是PEB_LDR_DATA结构体指针 存放这已经被进程加载的动态链接库的信息
			mov ecx, [ecx + 0x1c]			// PEB_LDR_DATA+0x1c 指向模块初始化链表的头指针 InInitalizationOrderModuleList
			mov ecx, [ecx]				// ecx指向 第二个节点  KERNELBASE.dll
			mov ecx, [ecx]				// ecx指向 第三个节点 kernel32.dll
			mov ebp, [ecx + 0x08]			// InInitializationOrderLinks+0x08 得到 DllBase 字段 即kernel32.dll基地址

		find_lib_functions :
			lodsd;					// 将[esi]中的4字节 传到eax中
			cmp eax, 0x1e380a6a;			// 比较 MessageBoxA 字符串的hash值
			jne find_functions
			xchg eax, ebp				// 记录当前hash值
			call[edi - 0x8]; loadLibraryA
			xchg eax, ebp				// 还原当前hash值 并且把exa基地址更新为 user32.dll的基地址

		find_functions :
			pushad					// 保存寄存器环境
			mov eax, [ebp + 0x3c]			// 得到 当前DLL PE头  循环依次是kernel32.dll 和 user32.dll
			mov ecx, [ebp + eax + 0x78]		// 得到 当前DLL 导出函数表 相对地址
			add ecx, ebp				// 得到 当前DLL 导出函数表 绝对地址
			mov ebx, [ecx + 0x20]			// 得到导出函数 名称表 相对地址
			add ebx, ebp				// 得到导出函数 名称表 绝对地址
			xor edi, edi				// 初始化计数器

		next_function_loop :
			inc edi					// 函数计数器+1
			mov esi, [ebx + edi * 4]		// 得到 当前函数名的 相对偏移
			add esi, ebp				// 得到 当前函数名的 绝对地址
			cdq;					// 因为知道EAX<80000000 所以edx会被置0 相当于 xor edx,edx的功能 
								// 此指令只有一个字节 节约空间 算是一种优化

		hash_loop:					// 循环得到当前函数名的hash
			movsx eax, byte ptr[esi]		// 得到当前函数名称 第esi的一个字母
			cmp al, ah				// 比较到达函数名最后的0没有
			jz compare_hash				// 函数名hash 计算完毕后跳到 下一个流程
			ror edx, 7				// 循环右移7位
			add edx, eax				// 累加得到hash
			inc esi					// 计数+1 得到函数名的下一个字母
			jmp hash_loop				// 循环跳到 hash_loop

		compare_hash :						
			cmp edx, [esp + 0x1c]			// 比较 目标函数名hash 和 当前函数名的hash
			jnz next_function_loop			// 如果 不等于 继续下一个函数名
			mov ebx, [ecx + 0x24]			// 得到 PE导出表中的 函数序号列表的 相对位置
			add ebx, ebp				// 得到 PE导出表中的 函数序号列表的 绝对位置
			mov di, [ebx + 2 * edi]			// 得到 PE导出表中的 当前函数的序号
			mov ebx, [ecx + 0x1c]			// 得到 PE导出表中的 函数地址列表的 相对位置
			add ebx, ebp				// 得到 PE导出表中的 函数地址列表的 绝对位置
			add ebp, [ebx + 4 * edi]		// 得到 PE导出表中的 当前函数的绝对地址 
								// 循环依次得到kernel32.dll中的 LoadLibraryA  ExitProcess
								// 和user32.dll中的 MessageBoxA

			xchg eax, ebp				// 把函数地址放入eax中
			pop edi					// pushad中最后一个压入的是edi 正好是开始预留 用于存放的三个函数地址 的栈空间
			stosd					// 把找到函数地址出入 edi对应的栈空间
			push edi				// 继续压栈 平衡栈
			popad					// 还原环境
			cmp eax, 0x1e380a6a			// 比较是否是 MessageBoxA 函数 如果是说明全部函数已经找齐 可以调用函数执行功能
			jne find_lib_functions

		function_call :
			xor ebx, ebx
			push ebx				// 压入字符串结尾的0
			push 0x74736577
			push 0x6c696166				// 压入字符串 failwest
			mov eax, esp				// 得到字符串 failwest 地址
			push ebx
			push eax
			push eax
			push ebx
			call[edi - 0x04]			// 调用	MessageBoxA(0, "failwest", "failwest", 0);
			push ebx
			call[edi - 0x08]			// 调用 ExitProcess(0);
			nop
			nop
			nop
			nop

	}

参考《0day安全:软件漏洞分析技术》

你可能感兴趣的:(shellcode中动态定位API)