自己動手製作一個惡意流量檢測系統(附源碼)

  • 2019 年 12 月 5 日
  • 筆記

0x0 成果展示

沒有做日誌記錄因為時間關係。

我們假設惡意C2C伺服器IP是220.181.38.148(百度的某個節點),某個木馬的惡意流量特徵是?? ?? ?? ??(? 匹配所有)

當我們要屏蔽220.181.38.148IP的時候 在我們後台輸入這個IP地址就可以屏蔽了,當然這裡只是展示了屏蔽功能,也可以做成給後台報警

這是沒有增加規則的:

增加了規則:

數據包同理,這裡的?? 代表匹配所有數據包。因此會把所有的數據包全部drop 掉

依然ping的通百度但是無法打開頁面(因為數據包都被drop了。

當然你也可以選擇記錄這些IP和數據去後台進行報警通知.只要木馬的流量特徵庫足夠大,

這裡說一下 很多DDOS防火牆也是這個道理,通過檢測數據包的特徵判斷是否有DDOS攻擊,一些AMP攻擊的流量特徵是固定的.

GitHub地址:https://github.com/huoji120/NetWatch

0x1 起因

本人是新手第一次接觸驅動開發的小白,事情是這樣的,一個星期前突發奇想想做一個威脅流量檢測系統.於是就有了今天這篇文章。

0x2 準備的東西

做過防火牆的同學應該熟悉在windows下普遍使用WFP或者NDIS這兩種驅動框架,這兩種框架各有各的優點,我自己總結為:

NDIS夠底層,能攔截所有的數據包和協議,而且兼容性好,覆蓋系統全面.但是NDIS因為太底層,很多功能無法實現.而且很多功能實現的方法不是很友好

WFP夠方便,Filte Engine很容易就能實現一個防火牆,簡單快捷.而且可以很方便的得到發包進程的詳細資訊.但是不是很底層,依賴於tcpip.sys,如果某些東西自己寫一個驅動去發包就無法抓取到.

NDIS防火牆安裝會斷網.這也是為什麼諸如安全狗這類軟體會斷網的原因

不過考慮到我放棄了WIN2003系統,所以我選擇了更加方便的WFP

WFP工作方式如圖:

0x3 驅動編寫

首先實現一個抓包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模板部分不單獨發了,沒什麼好說的,重點說一下我們要掛鉤的幾個地方:

FWPMLAYERALEAUTHCONNECTV4、FWPMLAYERALEAUTHRECVACCEPT_V4 分別對應鏈接認證階段階段,區別是一個是out一個是income

FWPMLAYERSTREAM_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);

其中 值得一提的是FWPMLAYERALECONNECTREDIRECT_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);      }  }  

FWPMLAYERALEAUTHRECVACCEPTV4 這裡是我之前程式碼,有個小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 = FWPACTIONBLOCK; 則丟掉這個數據包

匹配函數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;  }

至此.驅動部分編寫完畢.

0x4 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%08Xn", 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錯誤: %dn", GetLastError());          }          else          {              printf("提交規則成功 n");          }          closesocket(ClientSocket);          ClientSocket = accept(sockSrv, (SOCKADDR*)&addrClient, &len);      }      closesocket(sockSrv);      WSACleanup();        system("pause");      return 0;  }

0x5 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)

0x6 結論

在前人無數的踩坑文章下誕生的沒有的東西

0x7 後記

當今的安全企業很多都是在蹭政策(AkA 等保測評)去賣自家的產品賺錢,但自家的產品多半是從其他地方購買的東西或者是直接從github下載改造的東西。而且安全這個行業處於一個尷尬的地位,高壓政策讓進來的門檻變高,人員缺少,很多人覺得搞安全=搞黑客,去報培訓班"培訓",加上安全中的滲透這一塊入門門檻和其他程式語言相比和java差不多,而滲透的水平只在於是否花更多時間積累更多經驗。這些因素導致整個安全行業水分很大,有搞安全的公司領導根本不懂安全的、有安全公司直接拿開源產品改編然後去賣給甲方的;有整個公司就一個會搞安全的其他都是蹭飯的。這個時代的人們很浮躁,已經沒有多少人可以像以前的人們一樣能靜下心來研究技術了,而是樂於給自己打上標籤然後止步於這些自己立下的標籤之下。

*本文原創作者:huoji120,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載

Tags: