日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[安全攻防进阶篇] 七.恶意样本检测之编写代码自动提取IAT表、字符串及时间戳溯源

發布時間:2024/6/1 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [安全攻防进阶篇] 七.恶意样本检测之编写代码自动提取IAT表、字符串及时间戳溯源 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

系統安全繞不開PE文件,PE文件又與惡意樣本檢測及分析緊密相關。前文作者帶領大家逆向分析兩個CrackMe程序,包括逆向分析和源碼還原。這篇文章主要介紹了PE文件基礎知識及惡意樣本檢測的三種處理知識,手動編寫代碼實現了提取IAT表、二進制轉字符串及獲取PE文件時間戳,這是惡意樣本分析和溯源至關重要的基礎,并且網絡上還沒見到同時涵蓋這三個功能且詳細的文章,希望對您有所幫助。術路上哪有享樂,為了提升安全能力,別抱怨,干就對了~

同時,PE文件基礎知識推薦作者另一個安全系列:

  • [網絡安全自學篇] PE文件逆向之PE文件解析、PE編輯工具使用和PE結構修改
  • [網絡安全自學篇] PE文件逆向之數字簽名解析及Signcode、PEView、010Editor等工具用法
  • [網絡安全自學篇] Windows PE病毒原理、分類及感染方式詳解

從2019年7月開始,我來到了一個陌生的專業——網絡空間安全。初入安全領域,是非常痛苦和難受的,要學的東西太多、涉及面太廣,但好在自己通過分享100篇“網絡安全自學”系列文章,艱難前行著。感恩這一年相識、相知、相趣的安全大佬和朋友們,如果寫得不好或不足之處,還請大家海涵!

接下來我將開啟新的安全系列,叫“安全攻防進階篇”,也是免費的100篇文章,作者將更加深入的去研究惡意樣本分析、逆向分析、內網滲透、網絡攻防實戰等,也將通過在線筆記和實踐操作的形式分享與博友們學習,希望能與您一起進步,加油~

  • 推薦前文:網絡安全自學篇系列-100篇

話不多說,讓我們開始新的征程吧!您的點贊、評論、收藏將是對我最大的支持,感恩安全路上一路前行,如果有寫得不好或侵權的地方,可以聯系我刪除。基礎性文章,希望對您有所幫助,作者目的是與安全人共同進步,加油~

文章目錄

  • 一.PE文件
    • 1.PE文件基礎
    • 2.PE文件格式解析
  • 二.編寫代碼提取IAT表
  • 三.二進制PE文件轉字符串
  • 四.自動提取PE文件時間戳
  • 五.總結

作者的github資源:
軟件安全:https://github.com/eastmountyxz/Software-Security-Course
其他工具:https://github.com/eastmountyxz/NetworkSecuritySelf-study
Windows-Hacker:https://github.com/eastmountyxz/PE-InfoGet


聲明:本人堅決反對利用教學方法進行犯罪的行為,一切犯罪行為必將受到嚴懲,綠色網絡需要我們共同維護,更推薦大家了解它們背后的原理,更好地進行防護。(參考文獻見后)

前文回顧:
[安全攻防進階篇] 一.什么是逆向分析、逆向分析應用及經典掃雷游戲逆向
[安全攻防進階篇] 二.如何學好逆向分析、逆向路線推薦及呂布傳游戲逆向案例
[安全攻防進階篇] 三.OllyDbg和Cheat Engine工具逆向分析植物大戰僵尸游戲
[安全攻防進階篇] 四.逆向分析之條件語句和循環語句源碼還原及流程控制逆向
[安全攻防進階篇] 五.逆向分析之Win32 API獲取及加解密目錄文件、OllyDbg逆向其原理
[安全攻防進階篇] 六.逆向分析之OllyDbg逆向CrackMe01-02及加殼判斷



一.PE文件

1.PE文件基礎

什么是PE文件?
PE文件的全稱是Portable Executable,意為可移植的可執行的文件,常見的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微軟Windows操作系統上的程序文件(可能是間接被執行,如DLL)。

EXE文件格式:

  • DOS:MZ格式
  • WIndows 3.0/3.1:NE(New Executable)、16位Windows可執行文件格式

為什么要重點學習這種文件格式呢?

  • PE文件是可移植、可執行、跨Win32平臺的文件格式
  • 所有Win32執行體(exe、dll、kernel mode drivers)
  • 知道PE文件本質后,能更好進行惡意樣本分析、APT攻擊分析、勒索病毒分析
  • 了解軟件加密和加殼的思想,能夠PJ相關的PE文件
  • 它是您熟悉Windows操作系統的第一步,包括EXE程序怎么映射到內存,DLL怎么導入等
  • 軟件逆向工程的基本思想與PE文件格式息息相關
  • 如果您想成為一名黑客、系統安全工程師,那么精通PE文件是非常必要的

