自己動手製作一個惡意流量檢測系統(附源碼)
摘要:} default: break。', 'text': '攔截全部流量(此規則不會生效)'}] HOST = 'localhost' PORT = 5099 BUFSIZ = 255 ADDR = (HOST, PORT) def push_backlist_data(data, data_len, block_ip, push_type): if push_type == 1: data = "huoji" data_len = len(data) c = socket(AF_INET, SOCK_STREAM) c.connect(ADDR) packet = struct.pack("IL255s", data_len, int(block_ip), data.encode('gbk'), ) c.send(packet) c.close() def Ip2Int(ipaddr): int_ip = struct.unpack('。
0×0 成果展示 0×1 起因 0×2 準備的東西 0×3 驅動編寫 0×4 Client編寫 0×5 Python Web後端編寫 0×6 結論 0×7 後記
0×0 成果展示
沒有做日誌記錄因爲時間關係。
我們假設惡意C2C服務器IP是220.181.38.148(百度的某個節點),某個木馬的惡意流量特徵是?? ?? ?? ??(? 匹配所有)
當我們要屏蔽220.181.38.148IP的時候 在我們後臺輸入這個IP地址就可以屏蔽了,當然這裏只是展示了屏蔽功能,也可以做成給後臺報警
這是沒有增加規則的:
增加了規則:
數據包同理,這裏的?? 代表匹配所有數據包。因此會把所有的數據包全部drop 掉
依然ping的通百度但是無法打開頁面(因爲數據包都被drop了。
當然你也可以選擇記錄這些IP和數據去後臺進行報警通知.只要木馬的流量特徵庫足夠大,
這裏說一下 很多DDOS防火牆也是這個道理,通過檢測數據包的特徵判斷是否有DDOS攻擊,一些AMP攻擊的流量特徵是固定的.
GitHub地址: https://github.com/huoji120/NetWatch
0×1 起因
本人是新手第一次接觸驅動開發的小白,事情是這樣的,一個星期前突發奇想想做一個威脅流量檢測系統.於是就有了今天這篇文章。
0×2 準備的東西
做過防火牆的同學應該熟悉在windows下普遍使用WFP或者NDIS這兩種驅動框架,這兩種框架各有各的優點,我自己總結爲:
NDIS夠底層,能攔截所有的數據包和協議,而且兼容性好,覆蓋系統全面.但是NDIS因爲太底層,很多功能無法實現.而且很多功能實現的方法不是很友好
WFP夠方便,Filte Engine很容易就能實現一個防火牆,簡單快捷.而且可以很方便的得到發包進程的詳細信息.但是不是很底層,依賴於tcpip.sys,如果某些東西自己寫一個驅動去發包就無法抓取到.
NDIS防火牆安裝會斷網.這也是爲什麼諸如安全狗這類軟件會斷網的原因
不過考慮到我放棄了WIN2003系統,所以我選擇了更加方便的WFP
WFP工作方式如圖:
0×3 驅動編寫
首先實現一個抓包demo:
R3部分建立pipe管道
enum ReportType { r_income, //進來auth r_output, //出去auth r_stream_income, //TCP流交互 r_stream_output }; enum Protocoltype { Pro_ICMP = 1, Pro_IGMP = 2, Pro_TCP = 6, Pro_UDP = 17, Pro_RDP = 27, Pro_UNKNOWN }; struct Networkreport { ReportType type; Protocoltype Protocol; DWORD IPaddr; DWORD BuffDataLen; char* BuffData; }; std::string GetSigHex(char* data, int len) { char buf[0xFFFF] = { 0 }; for (int i = 0; i < len; i++) { char test[8] = { 0 }; if (i == len - 1) { sprintf_s(test, "%02X", (BYTE)data[i]); strcat_s(buf, test); } else { sprintf_s(test, "%02X ", (BYTE)data[i]); strcat_s(buf, test); } } return std::string(buf); } int main() { HANDLE hPipe = CreateNamedPipe( TEXT("\\\\.\\Pipe\\EzFireWall"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, NULL); if (INVALID_HANDLE_VALUE == hPipe) return false; std::cout << "創建管道完畢,監聽程序...\n"; const int size = 1024 * 10; char buf[size]; DWORD rlen = 0; while (true) { if (ConnectNamedPipe(hPipe, NULL) != NULL) { if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE) continue; else { //接收信息 Networkreport* buffer_tmp = (Networkreport*)&buf; SIZE_T buffer_len = sizeof(Networkreport) + buffer_tmp->BuffDataLen; Networkreport* buffer = (Networkreport*)malloc(buffer_len); memcpy(buffer, buffer_tmp, buffer_len); char* data = (char*)malloc(buffer->BuffDataLen); BYTE* tmp = (BYTE*)buffer + sizeof(Networkreport); memcpy(data, tmp, buffer->BuffDataLen); DWORD RemoteIP = buffer->IPaddr; printf("遠程IP:%u.%u.%u.%u 協議類型: %d 數據類型: %d 長度: %d \n", (RemoteIP >> 24) & 0xFF, (RemoteIP >> 16) & 0xFF, (RemoteIP >> 8) & 0xFF, RemoteIP & 0xFF, buffer->Protocol, buffer->type, buffer->BuffDataLen); if (buffer->type == r_stream_income || buffer->type == r_stream_output) { printf("數據: %s \n", GetSigHex(data,buffer->BuffDataLen).c_str()); } free(data); free(buffer); } } } std::cout << "出現錯誤 \n"; system("pause"); return 0; }
在驅動entry部分連接pipe管道:
HANDLE g_hClient; IO_STATUS_BLOCK g_ioStatusBlock; KEVENT g_event; VOID ReportToR3(Networkreport* m_parameter,int lent) { if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL))) DPRINT("[DebugMessage] Error Cannot Wirte Pipe! \n"), g_hClient = 0; }
RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\EzFireWall"); InitializeObjectAttributes(&objAttr, &uniName,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL, NULL); Status = ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!NT_SUCCESS(Status) || !g_hClient) { DPRINT("[DebugMessage] Cannot pipe Fail! 0x%08X \n", Status); return Status; } DPRINT("[DebugMessage] Connect pipe Success!\n"); KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);
然後WFP模板部分不單獨發了,沒什麼好說的,重點說一下我們要掛鉤的幾個地方:
FWPM_LAYER_ALE_AUTH_CONNECT_V4、FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 分別對應鏈接認證階段階段,區別是一個是out一個是income
FWPM_LAYER_STREAM_V4 抓取數據流,用於流量信息監測
if (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V4)) { DPRINT("[DebugMessage] 掛載 FWPM_LAYER_ALE_AUTH_CONNECT_V4! \n"); sCallout.classifyFn = SFALEConnectClassify; //我們的回調 sCallout.notifyFn = SFALEConnectNotify; } else if (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4)) { DPRINT("[DebugMessage] 掛載 FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4! \n"); sCallout.classifyFn = SFALERecvAcceptClassify; //我們的回調 sCallout.notifyFn = SFALERecvAcceptNotify; } else if (IsEqualGUID(layerKey, &FWPM_LAYER_STREAM_V4)) { DPRINT("[DebugMessage] 掛載 FWPM_LAYER_STREAM_V4! \n"); sCallout.classifyFn = SFALERecvDataClassify; //我們的回調 sCallout.notifyFn = SFALERecvDataNotify; } Status = FwpsCalloutRegister0(DeviceObject, &sCallout, calloutId);
其中 值得一提的是FWPM_LAYER_ALE_CONNECT_REDIRECT_V4,可以改變連接的地址,實現偷樑換柱功能.非常適合拿去幹壞事.不過不在本次的討論範圍內
在SFALEConnectClassify和SFALERecvAcceptClassify回調中,負責屏蔽黑名單IP,這部分稍微抄了一下前輩的代碼:
//本地連別人的IP的連接 void SFALEConnectClassify( __in const FWPS_INCOMING_VALUES0* inFixedValues, __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, __inout void* layerData, __in const FWPS_FILTER0* filter, __in UINT64 flowContext, __in FWPS_CLASSIFY_OUT0* classifyOut ) { if (classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) { FWP_ACTION_TYPE Action = (CanIFilterThisRequest(inFixedValues,inMetaValues, layerData,flowContext) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT); PerformBasicAction(inFixedValues, inMetaValues, layerData, filter, flowContext, classifyOut, Action); } } //接收遠程IP的連接 void SFALERecvAcceptClassify( __in const FWPS_INCOMING_VALUES0* inFixedValues, __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, __inout void* layerData, __in const FWPS_FILTER0* filter, __in UINT64 flowContext, __inout FWPS_CLASSIFY_OUT0* classifyOut ) { if (classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) { FWP_ACTION_TYPE Action = (CanIFilterThisRequest(inFixedValues,inMetaValues, layerData, flowContext) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT); PerformBasicAction(inFixedValues, inMetaValues, layerData, filter, flowContext, classifyOut, Action); } }
FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 這裏是我之前代碼,有個小bug,至於是什麼bug大佬應該一眼看出來了:
void SFALERecvDataClassify( __in const FWPS_INCOMING_VALUES0* inFixedValues, __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, __inout void* layerData,//這玩意是數據指針 __in const FWPS_FILTER0* filter, __in UINT64 flowContext, __inout FWPS_CLASSIFY_OUT0* classifyOut ) { if (KeGetCurrentIrql() > DISPATCH_LEVEL) { DPRINT("[DebugMessage] Erro in PassIve: %d \n", KeGetCurrentIrql()); return FALSE; } FWPS_STREAM_CALLOUT_IO_PACKET* streamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; DWORD RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; DWORD LocalIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; if (streamPacket && streamPacket->streamData != NULL && streamPacket->streamData->dataLength != 0 && RemoteIP != LocalIP && g_StartFilter) { SIZE_T streamLength = streamPacket->streamData->dataLength; BOOLEAN inbound = (BOOLEAN)((streamPacket->streamData->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE); BYTE* stream = ExAllocatePoolWithTag(NonPagedPool, streamLength, TAG_NAME_NOTIFY); SIZE_T byte_copied = 0; if (stream) { RtlZeroMemory(stream, streamLength); FwpsCopyStreamDataToBuffer( streamPacket->streamData, stream, streamLength, &byte_copied); NT_ASSERT(bytesCopied == streamLength); DPRINT("[DebugMessage] inbound: %d streamBuffer: %s ", inbound, stream); SIZE_T buffsize = streamLength + sizeof(Networkreport); inbound = (BOOLEAN)((streamPacket->streamData->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE); Networkreport* report = (Networkreport*)ExAllocatePoolWithTag(NonPagedPool, buffsize, TAG_NAME_REPORT); if (report) { RtlZeroMemory(report, buffsize); report->type = inbound ? r_stream_income : r_stream_output; report->Protocol = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16; report->IPaddr = RemoteIP; report->BuffDataLen = streamLength; //定位到buffer的sizeof(Networkreport)位置 BYTE* tmp = (BYTE*)report + sizeof(Networkreport); memcpy(tmp, stream, streamLength); ReportToR3(report, buffsize); ExFreePool(report); } ExFreePool(stream); } } classifyOut->actionType = FWP_ACTION_CONTINUE; }
結果:
好的可以抓包了就可以開始進行下一步過濾了:
首先是提交過濾的程序,這裏偷懶了:
//添加數據到 到黑名單數據列表 VOID AddBlackListData(char* data,DWORD blockip,SIZE_T len) { if (blockip == 0x0) { DPRINT("[DebugMessage] BlackData :%s len: %d \n", data, len); PBLACK_LIST_DATA newLink = (PBLACK_LIST_DATA)ExAllocatePoolWithTag(PagedPool, sizeof(BLACK_LIST_DATA), TAG_NAME_BLACKLISTDATA); if (newLink == NULL) ASSERT(false); //RtlZeroMemory(newLink, sizeof(BLACK_LIST_DATA)); memcpy(newLink->data, data, len); DPRINT("[DebugMessage] BlackData :%s \n", newLink->data); InsertTailList(&gBackListDataTable.link, (PLIST_ENTRY)newLink); } else { for (int i = 0; i < MAX_DATA_SIZE; i++) { if (gBackListIPTable[i] == 0) { gBackListIPTable[i] = blockip; DPRINT("[DebugMessage] BlackIP :0x%08X \n", gBackListIPTable[i]); break; } } } }
控制碼:
NTSTATUS DriverControlHandler( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION irpSp;// Pointer to current stack location NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;// Assume success ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PUCHAR inBuf, outBuf; UNREFERENCED_PARAMETER(DeviceObject); irpSp = IoGetCurrentIrpStackLocation(Irp); inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; inBuf = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; outBuf = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; DPRINT("[DebugMessage] DriverControlHandler: inBufLength: %d outBufLength: %d \n", inBufLength, outBufLength); if (!inBufLength || !outBufLength) { ntStatus = STATUS_INVALID_PARAMETER; goto End; } switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_ADD_BLACKLIST_DATA: { DPRINT("[DebugMessage] Add BlackList Data! \n"); PPUSH_DATA push_data = (PPUSH_DATA)ExAllocatePoolWithTag(PagedPool, sizeof(PUSH_DATA), "tM2d"); if (push_data) { RtlZeroMemory(push_data, sizeof(PUSH_DATA)); memcpy(push_data, inBuf, inBufLength); AddBlackListData(push_data->data, push_data->BlockIP, push_data->dataLen); DPRINT("[DebugMessage] BlockIP: 0x%08X data: %s len: %d \n", push_data->BlockIP, push_data->data, push_data->dataLen); g_StartFilter = TRUE; ntStatus = STATUS_SUCCESS; ExFreePoolWithTag(push_data, "tM2d"); } else { ntStatus = STATUS_INVALID_PARAMETER; } break; } default: break; } End: Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return ntStatus; }
黑名單IP匹配:
//黑名單IP匹配 BOOLEAN QueryBlackIP(DWORD ipaddr) { KIRQL Irql = ExAcquireSpinLockExclusive(&gBlockIpLock); BOOLEAN result = FALSE; for (int i = 0; i < MAX_DATA_SIZE; i++) { if (gBackListIPTable[i] == ipaddr) { result = TRUE; break; } } ExReleaseSpinLockExclusive(&gBlockIpLock, Irql); return result; }
之後在SFALEConnectClassify和SFALERecvAcceptClassify的地方進行驗證:
BOOLEAN CanIFilterThisRequest( __in const FWPS_INCOMING_VALUES0* inFixedValues, __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, __in void* packet, _In_ UINT64 flowContext ) { UNREFERENCED_PARAMETER(inMetaValues); if (KeGetCurrentIrql() > DISPATCH_LEVEL) { DPRINT("[DebugMessage] Erro in PassIve: %d \n", KeGetCurrentIrql()); return FALSE; } if (g_StartFilter) { DWORD LocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; DWORD RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; if (LocalIp != RemoteIP) { if (QueryBlackIP(RemoteIP)) { DPRINT("[DebugMessage] Found BlackList IP! \n"); return TRUE; } } } //這邊可以阻止黑名單IP進入. /* char* ProtocolName = ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16); DWORD LocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; DWORD RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; DPRINT("[DebugMessage] Out: ProtocolName: %s Local: %u.%u.%u.%u:%d Remote:%u.%u.%u.%u:%d Protocol: %s \n", (LocalIp >> 24) & 0xFF, (LocalIp >> 16) & 0xFF, (LocalIp >> 8) & 0xFF, LocalIp & 0xFF, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16, (RemoteIP >> 24) & 0xFF, (RemoteIP >> 16) & 0xFF, (RemoteIP >> 8) & 0xFF, RemoteIP & 0xFF, inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16, ProtocolName); ExFreePool(ProtocolName); */ return FALSE; }
CanIFilterThisRequest 返回TRUE後 在SFALEConnectClassify和SFALERecvAcceptClassify中阻止鏈接:
FWP_ACTION_TYPE Action = (CanIFilterThisRequest(inFixedValues,inMetaValues, layerData,flowContext) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT); PerformBasicAction(inFixedValues,inMetaValues,layerData,filter,flowContext,classifyOut,Action);
然後是數據包過濾:
//黑名單數據匹配 BOOLEAN QueryBlackListData(char* data, SIZE_T len) { KIRQL Irql = ExAcquireSpinLockExclusive(&gBlockDataLock); BOOLEAN result = FALSE; PLIST_ENTRY head = &gBackListDataTable.link; PBLACK_LIST_DATA next = (PBLACK_LIST_DATA)gBackListDataTable.link.Blink; while (head != (PLIST_ENTRY)next) { if(FindPattern(next->data, data, len)) { result = TRUE; break; } } ExReleaseSpinLockExclusive(&gBlockDataLock, Irql); return result; }
void SFALERecvDataClassify( __in const FWPS_INCOMING_VALUES0* inFixedValues, __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, __inout void* layerData,//這玩意是數據指針 __in const FWPS_FILTER0* filter, __in UINT64 flowContext, __inout FWPS_CLASSIFY_OUT0* classifyOut ) { if (KeGetCurrentIrql() > DISPATCH_LEVEL) { DPRINT("[DebugMessage] Erro in PassIve: %d \n", KeGetCurrentIrql()); return FALSE; } FWPS_STREAM_CALLOUT_IO_PACKET* streamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; DWORD RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32; DWORD LocalIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32; if (streamPacket && streamPacket->streamData != NULL && streamPacket->streamData->dataLength != 0 && RemoteIP != LocalIP && g_StartFilter) { SIZE_T streamLength = streamPacket->streamData->dataLength; BOOLEAN inbound = (BOOLEAN)((streamPacket->streamData->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE); BYTE* stream = ExAllocatePoolWithTag(NonPagedPool, streamLength, TAG_NAME_NOTIFY); SIZE_T byte_copied = 0; if (stream) { RtlZeroMemory(stream, streamLength); FwpsCopyStreamDataToBuffer( streamPacket->streamData, stream, streamLength, &byte_copied); NT_ASSERT(bytesCopied == streamLength); if (QueryBlackListData(stream, streamLength)) { DPRINT("[DebugMessage] Found BlackList Data! \n"); classifyOut->actionType = FWP_ACTION_BLOCK; ExFreePool(stream); return; } /* //抓包與截包,如果你發現這裏藍屏請自己加鎖,但是會極大的影響系統運行效率(網絡吞吐量太大,pipe管道有20MS的延遲,而且還是單線程.傷不起 DPRINT("[DebugMessage] inbound: %d streamBuffer: %s ", inbound, stream); SIZE_T buffsize = streamLength + sizeof(Networkreport); inbound = (BOOLEAN)((streamPacket->streamData->flags & FWPS_STREAM_FLAG_RECEIVE) == FWPS_STREAM_FLAG_RECEIVE); Networkreport* report = (Networkreport*)ExAllocatePoolWithTag(NonPagedPool, buffsize, TAG_NAME_REPORT); if (report) { RtlZeroMemory(report, buffsize); report->type = inbound ? r_stream_income : r_stream_output; report->Protocol = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16; report->IPaddr = RemoteIP; report->BuffDataLen = streamLength; //定位到buffer的sizeof(Networkreport)位置 BYTE* tmp = (BYTE*)report + sizeof(Networkreport); memcpy(tmp, stream, streamLength); ReportToR3(report, buffsize); ExFreePool(report); }*/ ExFreePool(stream); } } classifyOut->actionType = FWP_ACTION_CONTINUE; }
匹配成功後 classifyOut->actionType = FWP_ACTION_BLOCK; 則丟掉這個數據包
匹配函數FindPattern
BOOL FindPattern(char* pattern,void* data,SIZE_T data_len) { const char* pat = pattern; DWORD firstMatch = 0; DWORD End = (DWORD)data + data_len; for (DWORD pCur = (DWORD)data; pCur < End; pCur++) { if (!*pat) return firstMatch; if (*(PBYTE)pat == '\?' || *(BYTE*)pCur == getByte(pat)) { if (!firstMatch) firstMatch = pCur; if (!pat[2]) return firstMatch; if (*(PWORD)pat == '\?\?' || *(PBYTE)pat != '\?') pat += 3; else pat += 2; //one ? } else { pat = pattern; firstMatch = 0; } } return firstMatch != NULL; }
至此.驅動部分編寫完畢.
0×4 Client編寫
這裏有個失誤,不應該使用Client作爲TCP服務端,應該是Python來做服務端,無所謂了.先能用再說:
#define IOCTL_ADD_BLACKLIST_DATA \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1337, METHOD_IN_DIRECT, FILE_ANY_ACCESS) typedef NTSTATUS(WINAPI* NtOpenFileEx)( _Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK IoStatusBlock, _In_ ULONG ShareAccess, _In_ ULONG OpenOptions ); struct Networkstruct { int data_len; DWORD IP; char data[255]; }; typedef struct _PUSH_DATA { DWORD BlockIP; SIZE_T dataLen; char data[255]; }PUSH_DATA, * PPUSH_DATA; NtOpenFileEx fpNtOpenFile = (NtOpenFileEx)GetProcAddress(GetModuleHandleA("ntdll"), "NtOpenFile"); HANDLE deviceHandle_; bool is_loaded() { if (!deviceHandle_ || deviceHandle_ == INVALID_HANDLE_VALUE) { //deviceHandle_ = CreateFile(L"C:\\windows\\TEMP\\cpuz147\\cpuz145_x64.sys", FILE_ALL_ACCESS, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); IO_STATUS_BLOCK io_status; NTSTATUS status; UNICODE_STRING device_name = UNICODE_STRING{ sizeof(DEVICE_NAME) - sizeof(WCHAR), sizeof(DEVICE_NAME), (PWSTR)DEVICE_NAME }; OBJECT_ATTRIBUTES obj_attr = OBJECT_ATTRIBUTES{ sizeof(OBJECT_ATTRIBUTES), nullptr, &device_name, 0, nullptr, nullptr }; status = fpNtOpenFile( &deviceHandle_, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &obj_attr, &io_status, 0, OPEN_EXISTING); if (!NT_SUCCESS(status)) { ULONG i = 10; do { status = fpNtOpenFile( &deviceHandle_, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &obj_attr, &io_status, 0, OPEN_EXISTING); Sleep(250); } while (!NT_SUCCESS(status) && i--); } } return deviceHandle_ && deviceHandle_ != INVALID_HANDLE_VALUE; } int main() { if (!is_loaded()) { printf("加載驅動失敗! %d \n", GetLastError()); system("pause"); return 0; } WSADATA wsaData; SOCKET ClientSocket; int port = 5099; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return false; } SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN addrSrv; addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(port); //1024以上的端口號 addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN)); if (retVal == SOCKET_ERROR) { return false; } if (listen(sockSrv, 10) == SOCKET_ERROR) { return false; } SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); static bool first = false; ClientSocket = accept(sockSrv, (SOCKADDR*)&addrClient, &len); if (ClientSocket == SOCKET_ERROR) { return false; } while (true) { char recvBuf[255]; memset(recvBuf, 0, sizeof(recvBuf)); if (recv(ClientSocket, recvBuf, sizeof(recvBuf), 0) == 0 || ClientSocket == INVALID_SOCKET) { first = false; closesocket(ClientSocket); ClientSocket = accept(sockSrv, (SOCKADDR*)&addrClient, &len); continue; } Networkstruct* buffer = (Networkstruct*)recvBuf; //1說明是ip 2說明是data char output; DWORD returnLen, read; PUSH_DATA data = { 0 }; data.BlockIP = buffer->IP; data.dataLen = buffer->data_len; printf("buffer->data %s \n", buffer->data); memcpy(data.data, buffer->data,buffer->data_len); printf("buffer len: %d data: %s IP: 0x%08X\n", data.dataLen, data.data, data.BlockIP); if (!DeviceIoControl(deviceHandle_, IOCTL_ADD_BLACKLIST_DATA, (LPVOID)&data, sizeof(PUSH_DATA), &output, sizeof(char), &returnLen, NULL)) { printf("DeviceIoControl錯誤: %d\n", GetLastError()); } else { printf("提交規則成功 \n"); } closesocket(ClientSocket); ClientSocket = accept(sockSrv, (SOCKADDR*)&addrClient, &len); } closesocket(sockSrv); WSACleanup(); system("pause"); return 0; }
0×5 Python web後臺編寫
非常簡單的代碼,基於Flask和Jquery與semanticUI
from flask import Flask, request from flask import render_template from binascii import hexlify from socket import inet_aton, socket, AF_INET, SOCK_STREAM import ctypes import json import struct import sys app = Flask(__name__) ip_blacklist = [{'data': '192.168.1.1', 'text': '測試路由器地址(此規則不會生效)'}] data_blacklist = [{'data': '?? ?? ?? ??', 'text': '攔截全部流量(此規則不會生效)'}] HOST = 'localhost' PORT = 5099 BUFSIZ = 255 ADDR = (HOST, PORT) def push_backlist_data(data, data_len, block_ip, push_type): if push_type == 1: data = "huoji" data_len = len(data) c = socket(AF_INET, SOCK_STREAM) c.connect(ADDR) packet = struct.pack("IL255s", data_len, int(block_ip), data.encode('gbk'), ) c.send(packet) c.close() def Ip2Int(ipaddr): int_ip = struct.unpack('!I', inet_aton(ipaddr))[0] print('IP:' + str(int_ip)) return int_ip @app.route('/') def index(): context = ip_blacklist context_type = { 'table_type1': "IP地址", 'table_type2': "備註" } return render_template('index.html', main=context, type=context_type) @app.route('/api', methods=["POST", "GET"]) def api(): if request.method == 'POST': data = request.form.get("data") page = request.form.get("page") push = request.form.get("push") push_data = request.form.get("push_data") if data: # push_backlist_data(data, len(data)) return json.dumps({"status": "success"}) if page: json_return = {"status": "Error"} if int(page) == 1: json_return = { "status": "success", "page": "ip_list", "table_1": "IP地址", "table_2": "備註", "data": ip_blacklist } if int(page) == 2: json_return = { "status": "success", "page": "data_list", "table_1": "特徵碼", "table_2": "備註", "data": data_blacklist } return json.dumps(json_return) if push and push_data: json_return = {"status": "Error"} processdata = push_data.split("@") if int(push) == 1: ip_blacklist.append({ "data": processdata[0], "text": processdata[1] }) else: data_blacklist.append({ "data": processdata[0], "text": processdata[1] }) blockip = 0 if int(push) == 1: blockip = Ip2Int(processdata[0]) print("type " + push + " blockip: " + str(blockip)) push_backlist_data( processdata[0], len(processdata[0]), blockip, int(push) ) return json.dumps(json_return) return "API" if __name__ == '__main__': app.run(debug=True)
0×6 結論
在前人無數的踩坑文章下誕生的沒有的東西
0×7 後記
當今的安全企業很多都是在蹭政策(AkA 等保測評)去賣自家的產品賺錢,但自家的產品多半是從其他地方購買的東西或者是直接從github下載改造的東西.而且安全這個行業處於一個尷尬的地位,高壓政策讓進來的門檻變高,人員缺少,很多人覺得搞安全 = 搞黑客,去報培訓班”培訓” ,加上安全中的滲透這一塊入門門檻和其他編程語言相比和java差不多,而滲透的水平只在於是否花更多時間積累更多經驗.這些因素導致整個安全行業水分很大.有搞安全的公司領導根本不懂安全的、有安全公司直接拿開源產品改編然後去賣給甲方的.有整個公司就一個會搞安全的其他都是蹭飯的.這個時代的人們很浮躁,已經沒有多少人可以像以前的人們一樣能靜下心來研究技術了.而是樂於給自己打上標籤然後止步於這些自己立下的標籤之下.
*本文原創作者:huoji120,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載