1.分析感染后的可執行程序
(1)確定感染后程序包含哪幾個節,每個節的訪問屬性如何;
訪問屬性參考文檔
來源於PE Explorer
的幫助文檔
The last member of a section header is the 32 bits 'Characteristics', which is a bunch of flags describing how the section's memory should be treated:
If bit 5 (IMAGE_SCN_CNT_CODE) is set, the section contains executable code.
If bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA) is set, the section contains data that gets a defined value before execution starts. In other words: the section's data in the file is meaningful.
If bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) is set, this section's data is uninitialized data and will be initialized to all-0-bytes before execution starts. This is normally the BSS.
If bit 9 (IMAGE_SCN_LNK_INFO) is set, the section doesn't contain image data but comments, description or other documentation.
If bit 11 (IMAGE_SCN_LNK_REMOVE) is set, the data is part of an object file's section that is supposed to be left out when the executable file is linked.
If bit 12 (IMAGE_SCN_LNK_COMDAT) is set, the section contains "common block data", which are packaged functions of some sort.
Bit 15 (IMAGE_SCN_MEM_FARDATA) is reserved for future use.
Bit 17 (IMAGE_SCN_MEM_PURGEABLE) is reserved for future use.
The same is IMAGE_SCN_MEM_16BIT.
Bit 18 (IMAGE_SCN_MEM_LOCKED) is reserved for future use.
Bit 19 (IMAGE_SCN_MEM_PRELOAD) is reserved for future use.
Bits 20 to 23 specify an alignment of data on a 1/8192-byte boundary. This is valid for object files only.
Bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) indicates that the count of relocations for the section exceeds the 16 bits reserved for it in section header. If the bit is set and the NumberOfRelocations field in the section header is 0xffff, the actual relocation count is stored in the 32-bit VirtualAddress field of the first relocation.
If bit 25 (IMAGE_SCN_MEM_DISCARDABLE) is set, the section's data is not needed after the process has started. This is the case, for example, with the relocation information. I've seen it also for startup routines of drivers and services that are only executed once.
If bit 26 (IMAGE_SCN_MEM_NOT_CACHED) is set, the section's data should not be cached. Don't ask my why not. Does this mean to switch off the 2nd-level-cache?
If bit 27 (IMAGE_SCN_MEM_NOT_PAGED) is set, the section's data should not be paged out. This may be interesting for drivers.
If bit 28 (IMAGE_SCN_MEM_SHARED) is set, the section's data is shared among all running instances of the image. If it is e.g. the initialized data of a DLL, all running instances of the DLL will at any time have the same variable contents.
Note that only the first instance's section is initialized. Sections containing code are always shared.If bit 29 (IMAGE_SCN_MEM_EXECUTE) is set, the process gets 'execute'-access to the section's memory.
If bit 30 (IMAGE_SCN_MEM_READ) is set, the process gets 'read'-access to the section's memory.
If bit 31 (IMAGE_SCN_MEM_WRITE) is set, the process gets 'write'-access to the section's memory.
--------------
After the section headers we find the sections themselves. They are, in the file, aligned to 'FileAlignment' bytes (that is, after the optional header and after each section's data there will be padding bytes). When loaded (in RAM), the sections are aligned to 'SectionAlignment' bytes.
As an example, if the optional header ends at file offset 981 and 'FileAlignment' is 512, the first section will start at byte 1024. Note that you can find the sections via the 'PointerToRawData' or the 'VirtualAddress', so there is hardly any need to actually fuss around with the alignments.
The remainder of an image file contains blocks of data that are not necessarily at any specific file offset. Instead the locations are defined by pointers in the Optional Header or a section header. An exception is for images with a Section Alignment value (see the Optional Header description) of less than the page size of the architecture (4K for Intel x86 and for MIPS; 8K for Alpha). In this case there are constraints on the file offset of the section data. Another exception is that attribute certificate and debug information must be placed at the very end of an image file (with the attribute certificate table immediately preceding the debug section), because the loader does not map these into memory. The rule on attribute certificate and debug information does not apply to object files, however.
原本的彈窗程序test.exe
被感染后
原本的記事本程序重命名為test.exe
並被感染后
原本的計算器程序重命名為test.exe
並被感染后
(2)這兩方面與感染前程序相比,有什么變化
原本的彈窗程序test.exe
被感染前
原本的記事本程序notepad.exe
被感染前
原本的計算器程序calc.exe
被感染前
分析發現,不管是哪個文件,文件被感染后比被感染前多出了一個.hum
節,其訪問屬性是IMAGE_SCN_CNT_CODE
(節中包含可執行的代碼)、IMAGE_SCN_MEM_EXECUTE
(進程對節的內存有執行權限)、IMAGE_SCN_MEM_READ
(進程對節的內存有讀權限)、IMAGE_SCN_MEM_WRITE
(進程對節的內存有寫內存)。而感染前本來就有的節沒有發生變化。
(3)判斷“病毒”程序是否會反復感染同一文件;如果可以,多次感染和一次感染的不同點是什么;如果不可以,那么“病毒” 程序如何做到不重復感染
“病毒”程序不可以反復感染同一文件。
test.exe
已經被感染后嘗試再次運行病毒程序,提示沒有再次感染。
GetApiA部分代碼的調試
用ollydbg調試,F7單步執行,步入GetApiA
在GetApiA
中調用另外一個過程
發現程序死循環在這個過程里面
查資料后發現這並不是死循環,而是當每次循環使計數器減一,當計數器值為0時跳出循環。此處loop使用的計數器是寄存器ECX,而通過寄存器EAX查找目標函數,根據堆棧和ESI,猜測要尋找的是GetModuleHandleA
函數。根據cmp bl,byte ptr ds:[edx+eax]
和 je short 感染程序.00401016
完成判斷跳出循環。
用depends打開一個exe,查看user32.dll
中的函數,發現GetModuleHandleA
的編號是177。
此時找到GetModuleHandleA
一個字符一個字符地進行比較,把相同的位數存到EDX中
發現之后又使用這個循環去尋找其他函數,那么直接用F8跳過找函數的call 40100A
這部分代碼即可。注意到其在查找GetModuleHandleA
之后又尋找了GetProcAddress
、LoadLibraryA
、CreateFileA
、CreatingFileMappingA
、MapViewOfFile
、UnmapViewOfFile
、CloseHandle
、GetFileSize
、SetFilePointer
、SetEndOfFile
、ExitProcess
這些函數。
my_infect部分代碼的調試
跳轉到40144D
處,F2下斷點,F9運行至斷點處,步入my_infect
部分的代碼
經過call [ebp+aCreateFile]
和inc eax
后,EAX的值不為0,ZF值為0,所以je _Err
不會進行跳轉,說明打開目標文件test.exe
成功。
同理,后面的得到文件大小、關閉文件、創建文件映射、映射文件也都成功,my_include
部分代碼順利執行完畢。
modipe.asm
部分代碼的調試
my_include
部分執行結束后,緊接着的是modipe.asm
執行cmp word ptr [esi],'ZM'
后,ZF為1,所以jne CouldNotInfect
不跳轉。
執行cmp word ptr [esi],'EP'
后,ZF為1,所以jne CouldNotInfect
不跳轉
執行cmp dword ptr [esi+8],'dark'
后,ZF為1,所以je CouldNotInfect
跳轉。也就是說,因為文件中已經存在了dark
這個感染標記,所以不再次感染此文件。
CouldNotInfect
部分代碼的調試
執行test eax,eax
后,ZF為0,所以jnz @g12
不跳轉
然后會依次call No caption
和call _tips
_tips
部分代碼的調試
這里調用MessageBoxA函數,彈出彈窗“本程序僅感染本目錄下未被感染過的test.exe
程序。”
_where
部分代碼的調試
執行cmp dword ptr [esi+8],'dark'
后,ZF為0,判斷出是啟動程序而不是HOST程序,所以je jmp_oep
不跳轉
然后jmp _xit
跳轉到_xit
_xit
部分的代碼調試
退出啟動程序。
2.分析源程序main.asm
(1)定位並分析其中重定位相關的代碼,說明存儲相應偏差位移量的寄存器是哪個;
(2)定位並分析其中獲取kernel32.dll在內存中裝載位置的代碼,說明記錄裝載位置的存儲單元(包含寄存器和變量)
3.手工清除被感染程序中的“病毒”部分,將感染程序盡可能還原為原正常的程序,同時達到免疫效果
用winhex和PEview恢復文件並實現免疫
用winhex打開一個被感染后和一個被感染前的test.exe
,進行對比,同時借助PEview理解PE文件格式。
B6
處的節數修改為原來的3
B8
處的感染標記"dark"不要刪除,這是使test.exe
免疫此實驗中的病毒所要利用的。
D8
處的入口點地址需要修改為原來的1000
100
處的Image大小修改為原來的4000
220
到248
的.hum的IMAGE_SECTION_HEADER清0
A00
到最后的.hum節清零
A00
到最后的.hum節刪掉,把文件恢復至原來大小
使用winhex的選塊選擇這部分
鼠標右鍵→編輯→移除
發現剩下了近一行,繼續移除
至此文件恢復完畢
保存恢復后的文件為test_Rev.exe
,發現其大小已經變為被感染前的2560字節,而不再是被感染后的6656字節。
比較恢復后和被感染前的文件
在linux中使用xxd
指令將二進制文件轉為16進制文本文件
xxd test_Rev.exe > test_Rev.txt
xxd test.exe > test.txt
然后用vscode比較兩個文本文件的不同
比較發現,兩個文件唯一不同的地方就在於test_Rev.exe
具有"dark"這個感染標記,則確定已經盡可能將文件恢復為感染前的樣子
運行測試結果
運行test_Rev.exe
,可以實現與被感染前的test.exe
一樣的效果
將test_Rev.exe
重命名為test.exe
,放到與病毒文件同一個文件夾中,嘗試用病毒文件感染它
提示無法感染
再次運行test.exe
,發現確實沒有被感染