可執行程序是具有不同的形態的,比如用戶眼中的QQ如下圖所示。

本質上,QQ如下圖所示。


PE文件格式總體結構
接著讓我們來欣賞下PE文件格式總體結構圖,包括:MZ頭部、DOS stub、PE文件頭、可選文件頭、節表、節等。

本文的第二部分我們將對PE文件格式進行詳細解析。比如,MZ頭文件是定位PE文件頭開始位置,用于PE文件合法性檢測。DOS下運行該程序時,會提示用戶“This Program cannot be run in DOS mode”。

PE文件格式與惡意軟件的關系

  • 何為文件感染或控制權獲取?
    使目標PE文件具備或啟動病毒功能(或目標程序)
    不破壞目標PE文件原有功能和外在形態(如圖標)等
  • 病毒代碼如何與目標PE文件融為一體呢?
    代碼植入
    控制權獲取
    圖標更改
    Hook

PE文件解析常用工具包括:

  • PEView:可按照PE文件格式對目標文件的各字段進行詳細解析。
  • Stud_PE:可按照PE文件格式對目標文件的各字段進行詳細解析。
  • Ollydbg:可跟蹤目標程序的執行過程,屬于用戶態調試工具。
  • UltraEdit \ 010Editor:可對目標文件進行16進制查看和修改。


2.PE文件格式解析

我們通過010Editor觀察PE文件例子程序hello-2.5.exe的16進制數據,詳細講解PE文件格式。PE文件結構如下圖所示,推薦大家使用010Editor工具及其模板來進行PE文件分析。

MZ頭部+DOS stub+PE文件頭+可選文件頭+節表+節


(1) 使用010Editor工具打開PE文件,并運行模板。
該PE文件可分為若干結構,如下圖所示。


(2) MZ文件頭(000h-03fh)。
下圖為hello-2.5.exe的MZ文件頭,該部分固定大小為40H個字節。偏移3cH處字段Offset to New EXE Header,指示“NT映象頭的偏移地址”,其中000000B0是NT映象頭的文件偏移地址,定位PE文件頭開始位置,用于PE文件合法性檢驗。

000000B0指向PE文件頭開始位置。


(3) DOS插樁程序(040h-0afh)
DOS Stub部分大小不固定,位于MZ文件頭和NT映象頭之間,可由MZ文件頭中的Offset to New EXE Header字段確定。下圖為hello-2.5.exe中的該部分內容。


(4) PE文件頭(0b0h-1a7h)
該部分包括PE標識、映像文件頭、可選文件頭。

  • Signature:字串“PE\0\0”,4個字節(0b0H~0b4H)
  • 映象文件頭File Header:14H個字節(0b5H~0c7H)
    偏移2H處,字段Number of Section 給出節的個數(2個字節):0003
    偏移10H處,字段Size of Optional Header 給出可選映象頭的大小(2個字節):00E0
  • 可選映象頭Optional Header:0c8H~1a7H

對應解析如下圖所示,包括PE標識、X86架構、3個節、文件生成時間、COFF便宜、可選頭大小、文件信息標記等。

010Editor使用模板定位PE文件各節點信息。

PE文件可選文件頭224字節,其對應的字段信息如下所示:

