NtSockets - 直接與驅動通信實現sockets

本文為看雪論壇優秀???文章
看雪論壇作者ID:zhenwo

最近在研究syscall相關的技術,無意中發現了這個NtSockets(只使用NtCreateFile和NtDeviceIoControlFile 函數,直接操作afd.sys驅動實現socket),感謝作者@x86matthew。
原文鏈接 NTSockets - Downloading a file via HTTP using the NtCreateFile and NtDeviceIoControlFile syscalls
(https://www.x86matthew.com/view_post?id=ntsockets)
項目的bug和不足
作者只實現了TCP-client的代碼,并且x86下測試通過,但是在x64模式下連接到服務端時出現了錯誤。
解決x64環境下connect中的bug
1. 推測問題
根據函數的返回狀態 0xc000000d 可知是參數錯誤。

根據代碼:
NtDeviceIoControlFile(pSocketData->hSocket, pSocketData->hStatusEvent, NULL, NULL, &IoStatusBlock, dwIoControlCode, (void*)pData, dwLength, bOutputBlock, sizeof(bOutputBlock));
可以推測應該是傳入的pData和dwLength出現了錯誤,根據調用關系:
struct NTSockets_ConnectDataStruct 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;ULONG sz = sizeof(NTSockets_ConnectData);if (NTSockets_SocketDriverMsg(pSocketData, 0x00012007, (BYTE*)&NTSockets_ConnectData, sz, NULL) != 0){ // error return 1;}
那么問題就出在結構體NTSockets_ConnectDataStruct上。
struct NTSockets_ConnectDataStruct{ ULONG dwUnknown1; ULONG dwUnknown2; ULONG dwUnknown3; struct sockaddr_in SockAddr;};
2. 證實猜測
隨便在網上找了個TCP-Client的代碼,選擇x64模式,在connect上下斷點,當在connect斷下來后,再在NtDeviceIoControlFile上下斷點,再運行。
可以看到有幾次觸發斷點:
dwIoControlCode0x01207b mswsock.dll!SockGetInformation() Unknown0x0120bf mswsock.dll!WSPSetSockOpt() Unknown0x012003 mswsock.dll!WSPBind() Unknown0x012047 mswsock.dll!SockSetHandleContext() Unknown0x012007 mswsock.dll!SockDoConnectReal() Unknown
這里我們只關心 dwIoControlCode為0x012007 的調用。通過對比參數可以找到參數以及大小。
size 0x280x000000D8606FEE40 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |AFIN |port |---IP---|0x000000D8606FEE50 30 ef 6f 60 d8 00 00 00 02 00 00 50 7f 00 00 01 |------ZERO8----------|0x000000D8606FEE60 00 00 00 00 00 00 00 00
而NtSockets代碼參數是:
size 0x1c |AFIN |port0x0000002EE86FD9F8 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 50 |---IP---| |------ZERO8----------|0x0000002EE86FDA08 c0 a8 0a 75 00 00 00 00 00 00 00 00
3.解決辦法
重新定義下NTSockets_ConnectDataStruct結構體就可以了。
struct NTSockets_ConnectDataStruct{ SIZE_T dwUnknown1; SIZE_T dwUnknown2; SIZE_T dwUnknown3; struct sockaddr_in SockAddr;};
SIZE_T x86下是ULONG 在x64下是ULONGLONG。
增加對UDP的支持
先來看看TCP的細節,根據代碼可以看出TCP創建句柄跟bExtendedAttributes有關。所以我們關注下這個參數:
NTSTATUS NTAPI NtCreateFile ( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocateSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength ) dwStatus = NtCreateFile(&hSocket, 0xC0140000, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, &bExtendedAttributes, sizeof(bExtendedAttributes));
在socket函數下斷點,斷下來后在NtCreateFile函數下斷點。堆棧情況如下:
0x000000EC37FCEE40 0000000000000000 ........0x000000EC37FCEE48 004f004400000000 ....D.O.0x000000EC37FCEE50 000000ec00000003 ....ì...0x000000EC37FCEE58 000000ec00000003 ....ì...0x000000EC37FCEE60 000000ec00000000 ....ì...0x000000EC37FCEE68 000000ec37fcef60 `?ü7ì...0x000000EC37FCEE70 002e006b00000039 9...k...
這是x64下的情況,去除前4個參數(利用寄存器傳參),以上是第5-11參數。這里我們只關注后兩個參數:
0x000000EC37FCEE68 000000ec37fcef60 `?ü7ì... EaBuffer0x000000EC37FCEE70 002e006b00000039 9...k... EaLength EaBuffer:0x000000EC37FCEF60 00 00 00 00 00 0f 1e 00 41 66 64 4f 70 65 6e 50 ........AfdOpenP0x000000EC37FCEF70 61 63 6b 65 74 58 58 00 00 00 00 00 00 00 00 00 acketXX.........0x000000EC37FCEF80 02 00 00 00 01 00 00 00 06 00 00 00 00 00 00 00 ................0x000000EC37FCEF90 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
對比NtSockets的EaBuffer:
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 };
我解惑的數據和作者給出的有些出入,不過不影響使用。
截獲UDP的EaBuffer
直接將socket的參數調整成UDP的。還是在socket斷下來以后,給NtCreateFile下斷點,以下是udp的堆棧情況。
0x000000B698EFF308 0000000000000000 ........0x000000B698EFF310 0000000000000000 ........0x000000B698EFF318 004f004400000000 ....D.O.0x000000B698EFF320 000000b600000003 ....?...0x000000B698EFF328 000000b600000003 ....?...0x000000B698EFF330 000000b600000000 ....?...0x000000B698EFF338 000000b698eff430 0????...0x000000B698EFF340 002e006b00000039 9...k... EaBuffer:0x000000B698EFF430 00 00 00 00 00 0f 1e 00 41 66 64 4f 70 65 6e 50 ........AfdOpenP0x000000B698EFF440 61 63 6b 65 74 58 58 00 11 00 00 00 00 00 00 00 acketXX.........0x000000B698EFF450 02 00 00 00 02 00 00 00 11 00 00 00 00 00 00 00 ................0x000000B698EFF460 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
只要替換NtSockets中的bExtendedAttributes數組就可以產生UDP的連接。
最終代碼
#include #include #pragma warning(suppress : 4996)#pragma warning(disable : 4996)#define _CRT_SECURE_NO_WARNINGS 1struct 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; struct UNICODE_STRING* ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService;}; struct FILE_FULL_EA_INFORMATION{ ULONG NextEntryOffset; UCHAR Flags; UCHAR EaNameLength; USHORT EaValueLength; CHAR EaName[1];}; struct NTSockets_ConnectDataStruct{ SIZE_T dwUnknown1; SIZE_T dwUnknown2; SIZE_T dwUnknown3; struct sockaddr_in SockAddr;}; struct NTSockets_BindDataStruct{ DWORD dwUnknown1; struct sockaddr_in SockAddr;}; struct NTSockets_DataBufferStruct{ DWORD dwDataLength; BYTE* pData;}; struct NTSockets_SendRecvDataStruct{ struct 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(NTAPI* NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, struct IO_STATUS_BLOCK* IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);DWORD(NTAPI* NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, struct OBJECT_ATTRIBUTES* ObjectAttributes, struct IO_STATUS_BLOCK* IoStatusBlock, LARGE_INTEGER* AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength); DWORD NTSockets_CreateTcpSocket(struct NTSockets_SocketDataStruct* pSocketData,int bTcp){ struct IO_STATUS_BLOCK IoStatusBlock; HANDLE hEvent = NULL; HANDLE hSocket = NULL; struct OBJECT_ATTRIBUTES ObjectAttributes; struct NTSockets_SocketDataStruct SocketData; struct UNICODE_STRING ObjectFilePath; DWORD dwStatus = 0; /* tcp extended attr 0x0000003395EFF130 00 00 00 00 00 0f 1e 00 41 66 64 4f 70 65 6e 50 ........AfdOpenP 0x0000003395EFF140 61 63 6b 65 74 58 58 00 00 00 00 00 00 00 00 00 acketXX......... 0x0000003395EFF150 02 00 00 00 01 00 00 00 06 00 00 00 00 00 00 00 ................ 0x0000003395EFF160 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ */ /* udp 0x000000506EF3F040 00 00 00 00 00 0f 1e 00 41 66 64 4f 70 65 6e 50 ........AfdOpenP 0x000000506EF3F050 61 63 6b 65 74 58 58 00 11 00 00 00 00 00 00 00 acketXX......... 0x000000506EF3F060 02 00 00 00 02 00 00 00 11 00 00 00 00 00 00 00 ................ 0x000000506EF3F070 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ */ BYTE bEaUdp[] = { 0x00,0x00,0x00,0x00,0x00,0x0f,0x1e,0x00,0x41,0x66,0x64,0x4f,0x70,0x65,0x6e,0x50, 0x61,0x63,0x6b,0x65,0x74,0x58,0x58,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; 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 }; printf("[*] NTSockets_CreateTcpSocket:: is %s !",bTcp ? "TCP" : "UDP"); struct FILE_FULL_EA_INFORMATION* ffe; ffe = (struct FILE_FULL_EA_INFORMATION*)bTcp ? bExtendedAttributes : bEaUdp; // create status event hEvent = CreateEvent(NULL, 0, 0, NULL); if (hEvent == NULL) { // error return 1; } // set afd endpoint path memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath)); ObjectFilePath.Buffer = L"\\Device\\Afd\\Endpoint"; ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t); ObjectFilePath.MaximumLength = ObjectFilePath.Length; // initialise object attributes memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes)); ObjectAttributes.Length = sizeof(ObjectAttributes); ObjectAttributes.ObjectName = &ObjectFilePath; ObjectAttributes.Attributes = 0x40; // create socket handle IoStatusBlock.Status = 0; IoStatusBlock.Information = NULL; dwStatus = NtCreateFile(&hSocket, 0xC0140000, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, ffe, sizeof(bExtendedAttributes)); if (dwStatus != 0) { // error CloseHandle(hEvent); return 1; } // initialise SocketData object memset((void*)&SocketData, 0, sizeof(SocketData)); SocketData.hSocket = hSocket; SocketData.hStatusEvent = hEvent; // store socket data memcpy((void*)pSocketData, (void*)&SocketData, sizeof(SocketData)); return 0;} DWORD NTSockets_SocketDriverMsg(struct NTSockets_SocketDataStruct* pSocketData, DWORD dwIoControlCode, BYTE* pData, DWORD dwLength, DWORD* pdwOutputInformation){ struct IO_STATUS_BLOCK IoStatusBlock; DWORD dwStatus = 0; BYTE bOutputBlock[0x10]; // reset status event ResetEvent(pSocketData->hStatusEvent); // send device control request 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) { // response pending - wait for event if (WaitForSingleObject(pSocketData->hStatusEvent, -1) != WAIT_OBJECT_0) { // error return 1; } // complete - get final status code dwStatus = IoStatusBlock.Status; } // check for errors if (dwStatus != 0) { // error return 1; } if (pdwOutputInformation != NULL) { // store output info *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; // read IP string memset(szCurrOctet, 0, sizeof(szCurrOctet)); dwCurrOctetIndex = 0; pCurrByte = pIP; for (;;) { // process current character dwEndOfOctet = 0; if (*pCurrByte == '\0') { // end of string dwEndOfOctet = 1; dwEndOfString = 1; } else if (*pCurrByte == '.') { // end of octet dwEndOfOctet = 1; } else { // ensure this character is a number if (*pCurrByte >= '0' && *pCurrByte <= '9') { if (dwCurrOctetIndex > 2) { // invalid ip return 1; } // store current character szCurrOctet[dwCurrOctetIndex] = *pCurrByte; dwCurrOctetIndex++; } else { // invalid ip return 1; } } // check if the current octet is complete if (dwEndOfOctet != 0) { if (dwCurrOctetIndex == 0) { // invalid ip return 1; } // convert octet string to integer dwOctet = atoi(szCurrOctet); if (dwOctet > 255) { // invalid ip return 1; } // already read 4 octets if (dwCompletedOctetCount >= 4) { // invalid ip return 1; } // store current octet bOctets[dwCompletedOctetCount] = (BYTE)dwOctet; // current octet complete dwCompletedOctetCount++; if (dwEndOfString != 0) { // end of string break; } // reset szCurrOctet string memset(szCurrOctet, 0, sizeof(szCurrOctet)); dwCurrOctetIndex = 0; } // move to the next character pCurrByte++; } // ensure 4 octets were found if (dwCompletedOctetCount != 4) { // invalid string return 1; } // store octets in dword value memcpy((void*)&dwAddr, bOctets, 4); // store value *pdwAddr = dwAddr; return 0;} WORD NTSockets_Swap16BitByteOrder(WORD wValue){ WORD wNewValue = 0; // swap byte order - this assumes we are running on an x86-based chip //*(BYTE*)((DWORD)&wNewValue + 0) = *(BYTE*)((DWORD)&wValue + 1); //*(BYTE*)((DWORD)&wNewValue + 1) = *(BYTE*)((DWORD)&wValue + 0); *(((BYTE*)&wNewValue) + 0) = *(((BYTE*)&wValue) + 1); *(((BYTE*)&wNewValue) + 1) = *(((BYTE*)&wValue) + 0); return wNewValue;} DWORD NTSockets_Connect(struct NTSockets_SocketDataStruct* pSocketData, char* pIP, WORD wPort){ struct NTSockets_BindDataStruct NTSockets_BindData; struct NTSockets_ConnectDataStruct NTSockets_ConnectData; WORD wConnectPort = 0; DWORD dwConnectAddr = 0; // bind to local port 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) { // error return 1; } // read connection ip if (NTSockets_ConvertIP(pIP, &dwConnectAddr) != 0) { // error return 1; } // use network byte order for connection port // wConnectPort = htons(wPort); wConnectPort = NTSockets_Swap16BitByteOrder(wPort); // connect to remote port 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; ULONG sz = sizeof(NTSockets_ConnectData); if (NTSockets_SocketDriverMsg(pSocketData, 0x00012007, (BYTE*)&NTSockets_ConnectData, sz, NULL) != 0) { // error return 1; } return 0;} DWORD NTSockets_Send(struct NTSockets_SocketDataStruct* pSocketData, BYTE* pData, DWORD dwLength){ struct NTSockets_SendRecvDataStruct NTSockets_SendRecvData; struct NTSockets_DataBufferStruct NTSockets_DataBuffer; DWORD dwBytesSent = 0; BYTE* pCurrSendPtr = NULL; DWORD dwBytesRemaining = 0; // set initial values pCurrSendPtr = pData; dwBytesRemaining = dwLength; // send data for (;;) { if (dwBytesRemaining == 0) { // finished break; } // set data buffer values memset((void*)&NTSockets_DataBuffer, 0, sizeof(NTSockets_DataBuffer)); NTSockets_DataBuffer.dwDataLength = dwBytesRemaining; NTSockets_DataBuffer.pData = pCurrSendPtr; // send current block 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) { // error return 1; } if (dwBytesSent == 0) { // socket disconnected return 1; } // update values pCurrSendPtr += dwBytesSent; dwBytesRemaining -= dwBytesSent; } return 0;} int NTSockets_Recv(struct NTSockets_SocketDataStruct* pSocketData, BYTE* pData, DWORD dwLength){ struct NTSockets_SendRecvDataStruct NTSockets_SendRecvData; struct NTSockets_DataBufferStruct NTSockets_DataBuffer; DWORD dwBytesReceived = 0; // set data buffer values memset((void*)&NTSockets_DataBuffer, 0, sizeof(NTSockets_DataBuffer)); NTSockets_DataBuffer.dwDataLength = dwLength; NTSockets_DataBuffer.pData = pData; // recv current block 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) { // error return -1; } return dwBytesReceived;} DWORD NTSockets_CloseSocket(struct NTSockets_SocketDataStruct* pSocketData){ // close handles CloseHandle(pSocketData->hSocket); CloseHandle(pSocketData->hStatusEvent); return 0;} DWORD DNSClient_Query(char* pDNSClient_IP, char* pTargetHost, char* pOutput, DWORD dwOutputMaxLength){ struct NTSockets_SocketDataStruct SocketData; struct DNSClient_HeaderStruct DNSClient_RequestHeader; struct DNSClient_RequestQueryDetailsStruct DNSClient_RequestQueryDetails; struct DNSClient_HeaderStruct* pDNSClient_ResponseHeader = NULL; struct 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]; // convert target host name to dns format memset(szConvertedHost, 0, sizeof(szConvertedHost)); _snprintf_s(szConvertedHost, sizeof(szConvertedHost) - 1, -1, ".%s", pTargetHost); dwHostLength = strlen(szConvertedHost) + 1; for (DWORD i = 0; i < dwHostLength; i++) { // process domain labels if (szConvertedHost[i] == '.' || szConvertedHost[i] == '\0') { // check if a previous separator exists if (pCurrDot != NULL) { // calculate current label length dwCurrLabelLength = (DWORD)(&szConvertedHost[i] - pCurrDot); dwCurrLabelLength--; if (dwCurrLabelLength == 0 || dwCurrLabelLength >= 64) { return 1; } // insert label length *pCurrDot = (char)dwCurrLabelLength; } // store current dot position pCurrDot = &szConvertedHost[i]; } } // create socket handle if (NTSockets_CreateTcpSocket(&SocketData,1) != 0) { // error return 1; } // connect to DNS server if (NTSockets_Connect(&SocketData, pDNSClient_IP, 53) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // calculate request length wRequestLength = sizeof(struct DNSClient_HeaderStruct) + dwHostLength + sizeof(DNSClient_RequestQueryDetails); wBlockLength = NTSockets_Swap16BitByteOrder(wRequestLength); // set request header details 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); // type A dns request memset((void*)&DNSClient_RequestQueryDetails, 0, sizeof(DNSClient_RequestQueryDetails)); DNSClient_RequestQueryDetails.wType = NTSockets_Swap16BitByteOrder(1); DNSClient_RequestQueryDetails.wClass = NTSockets_Swap16BitByteOrder(1); // send request length if (NTSockets_Send(&SocketData, (BYTE*)&wBlockLength, sizeof(WORD)) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // send request header if (NTSockets_Send(&SocketData, (BYTE*)&DNSClient_RequestHeader, sizeof(DNSClient_RequestHeader)) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // send host name if (NTSockets_Send(&SocketData, (BYTE*)szConvertedHost, dwHostLength) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // send host query details if (NTSockets_Send(&SocketData, (BYTE*)&DNSClient_RequestQueryDetails, sizeof(DNSClient_RequestQueryDetails)) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // receive response length if (NTSockets_Recv(&SocketData, (BYTE*)&wBlockLength, sizeof(WORD)) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // swap byte order wResponseLength = NTSockets_Swap16BitByteOrder(wBlockLength); // validate response length if (wResponseLength < sizeof(struct DNSClient_HeaderStruct) || wResponseLength > sizeof(bResponseBuffer)) { // error NTSockets_CloseSocket(&SocketData); return 1; } // receive response data memset((void*)bResponseBuffer, 0, sizeof(bResponseBuffer)); if (NTSockets_Recv(&SocketData, bResponseBuffer, wResponseLength) != 0) { // error NTSockets_CloseSocket(&SocketData); return 1; } // set response header ptr pDNSClient_ResponseHeader = (struct DNSClient_HeaderStruct*)bResponseBuffer; // check flags (expect response, no error) if (pDNSClient_ResponseHeader->wFlags != NTSockets_Swap16BitByteOrder(0x8180)) { // error NTSockets_CloseSocket(&SocketData); return 1; } // validate question count if (pDNSClient_ResponseHeader->wQuestionCount != NTSockets_Swap16BitByteOrder(1)) { // error NTSockets_CloseSocket(&SocketData); return 1; } // get response answer count wAnswerCount = NTSockets_Swap16BitByteOrder(pDNSClient_ResponseHeader->wAnswerRecordCount); // read DNS response answers dwCurrAnswerEntryStartIndex = wRequestLength; for (DWORD i = 0; i < (DWORD)wAnswerCount; i++) { // validate start index if ((dwCurrAnswerEntryStartIndex + sizeof(struct DNSClient_ResponseAnswerHeaderStruct)) > (DWORD)wResponseLength) { // error NTSockets_CloseSocket(&SocketData); return 1; } // get current response answer header ptr pDNSClient_ResponseAnswerHeader = (struct DNSClient_ResponseAnswerHeaderStruct*)&bResponseBuffer[dwCurrAnswerEntryStartIndex]; // check if this is a type A record if (pDNSClient_ResponseAnswerHeader->wType == NTSockets_Swap16BitByteOrder(1) && pDNSClient_ResponseAnswerHeader->wClass == NTSockets_Swap16BitByteOrder(1)) { // ensure value length is 4 (ipv4 addr) if (pDNSClient_ResponseAnswerHeader->wLength != NTSockets_Swap16BitByteOrder(4)) { // error NTSockets_CloseSocket(&SocketData); return 1; } // validate ip addr index dwIpAddrIndex = dwCurrAnswerEntryStartIndex + sizeof(struct DNSClient_ResponseAnswerHeaderStruct); if ((dwIpAddrIndex + 4) > (DWORD)wResponseLength) { // error NTSockets_CloseSocket(&SocketData); return 1; } // store IP addr memcpy((void*)bIP, (void*)&bResponseBuffer[dwIpAddrIndex], 4); // set flag dwFoundRecord = 1; break; } else { // check next entry dwCurrAnswerEntryStartIndex += sizeof(struct DNSClient_ResponseAnswerHeaderStruct); dwCurrAnswerEntryStartIndex += NTSockets_Swap16BitByteOrder(pDNSClient_ResponseAnswerHeader->wLength); } } // close socket NTSockets_CloseSocket(&SocketData); // ensure a valid record was found if (dwFoundRecord == 0) { return 1; } // generate IP string memset(szIP, 0, sizeof(szIP)); _snprintf_s(szIP, sizeof(szIP) - 1, -1, "%u.%u.%u.%u", bIP[0], bIP[1], bIP[2], bIP[3]); // store value strncpy_s(pOutput,32, szIP, dwOutputMaxLength); return 0;} int main(int argc, char* argv[]){ char* szIP = "127.0.0.1"; unsigned int dwPort = 80; char buffer[1024] = "hello pediy.com !"; struct NTSockets_SocketDataStruct SocketData; // get NtDeviceIoControlFile function ptr NtDeviceIoControlFile = (unsigned long(__stdcall*)(void*, void*, void*, void*, struct IO_STATUS_BLOCK*, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtDeviceIoControlFile"); if (NtDeviceIoControlFile == NULL) { return 1; } // get NtCreateFile function 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(L"ntdll.dll"), "NtCreateFile"); if (NtCreateFile == NULL) { return 1; } // create socket handle printf("create socket !"); if (NTSockets_CreateTcpSocket(&SocketData,1) != 0) { // error printf("Error: Failed to create TCP socket"); return 1; } // connect to server printf("connect to server !"); if (NTSockets_Connect(&SocketData, szIP, (WORD)dwPort) != 0) { // error printf("Error: Failed to connect to server"); NTSockets_CloseSocket(&SocketData); return 1; } //send printf("send ... !"); if (NTSockets_Send(&SocketData, (BYTE*)buffer, strlen(buffer)) != 0) { // error printf("Error: Failed to send data to server"); NTSockets_CloseSocket(&SocketData); return 1; } //recv printf("recv ... !"); memset(buffer, 0, sizeof(buffer)); if (NTSockets_Recv(&SocketData, (BYTE*)buffer, sizeof(buffer) ) < 0) { // error printf("Error: Failed to read HTTP response header"); NTSockets_CloseSocket(&SocketData); return 1; } printf("buffer is: %s ",buffer); printf("close socket !"); NTSockets_CloseSocket(&SocketData); getchar(); return 0;}
vs完整工程見附件。
代碼測試

以上為UDP測試動畫。

以上為TCP測試動畫。
隨后附上TCP-Server和UDP-Server的代碼實現,敬請期待!