欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

0day安全阅读笔记[1]

程序员文章站 2024-03-11 22:16:01
...

第一个问题:

首先来看一下那个HASH算法,这个HASH算法有一点懵住我了,一开始看那一段代码始终没看出个所以然:

digest = ((digest << 25) | (digest >> 7));

一直在想的是|或位运算符是从左往右的顺序,那digest << 25生成的结果,后面的digest >> 7会使用吗? 还是全部用原始的? 结果证明是不会影响的:

0day安全阅读笔记[1]

转成反汇编后就看到了, 把digest的值。一次放eax, 一次放ecx, 分开来进行左右移的运算,最终或起来后加到一起。

第二个问题:

这段shellcode的详细注释已经写上,在看不明白那基本上很难明白了

__asm {
		cld // 把DF清零
		push 0x1e380a6a // MessageBoxA的HASH值
		push 0x4fd18963 // ExitProcess的HASH值
		push 0x0c917432 // LoadLibraryA的HASH值
		mov esi, esp // 保存LoadLibraryA的HASH首地址到esi中
		lea edi, [esi - 0xC] // 留出可容纳3个API的地址的空间

		xor ebx, ebx 
		mov bh, 0x04
		sub esp, ebx // 抬高栈顶0x400的大小, 防止破坏shellcode

		mov ebx, 0x3233
		push ebx 
		push 0x72657375  // 把user32字符串压入堆栈
		push esp // 把"user32"字符串首地址压入堆栈
		xor edx, edx 

		mov ebx, fs:[edx + 0x30] // 拿到PEB指针
		mov ecx, [ebx + 0x0c]    // 拿到PEB_LDR_DATA指针
		mov ecx, [ecx + 0x1c]    // 拿到指向模块初始化链表的头指针, 其指向了NTDLL.dll
		mov ecx, [ecx]			 // 拿到kernel32.dll
		mov ebp, [ecx + 0x08]	 // kernel32.dll的BaseImage

	find_lib_functions:
		lodsd // 从ds:[esi]中拿到了LoadLibraryA的HASH,esi增加4
		cmp eax, 0x1e380a6a // 这个是MessageBoxA的HASH值,第一次进入肯定不可能,因为在栈顶的是LoadLibrary,所以必然跳转, 第三次才会不跳
		jne find_functions // 如果第一次跳到这,直接看find_functions
		xchg eax, ebp  // 因为eax在下面的LoadLibraryA的调用中会被覆盖,所以保存一下eax即MessageBox的Hash值
		call [edi - 0x8] // 这里调用的是LoadLibraryA
		xchg eax, ebp // 这里成功加载了user32.dll并获取了其模块句柄。这时ebp装的是MessageBoxA的HASH而eax装的是user32.dll的句柄

	find_functions:
		pushad // 以EAX,ECX,EDX,EBX,ESP,EBP,ESI, EDI顺序依次压栈, 注意,这里最后一个是EDI即栈顶
		mov eax, [ebp + 0x3C] // Kernel32.dll的ImageBase即PE结构的DOS头开始+3C就是PE头的开始, 实际上就是e_lfanew的值
		mov ecx, [ebp + eax + 0x79] // 这里是PE头在加上0x79是IMAGE_DATA_DIRECTORY结构中导出表的VirtualAddress
		add ecx, ebp // VirtualAddress是RVA, 加上ImageBase后就变成VA了
		mov ebx, [ecx + 0x20] // AddressOfNames的RVA
		add ebx, ebp // AddressOfNames的VA
		xor edi, edi 

	next_function_loop:
		inc edi 
		mov esi, [ebx + edi * 4] // 这个是在名称地址表中遍历各个API名称, 因为名称地址表中存储的都是4字节为单位的函数名称的RVA地址
		add esi, ebp // 同样求出每个API名称的VA
		cdq // 这个指令把eax扩展成edx:eax, 并且edx内每一位的值都是eax的符号位, 实际上就是都是0了, 因为

	hash_loop: // 这段代码实际上就是GetHash函数的汇编形式
		movsx eax, byte ptr [esi] // 实际上这里玩了个技巧,可能一些人一开始会看懵(比如我)
		cmp al, ah				  // 他这里想做的事是把每个API名称字符与0比较,看字符串比较结束了没,结束了就代表HASH算完了,movsx的出现是为了让后面全部为0,方便用ah(0)与al字符比较
								 
		jz compare_hash // 如果相等代表字符串结束了, 不然就是按照算法按计算HASH
		ror edx, 7  // 循环右移, 多出来的位补最左边
		add edx, eax // edx 相当于之前的digest变量
		inc esi 
		jmp hash_loop 
	compare_hash:
		cmp edx, [esp + 0x1C] // esp就是LoadLibraryA字符串的首地址
		
		jnz next_function_loop // 不相等则跳回去继续把LoadLibraryA和下一个位于地址名称表中的API名字对比

		mov ebx, [ecx + 0x24] // 相等拿到AddressOfNameOrdinals的RVA
		add ebx, ebp  // 拿到AddressOfNameOrdinals的VA
		mov di, [ebx + 2 * edi] // 因为ordinal是word为单位所以是乘2
		
		mov ebx, [ecx + 0x1C] // AddressOfFunctions的RVA
		add ebx, ebp // AddressOfFunctions的VA

		add ebp, [ebx + 4 * edi] // 对应名称的函数VA地址
		xchg eax, ebp // 把函数VA地址放入eax中
		pop edi // edi是pushad最后一个压入栈的所以位于栈顶,pop出来即可
		stosd // 将函数地址其保存到ds:[edi]中, 往上回看一下会发现,lea edi, [esi - 0xC]即是这个
		
		push edi // push回去,不然popad会出问题

		popad 
		cmp eax, 0x1e380a6a // 与最后一个MessageBoxA对比,如果是MessageBoxA代表已经到了尽头。
		jne find_lib_functions // 不等则继续找
	function_call:
		xor ebx, ebx 
		push ebx
		push 0x74736577
		push 0x6C696166
		mov eax, esp
		push ebx
		push eax
		push eax
		push ebx 
		call[edi - 0x04] // MessageBox
		push ebx
		call [edi - 0x08] // ExitProcess
		nop
		nop
		nop
		nop
	}

这段shellcode写的真的很好。一切都算的刚刚好

(完)

相关标签: 漏洞