typedef struct _IMAGE_OPTIONAL_HEADER {WORD Magic;                  /*機器型號,判斷是PE是32位還是64位*/BYTE MajorLinkerVersion;          /*連接器版本號高版本*/BYTE MinorLinkerVersion;          /*連接器版本號低版本,組合起來就是 5.12 其中5是高版本,C是低版本*/DWORD SizeOfCode;               /*代碼節的總大小(512為一個磁盤扇區)*/DWORD SizeOfInitializedData;        /*初始化數據的節的總大小,也就是.data*/DWORD SizeOfUninitializedData;       /*未初始化數據的節的大小,也就是 .data ? */DWORD AddressOfEntryPoint;         /*程序執行入口(OEP) RVA(相對偏移)*/DWORD BaseOfCode;               /*代碼的節的起始RVA(相對偏移)也就是代碼區的偏移,偏移+模塊首地址定位代碼區*/DWORD BaseOfData;               /*數據結的起始偏移(RVA),同上*/DWORD ImageBase;               /*程序的建議模塊基址(意思就是說作參考用的,模塊地址在哪里)*/DWORD SectionAlignment;           /*內存中的節對齊*/DWORD FileAlignment;             /*文件中的節對齊*/WORD MajorOperatingSystemVersion;    /*操作系統版本號高位*/WORD MinorOperatingSystemVersion;    /*操作系統版本號低位*/WORD MajorImageVersion;          /*PE版本號高位*/WORD MinorImageVersion;          /*PE版本號低位*/WORD MajorSubsystemVersion;        /*子系統版本號高位*/WORD MinorSubsystemVersion;        /*子系統版本號低位*/DWORD Win32VersionValue;          /*32位系統版本號值,注意只能修改為4 5 6表示操作系統支持nt4.0 以上,5的話依次類推*/DWORD SizeOfImage;             /*整個程序在內存中占用的空間(PE映尺寸)*/DWORD SizeOfHeaders;            /*所有頭(頭的結構體大小)+節表的大小*/DWORD CheckSum;               /*校驗和,對于驅動程序,可能會使用*/WORD Subsystem;              /*文件的子系統 :重要*/WORD DllCharacteristics;         /*DLL文件屬性,也可以成為特性,可能DLL文件可以當做驅動程序使用*/DWORD SizeOfStackReserve;         /*預留的棧的大小*/DWORD SizeOfStackCommit;          /*立即申請的棧的大小(分頁為單位)*/DWORD SizeOfHeapReserve;         /*預留的堆空間大小*/DWORD SizeOfHeapCommit;          /*立即申請的堆的空間的大小*/DWORD LoaderFlags;             /*與調試有關*/DWORD NumberOfRvaAndSizes;        /*下面的成員,數據目錄結構的項目數量*/IMAGE_DATA_DIRECTORY DataDirectory[16]; /*數據目錄,默認16個,16是宏,這里方便直接寫成16*/ } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

(5) 節表(1a8h-21fh)

  • 表項大小固定,28H個字節;表項個數由映象文件頭的字段Number of Section 給出。
  • 每個表項的起始位置起(8個字節),字段Name給出對應節的名稱。
  • 每個表項的偏移14H處(4個字節),字段Offset to Raw Data給出對應節的起始文件偏移。

該結構包括3個節,對應上圖的3個struct IMAGE_SECTION_HEADER,即“.test”、“.rdata”、“.data”節,其偏移地址對應下圖紫色區域,分別是400、600、800的位置。


(6) 3個節

  • 400H-5ffH:代碼節
  • 600H-7ffH:引入函數節
  • 800H-9ffH:數據節

注意,代碼節“.text”前46H為數據,后面全是0位填充值,為了實現文件的200H對齊,所以代碼節是400H到5ffH。


(7) 引入函數節
?來從其他DLL中引?函數,引入了kernel32.dll和user32.dll,這個節一般名為“.rdata”。引入函數是被某模塊調用的但又不在調用者模塊中的函數,用來從其他(系統或第三方寫的)DLL中引入函數,例如kernel32.dll、gdi32.dll等。

010Editor打開如下圖所示:

詳細標注信息如下圖所示:(圖引自HYQ同學,再此感謝)


(8) 數據節
數據節實際大小58h,對齊后大小200h,地址為800h-9ffh,包括對話框彈出的具體內容。



二.編寫代碼提取IAT表

IAT的全稱是Import Address Table,導入地址表。 IAT表是執行程序或者DLL為了實現動態加載和重定位函數地址,用到的一個導入函數地址表,這里面記錄了每個導入函數的名字和所在的DLL名稱。在PE加載的時候系統會加載這些DLL到用戶的地址空間然后把函數地址覆蓋這個表里的函數地址,然后重構所有用到這個表的代碼,讓其調用直接指向實際函數地址,PE的IAT表會留在內存,其中導入地址表就指示函數實際地址。


首先,我們通過Stud_PE軟件打開我們的hello-2.5.exe,發現它調用了兩個DLL和兩個函數。

  • kernel32.dll:ExitProcess
  • user32.dll:MessageBoxA

同樣的方法我們打開惡意樣本就可以發現它加載的DLL文件及IAT表內容,如下圖所示的網絡函數及進程、文件操作,包括對應的地址和名稱。


第二步,我們打開VS或VC++,新建工程添加main.cpp函數。編寫代碼如下,它將實現一個自動提取IAT表名稱的功能。

#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <Dbghelp.h>void ReadNTPEInfo(PIMAGE_NT_HEADERS pImageNtPE); ULONG RvaToOffset(IMAGE_NT_HEADERS* pNtHeader, ULONG Rva);#define pNtHeaders pImageNtHeadersint main() {//PE文件名稱char file[] = "hello-2.5.exe";char name[] = "test";//DOS頭PIMAGE_DOS_HEADER pImageDosHeader;//NT頭(包括PE標識+Image_File_Header+OptionHeader)PIMAGE_NT_HEADERS pImageNtHeaders;//標準PE頭、PIMAGE_FILE_HEADER pImageFileHeader;//擴展PE頭IMAGE_OPTIONAL_HEADER32 pImageOptionHeaders;HANDLE hFile;HANDLE hMapObject;//DOS頭PUCHAR uFileMap;hFile = CreateFile(file, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);if (hFile == NULL){printf("打開文件失敗\n");system("pause");return 0;}hMapObject = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);if (hMapObject == NULL){printf("創建文件映射內核對對象失敗\n");system("pause");return 0;}//PE基址uFileMap = (PUCHAR)MapViewOfFile(hMapObject, FILE_MAP_READ, 0, 0, 0);if (uFileMap == NULL){printf("映射到進程地址空間失敗\n");system("pause");return 0;}pImageDosHeader = (PIMAGE_DOS_HEADER)uFileMap;if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE){printf("不是PE結構\n");system("pause");return 0;}//定位到NT PE頭pImageNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)uFileMap + pImageDosHeader->e_lfanew);//導入表的相對虛擬地址(RVA)ULONG rva_ofimporttable = pImageNtHeaders->OptionalHeader.DataDirectory[1].VirtualAddress;//根據相對虛擬(rva)地址計算偏移地址(offset)ULONG offset_importtable = RvaToOffset(pImageNtHeaders, rva_ofimporttable);if (!offset_importtable){printf("獲取導入表偏移地址失敗\n");system("pause");return 0;}PIMAGE_THUNK_DATA s;//取得導入表的地址IMAGE_IMPORT_DESCRIPTOR* pImportTable = (IMAGE_IMPORT_DESCRIPTOR*)((char*)uFileMap + offset_importtable);IMAGE_IMPORT_DESCRIPTOR null_iid;IMAGE_THUNK_DATA null_thunk;memset(&null_iid, 0, sizeof(null_iid));memset(&null_thunk, 0, sizeof(null_thunk));//每個元素代表了一個引入的DLL。for (int i = 0; memcmp(pImportTable + i, &null_iid, sizeof(null_iid)) != 0; i++){//獲取DLL名稱char* dllName = (char*)(uFileMap + RvaToOffset(pImageNtHeaders, pImportTable[i].Name));printf("模塊[%d]: %s\n", i, (char*)dllName);printf("%s\n", (char*)dllName);PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)(uFileMap + RvaToOffset(pImageNtHeaders, pImportTable[i].FirstThunk));while (pThunk->u1.Ordinal != NULL){PIMAGE_IMPORT_BY_NAME pname = (PIMAGE_IMPORT_BY_NAME)(uFileMap + RvaToOffset(pImageNtHeaders, pThunk->u1.AddressOfData));printf("函數編號: %d 名稱: %s\n", pname->Hint, pname->Name);//文件名稱 DLL名稱 函數名稱 組織名稱//printf("%s,%s,%s,%s\n", file, (char*)dllName, pname->Name, name);pThunk++;}printf("\n");}system("pause");return 0; }//讀取PE文件信息 void ReadNTPEInfo(PIMAGE_NT_HEADERS pImageNtPE) {printf("運行平臺: 0x%04X\n", pImageNtPE->FileHeader.Machine);printf("節數量: %d\n", pImageNtPE->FileHeader.NumberOfSections);printf("PE屬性: 0x%04X\n", pImageNtPE->FileHeader.Characteristics); }//計算Offset ULONG RvaToOffset(IMAGE_NT_HEADERS* pNtHeader, ULONG Rva) {//PE節IMAGE_SECTION_HEADER* p_section_header;ULONG sNum, i;//取得節表項數目sNum = pNtHeader->FileHeader.NumberOfSections;//取得第一個節表項p_section_header = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader + sizeof(IMAGE_NT_HEADERS));for (i = 0; i < sNum; i++){//printf("PE 節名稱: %s\n",p_section_header->Name);if ((p_section_header->VirtualAddress <= Rva) && Rva < (p_section_header->VirtualAddress + p_section_header->SizeOfRawData)){return Rva - p_section_header->VirtualAddress + p_section_header->PointerToRawData;}p_section_header++;}return 0; }

輸出結果如下圖所示:


同樣的方法我們可以獲取惡意樣本的IAT表,如下圖所示:

模塊[0]: msvcrt.dll msvcrt.dll 函數編號: 221 名稱: _controlfp 函數編號: 158 名稱: __set_app_type 函數編號: 746 名稱: memcpy 函數編號: 138 名稱: __p__fmode 函數編號: 133 名稱: __p__commode 函數編號: 189 名稱: _adjust_fdiv 函數編號: 160 名稱: __setusermatherr 函數編號: 322 名稱: _initterm 函數編號: 170 名稱: __wgetmainargs 函數編號: 560 名稱: _wcmdln 函數編號: 668 名稱: exit 函數編號: 207 名稱: _cexit 函數編號: 79 名稱: _XcptFilter 函數編號: 253 名稱: _exit 函數編號: 204 名稱: _c_exit 函數編號: 740 名稱: malloc 函數編號: 244 名稱: _except_handler3 函數編號: 748 名稱: memset模塊[1]: urlmon.dll urlmon.dll 函數編號: 113 名稱: UrlMkGetSessionOption模塊[2]: WININET.dll WININET.dll 函數編號: 154 名稱: InternetOpenW 函數編號: 105 名稱: InternetCheckConnectionW 函數編號: 159 名稱: InternetReadFile 函數編號: 107 名稱: InternetCloseHandle 函數編號: 153 名稱: InternetOpenUrlW模塊[3]: KERNEL32.dll KERNEL32.dll 函數編號: 449 名稱: GetCurrentProcessId 函數編號: 453 名稱: GetCurrentThreadId 函數編號: 659 名稱: GetTickCount 函數編號: 935 名稱: QueryPerformanceCounter 函數編號: 1189 名稱: SetUnhandledExceptionFilter 函數編號: 1235 名稱: UnhandledExceptionFilter 函數編號: 1216 名稱: TerminateProcess 函數編號: 611 名稱: GetStartupInfoW 函數編號: 1273 名稱: WaitForSingleObject 函數編號: 181 名稱: CreateThread 函數編號: 448 名稱: GetCurrentProcess 函數編號: 1258 名稱: VirtualAllocEx 函數編號: 1202 名稱: Sleep 函數編號: 633 名稱: GetSystemTimeAsFileTime.

該部分代碼參考看雪SuperProgram師傅文章,非常感謝。

  • https://www.52pojie.cn/thread-549840-1-1.html

第三步,為什么要實現這個功能呢?其它工具不是都有類似的功能了。
首先,在線沙箱在分析惡意代碼時,它們也會從IAT表這個角度進行分析。其操作比較簡單,就是將惡意樣本上傳至指定在想網址即可。

  • virustotal沙箱:https://www.virustotal.com/
  • 360沙箱:https://ti.qianxin.com/
  • Cuckoo沙箱:https://cuckoo.cert.ee/
  • 微步沙箱:https://s.threatbook.cn/

我們以 virustotal沙箱為例,打開主頁如下圖所示,點擊“choose file”,上傳我們的勒索exe文件。

結果從72個在線引擎中掃描出4個是惡意樣本的引擎,如下圖所示:

我們可以看到該樣本的基本信息,包括MD5、SHA-1、文件歷史信息、PE文件節點信息。其中關注的重點是該文件的導入函數信息,在Imports中顯示,主要包括:

  • KERNEL32.dll
  • VCRUNTIME140D.dll
  • ucrtbased.dll

ucrtbased.dll主要包括的文件操作如下圖所示,比如fopen、fputc、system、rename等函數。

其次,當我們要分析海量樣本,從中提取其關聯性時,是需要編寫代碼實現自動提取特征,再進行分析的,所以本部分實現了一個C++代碼提取IAT表的技術,希望對您有所幫助。當獲取各個APT組織的函數調用信息,才能進一步挖掘它們的特征及習慣。



三.二進制PE文件轉字符串

下面分享如何將二進制文件轉換成十六進制,再轉換成字符串的過程。這里作者真心請教大家兩個問題:

  • 如果自動獲取PE文件中定義的字符串呢?
  • 如果自動轉換成對應的源代碼呢?
  • 如何自動提取每部分功能對應的核心代碼呢?

代碼如下:

import os import binascii#-----------------------------------定義轉換函數----------------------------------- def str_to_hex(s):return r"\x"+r'\x'.join([hex(ord(c)).replace('0x', '') for c in s])def hex_to_str(s):return ''.join([chr(i) for i in [int(b, 16) for b in s.split(r'\x')[1:]]])def str_to_bin(s):return ' '.join([bin(ord(c)).replace('0b', '') for c in s])def bin_to_str(s):return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])#--------------------------------二進制轉字節碼--------------------------------- fileIn = 'hello-2.5.exe' fileOut = 'hex-hello' inp = open(fileIn,'rb') outp = open(fileOut,'w')i = 0 for c in inp.read():outp.write('\\%#04x' %(c))i += 1if i >= 16:outp.write('\n')i = 0 inp.close() outp.close() print('二進制換十六進制成功\n')""" a="abcdefg" x=str_to_hex(a) print(x) print(hex_to_str(x)) """#--------------------------------字節碼轉換字符串-------------------------------- #decode():bytes編碼轉為str #encode():str編碼轉為bytes f = open('hex-hello', 'r') outp = open("result-hello.txt",'w', encoding="utf-8") for n in f.readlines():n = n.strip()txt = n.replace('\\0x','\\x')res = hex_to_str(txt)outp.write(res + '\n') outp.close() print('十六進制轉字符串成功\n')

如果我們直接打開一個EXE文件,發現它顯示如下圖所示的內容:

當我們轉換成16進制和字符串后,它變成了如下圖所示的內容。字符串勉強還能進行下一步和自然語言處理結合的分析,但更詳細的分析需要和PE文件結構結合。

如果我們使用IDA、010editor類似軟件打開,它能夠更清晰地對比各部分內容。


四.自動提取PE文件時間戳

接著我們嘗試通過Python來獲取時間戳,python的PE庫是pefile,它是用來專門解析PE文件的,可靜態分析PE文件。

第一步,我們通過010Editor分析PE文件。
其時間戳的輸出結果如下:

  • 06/19/2020 10:46:21

我們希望通過Python寫代碼實現自動化提取,為后續自動化溯源提供幫助。

第二步,撰寫Python代碼實現簡單分析。

import pefile import os,string,shutil,rePEfile_Path = "MFCApplication.exe"pe = pefile.PE(PEfile_Path) print(type(pe)) print(pe)

輸出如下圖所示結果,這是Python包自定義的PE結構。

squeezed text表示python的一種編程規范要求,簡稱pep8,你只需要將鼠標放到Squeezed上,右鍵Copy即可查看內容,顯示的是該PE文件的基本結構。如下所示,與010Editor分析的結果前后是一致的。

----------DOS_HEADER----------[IMAGE_DOS_HEADER] 0x0 0x0 e_magic: 0x5A4D 0x2 0x2 e_cblp: 0x90 0x4 0x4 e_cp: 0x3 0x6 0x6 e_crlc: 0x0 0x8 0x8 e_cparhdr: 0x4 0xA 0xA e_minalloc: 0x0 0xC 0xC e_maxalloc: 0xFFFF 0xE 0xE e_ss: 0x0 0x10 0x10 e_sp: 0xB8 0x12 0x12 e_csum: 0x0 0x14 0x14 e_ip: 0x0 0x16 0x16 e_cs: 0x0 0x18 0x18 e_lfarlc: 0x40 0x1A 0x1A e_ovno: 0x0 0x1C 0x1C e_res: 0x24 0x24 e_oemid: 0x0 0x26 0x26 e_oeminfo: 0x0 0x28 0x28 e_res2: 0x3C 0x3C e_lfanew: 0x108 ----------NT_HEADERS----------[IMAGE_NT_HEADERS] 0x108 0x0 Signature: 0x4550 ----------FILE_HEADER----------[IMAGE_FILE_HEADER] 0x10C 0x0 Machine: 0x14C 0x10E 0x2 NumberOfSections: 0xA 0x110 0x4 TimeDateStamp: 0x5EEC977D [Fri Jun 19 10:46:21 2020 UTC] 0x114 0x8 PointerToSymbolTable: 0x0 0x118 0xC NumberOfSymbols: 0x0 0x11C 0x10 SizeOfOptionalHeader: 0xE0 0x11E 0x12 Characteristics: 0x102 Flags: IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_EXECUTABLE_IMAGE----------OPTIONAL_HEADER----------[IMAGE_OPTIONAL_HEADER] 0x120 0x0 Magic: 0x10B 0x122 0x2 MajorLinkerVersion: 0xE 0x123 0x3 MinorLinkerVersion: 0x1A 0x124 0x4 SizeOfCode: 0x700C00 0x128 0x8 SizeOfInitializedData: 0x2F1E00 0x12C 0xC SizeOfUninitializedData: 0x0 0x130 0x10 AddressOfEntryPoint: 0x36CE65 0x134 0x14 BaseOfCode: 0x1000 0x138 0x18 BaseOfData: 0x1000 0x13C 0x1C ImageBase: 0x400000 ....----------PE Sections----------[IMAGE_SECTION_HEADER] 0x200 0x0 Name: .textbss 0x208 0x8 Misc: 0x35B30B 0x208 0x8 Misc_PhysicalAddress: 0x35B30B 0x208 0x8 Misc_VirtualSize: 0x35B30B 0x20C 0xC VirtualAddress: 0x1000 0x210 0x10 SizeOfRawData: 0x0 ....

第三步,注意這里同樣可以通過Python獲取IAT表相關信息。

import pefile import os,string,shutil,rePEfile_Path = "MFCApplication.exe"#解析PE文件 pe = pefile.PE(PEfile_Path) print(type(pe)) print(pe)#獲取導入表信息 for item in pe.DIRECTORY_ENTRY_IMPORT:print(item.dll)for con in item.imports:print(con.name)print("") #換行

輸出如下所示的結果,包括KERNEL32.dll、USER32.dll等。

b'KERNEL32.dll' b'RtlUnwind' b'GetModuleHandleExW' b'GetCommandLineA' b'GetSystemInfo' b'CreateThread' ...b'USER32.dll' b'DlgDirSelectExA' b'FindWindowExA' b'FindWindowA' b'SetParent' b'ChildWindowFromPointEx' ...

對應010editor的PE軟件分析結果如下:


第四步,獲取PE時間。通過pe.DOS_HEADER、pe.FILE_HEADER和正則表達式等方法獲取對應的內容。

import pefile import os,string,shutil,rePEfile_Path = "MFCApplication.exe"#解析PE文件 pe = pefile.PE(PEfile_Path, fast_load=True) print(type(pe)) print(pe) print(pe.get_imphash())#顯示DOS_HEADER dh = pe.DOS_HEADER#顯示NT_HEADERS nh = pe.NT_HEADERS#顯示FILE_HEADER fh = pe.FILE_HEADER#顯示OPTIONAL_HEADER oh = pe.OPTIONAL_HEADERprint(type(fh)) #<class 'pefile.Structure'> print(str(fh))#通過正則表達式獲取時間 p = re.compile(r'[[](.*?)[]]', re.I|re.S|re.M) #最小匹配 res = re.findall(p, str(fh)) print(res[1]) #第一個值是IMAGE_FILE_HEADER # Fri Jun 19 10:46:21 2020 UTC

最終輸出結果如下所示,這樣我們就完成了Python自動化提取PE軟件的時間戳過程。任何一個PE軟件都能進行提取,該時間戳也記錄了軟件的編譯時間。

<class 'pefile.PE'> Squeezed text(347 lines).<class 'pefile.Structure'> [IMAGE_FILE_HEADER] 0x10C 0x0 Machine: 0x14C 0x10E 0x2 NumberOfSections: 0xA 0x110 0x4 TimeDateStamp: 0x5EEC977D [Fri Jun 19 10:46:21 2020 UTC] 0x114 0x8 PointerToSymbolTable: 0x0 0x118 0xC NumberOfSymbols: 0x0 0x11C 0x10 SizeOfOptionalHeader: 0xE0 0x11E 0x12 Characteristics: 0x102 Fri Jun 19 10:46:21 2020 UTC

為什么要進行這樣的時間戳分析呢?
在過去的四年中,安天的工程師們關注到了中國的機構和用戶反復遭遇來自“西南方向”的網絡入侵嘗試。這些攻擊雖進行了一些掩蓋和偽裝,我們依然可以將其推理回原點——來自南亞次大陸的某個國家。他們是怎么做的呢?

安天通過對樣本集的時間戳、時區分析進行分析,發現其來自南亞。樣本時間戳是一個十六進制的數據,存儲在PE文件頭里,該值一般由編譯器在開發者創建可執行文件時自動生成,時間單位細化到秒,通常可以認為該值為樣本生成時間(GMT時間)。

時間戳的分析需要收集所有可用的可執行文件時間戳,并剔除過早的和明顯人為修改的時間,再將其根據特定標準分組統計,如每周的天或小時,并以圖形的形式體現,下圖是通過小時分組統計結果:

從上圖的統計結果來看,如果假設攻擊者的工作時間是早上八九點至下午五六點的話,那么將工作時間匹配到一個來自UTC+4或UTC+5時區的攻擊者的工作時間。根據我們匹配的攻擊者所在時區(UTC+4 或UTC+5),再對照世界時區分布圖,就可以來推斷攻擊者所在的區域或國家。

所以當我們受到持續攻擊,并且樣本存在相似性的時候,就可以通過這種方法簡單溯源其攻擊地區來源。當然該方法比較粗,您需要進一步結合樣本特征深入分析。

最終代碼:

import pefile import time import warnings import datetime import os,string,shutil,re#忽略警告 warnings.filterwarnings("ignore")PEfile_Path = "MFCApplication.exe"#----------------------------------第一步 解析PE文件------------------------------- pe = pefile.PE(PEfile_Path, fast_load=True) print(type(pe)) print(pe) print(pe.get_imphash())#顯示DOS_HEADER dh = pe.DOS_HEADER#顯示NT_HEADERS nh = pe.NT_HEADERS#顯示FILE_HEADER fh = pe.FILE_HEADER#顯示OPTIONAL_HEADER oh = pe.OPTIONAL_HEADERprint(type(fh)) #<class 'pefile.Structure'> print(str(fh))#----------------------------------第二步 獲取UTC時間------------------------------- #通過正則表達式獲取時間 p = re.compile(r'[[](.*?)[]]', re.I|re.S|re.M) #最小匹配 res = re.findall(p, str(fh)) print(res[1]) #第一個值是IMAGE_FILE_HEADER res_time = res[1].replace(" UTC","") # Fri Jun 19 10:46:21 2020 UTC#獲取當前時間 t = time.ctime() print(t,"\n") # Thu Jul 16 20:42:18 2020 utc_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') print("UTC Time:", utc_time) # 2020-06-19 10:46:21#----------------------------------第三步 全球時區轉換------------------------------- #http://zh.thetimenow.com/india #UTC時間比北京時間晚八個小時 故用timedelta方法加上八個小時 china_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=8) print("China Time:",china_time)#美國 UTC-5 america_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') - datetime.timedelta(hours=5) print("America Time:",america_time)#印度 UTC+5 india_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=5) print("India Time:",india_time)#澳大利亞 UTC+10 australia_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=10) print("Australia Time",australia_time)#俄羅斯 UTC+3 russia_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=3) print("Russia Time",russia_time)#英國 UTC+0 england_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') print("England Time",england_time)#日本 UTC+9 japan_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=9) print("Japan Time",england_time)#德國 UTC+1 germany_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=1) print("Germany Time",germany_time)#法國 UTC+1 france_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=1) print("France Time",france_time)#加拿大 UTC-5 canada_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') - datetime.timedelta(hours=5) print("Canada Time:",canada_time)#越南 UTC+7 vietnam_time = datetime.datetime.strptime(res_time, '%a %b %d %H:%M:%S %Y') + datetime.timedelta(hours=7) print("Vietnam Time:",vietnam_time)

輸出結果如下圖所示,不同地區有對應的時間分布,如果正常作息是早上9點到12點、下午2點到5點,從結果看更像是來自India、England、Japan等地區。當然,只有惡意樣本很多、持續攻擊的時候,單個樣本意義并不大,我們才能進行更好的溯源,哈哈~



五.總結

寫到這里,這篇文章就介紹完畢,這三個技術在惡意代碼溯源和分析中都非常普遍,希望對您有所幫助,最后進行簡單的總結下。

  • PE文件
    PE文件基礎
    PE文件格式解析
  • 編寫代碼提取IAT表
  • 二進制PE文件轉字符串
  • 自動提取PE文件時間戳

學安全一年,認識了很多安全大佬和朋友,希望大家一起進步。這篇文章中如果存在一些不足,還請海涵。作者作為網絡安全初學者的慢慢成長路吧!希望未來能更透徹撰寫相關文章。同時非常感謝參考文獻中的安全大佬們的文章分享,深知自己很菜,得努力前行。

有點想家和女神了!月是故鄉圓啊~接著加油。

編程沒有捷徑,逆向也沒有捷徑,它們都是搬磚活,少琢磨技巧,干就對了。什么時候你把攻擊對手按在地上摩擦,你就贏了,也會慢慢形成了自己的安全經驗和技巧。加油吧,少年希望這個路線對你有所幫助,共勉。

(By:Eastmount 2020-08-12 星期三 中午2點寫于武漢 http://blog.csdn.net/eastmount/ )



2020年8月18新開的“娜璋AI安全之家”,主要圍繞Python大數據分析、網絡空間安全、人工智能、Web滲透及攻防技術進行講解,同時分享CCF、SCI、南核北核論文的算法實現。娜璋之家會更加系統,并重構作者的所有文章,從零講解Python和安全,寫了近十年文章,真心想把自己所學所感所做分享出來,還請各位多多指教,真誠邀請您的關注!謝謝。

總結

以上是生活随笔為你收集整理的[安全攻防进阶篇] 七.恶意样本检测之编写代码自动提取IAT表、字符串及时间戳溯源的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。