用 S2E 和 Kaitai Struct 針對性地處理檔案解析器(二)
用readelf實驗一下
我們終於可以開始readelf部分的實驗了。 在我們開始之前,請修改S2E配置檔案,僅啟用以下的外掛:
BaseInstructions
HostFiles
VMI
TranslationBlockCoverage
ModuleExecutionDetector
ForkLimiter
ProcessExecutionDetector
LinuxMonitor
我們還必須修改bootstrap.sh。
在${S2EGET} “readelf”下新增$
{S2EGET}“small_exec.elf”以便將測試用例複製到客戶機。為了使用我們的測試用例,在prepare_inputs函式中,將truncate
-s 256 $ {SYMB_FILE}替換為cp small_exec.elf $ {SYMB_FILE}。
還不用替換symbfile命令; 讓我們先來看一下readelf如何在一個完全符號化的檔案上執行。
執行S2E一分鐘左右,然後結束程式。 你應該看到很多分叉的情況(我這裡是136種情況)。 讓我們生成程式碼覆蓋資訊:
# The actual disassembler isn't important
s2e coverage basic_block --disassembler=binaryninja readelf_kaitai
這些分支情況發生在哪?
由於readelf呼叫在符號化資料時呼叫了printf,所以libc中有很多。 readelf 自身的分支呢?
下面的圖片顯示了readelf中的兩個函式的片段:process_section_headers和init_dwarf_regnames。
綠色的部分表示由S2E執行的塊。 分支節點受到的約束已由註釋說明(KLEE中的KQuery格式):
readelf's process_section_headers 程式碼覆蓋
readelf's init_dwarf_regnames 程式碼覆蓋
當檢查到下列情況也會發生分叉:
如果輸入檔案是一份存檔
資料編碼(小端或大端位元組序)
section header 表的檔案偏移量
如果每個部分的sh_link和sh_info值都是有效的
還有許多其他的地方!眼下只對留下那些與ELF頭部的e_machine欄位有關的程式路徑。編輯bootstrap.sh並用./s2e_kaitai_cmd
${SYMB_FILE}替換${S2ECMD} symbfile
${SYMB_FILE}。現在重新執行S2E一分鐘。在執行期間,分支情況僅限於get_machine_name和init_dwarf_regnames函式,這兩個函式都是取決於e_machine的值的switch語句。成功了!
讓我們嘗試在ELF檔案中換一個不同的欄位 -section header 的sh_type欄位。不像e_machine欄位,只會在ELF檔案中出現一次。sh_type可以在整個檔案中出現多次(取決於ELF檔案中section的數量)。
我們必須將S2E執行狀態和輸入檔案的起始地址傳播到ELF宣告中的相對應的屬性中。這次我們必須將params
spec新增到section_header型別中。
type屬性定義為無符號的4位元組列舉型別,因此我們必須將其更改為4位元組的陣列型別,以便我們可以使用s2e_make_symbolic:
# Elf(32|64)_Shdr
section_header:
params:
- id: s2e_state
- id: start_addr
seq:
# sh_name
- id: name_offset
type: u4
# sh_type
- id: type
size: 4
process: s2e_make_symbolic(s2e_state, start_addr, _io.pos, "sh_type")
# ...
我們還必須確保將這兩個引數傳遞給SectionHeader的建構函式。 section頭可以在section_headers例項下找到:
# The original section_headers
section_headers:
pos: section_header_offset
repeat: expr
repeat-expr: qty_section_header
size: section_header_entry_size
type: section_header
# Redefined for symbolic execution
section_headers:
pos: section_header_offset
repeat: expr
repeat-expr: qty_section_header
size: section_header_entry_size
type: section_header(s2e_state, start_addr)
注意section_headers被宣告為“例項規範”。
這意味著section_headers只能根據需要將要解析section頭部的函式編譯為一個函式。
因此,我們必須訪問section_headers以強制解析它們。
為此,我們必須修改s2e-config.lua中的make_elf_symbolic函式:
function make_symbolic_elf(state, start_addr, buffer)
-- ...
-- This will kick-start the parser. However, now we do care about the final
-- result, because we must access the section headers to force them to be
-- parsed
local elf_file = Elf(state, start_addr, KaitaiStream(ss))
-- This will kick-start the section header parser
_ = elf_file.header.section_headers
end
執行ksc再次重新生成elf.lua。 在我們重新執行S2E之前,我們來看下elf.lua。 特別是在section_headers中的get方法中解析的section頭部:
function Elf.EndianElf.property.section_headers:get()
-- ...
for i = 1, self.qty_section_header do
self._raw__m_section_headers[i] = \
self._io:read_bytes(self.section_header_entry_size)
local _io = KaitaiStream(stringstream(self._raw__m_section_headers[i]))
self._m_section_headers[i] = Elf.EndianElf.SectionHeader(self.s2e_state,
self.start_addr,
_io, self,
self._root,
self._is_le)
end
-- ...
end
注意到ksc建立一個區域性變數_io,它被傳遞給SectionHeader建構函式。 這個_io變數包含最終將被轉換成SectionHeader物件的原始資料。 不幸的是,這會導致s2e_make_symbolic出現處理規範方面的問題。
回想一下,解析器的當前位置(_io.pos)被傳遞給s2e_make_symbolic處理規範。 但是糟糕的是當建立本地_io流時,這個地址將清零,因此符號化的時候使用這個地址會造成錯誤的記憶體地址。 不過,我們可以通過對稍微修改下Lua程式碼來解決這個問題:
for i = 1, self.qty_section_header do
-- Get the absolute start address of the section header before it is parsed
local _sec_hdr_start_addr = self.start_addr + self._io:pos()
self._raw__m_section_headers[i] = \
self._io:read_bytes(self.section_header_entry_size)
local _io = KaitaiStream(stringstream(self._raw__m_section_headers[i]))
-- Use the section header's start address instead of the ELF's start address
self._m_section_headers[i] = Elf.EndianElf.SectionHeader(self.s2e_state,
_sec_hdr_start_addr,
_io, self,
self._root,
self._is_le)
end
是的,修改生成的Lua程式碼是令人厭惡的。但是,它確保了符號化時的記憶體地址是正確的。當我重新編譯S2E時,分支被限制在process_section_headers函式中的sh_type比較部分。
總結和未來的工作
在這篇文章中,探討了如何更有針對性的執行檔案解析器的符號執行問題。我們可以使用Kaitai Struct來定位輸入檔案的特定部分來進行符號化,而非給解析器一個完全符號的輸入檔案(這會很快導致路徑爆炸問題)。這種方法似乎奏效,但還是有些問題。
首先,首先,它依賴於使用者有一個有效的樣例檔案來執行符號執行。
。這個樣例檔案還必須包含我們希望執行的解析器部分的資料。比如,假設我們想將此技術應用於PNG解析器。如果我們拿這個PNG檔案的定義,並希望看到當bkgd_truecolor屬性符號化時發生了什麼,我們的PNG檔案也必須包含一個背景顏色塊。否則我們的解析器將沒有符號化的東西。
由於類似的原因,我們不能僅僅使用S2E引導指令碼建立的“空”的符號檔案。為當Kaitai Struct解析器執行時,它執行在檔案中的具體資料上。 S2E建立的預設符號檔案用NULL字元填充,因此解析器無法解析。如果我們可以憑空創造出檔案,是不是會很酷?
其他問題取決於我們如何使用Kaitai Struct。這不是Kaitai Struct的錯誤;實際上,Kaitai Struct FAQ明確指出,生成的解析器本來就不是為了“基於事件”的解析模型而設計的。我們可以修改ksc來生成基本不需要手動修改的程式碼(例如,自動生成引數規範,使用非延遲的例項規範,始終跟蹤解析器的絕對路徑等等),但是為了簡單起見不去考慮KaitaiStruct “原本的樣子”。
不是基於檔案的符號執行怎麼辦?例如,在我之前的帖子中,我展示瞭如何使用S2E來解決使用命令列字串作為輸入的CTF挑戰。這篇文章中描述的方法對解決這個CTF的挑戰是沒有幫助的。同樣我們可以擴充套件KaitaiStruct外掛來處理命令列字串。例如,我們可以在Kaitai Struct中定義CTF挑戰的輸入字串如下:
meta:
id: ctf-input
title: Google CTF input format
ks-version: 0.8
seq:
- id: prefix
size: 4
contents: "CTF{"
- id: to_solve
size: 63 # total length of 67 bytes minus the 4 byte prefix
process: s2e_make_symbolic(s2e_state, start_addr, _io.pos, "to_solve")
params:
- id: s2e_state
- id: start_addr
加上一些額外的程式碼,我們可以在輸入字串上的執行此解析器,只將最後63個位元組符號化。 這將允許我們從S2E外掛中刪除onSymbolicVariableCreation方法。
儘管出現了這些問題,但是把S2E和Kaitai Struct組合起來似乎對我目前正在做的工作(儘管你的目的可能會有所不同)還是很有幫助的。 我們可以通過更多的工作(更多的程式碼)來解決這些問題。 所以,我想我會把那作為一個未來的帖子:)
本文由看雪翻譯小組 fyb波 編譯,來源Adrian's Ramblings 轉載請註明來自看雪社群
相關文章
- 用 S2E 和 Kaitai Struct 針對性地處理檔案解析器2017-11-13AIStruct
- 對於CSV檔案中{,}和{"}的處理2009-03-16
- 用shell處理二進位制檔案(轉)2007-08-11
- Python使用struct處理二進位制2020-12-11PythonStruct
- Python使用struct處理二進位制(pack和unpack用法)2016-10-10PythonStruct
- Linux學習之檔案處理命令(二)目錄處理命令 && 檔案處理命令2017-06-20Linux
- 針對Adblock廣告遮蔽處理2017-12-15BloC
- Git處理二進位制檔案2018-07-14Git
- 用c語言處理檔案2020-09-13C語言
- sqlserver 針對預處理sql傳入引數的處理方式2017-03-21SQLServer
- 針對web高併發量的處理2017-11-12Web
- 多對一處理 和一對多處理的處理2020-06-20
- MyBaits | 對映檔案之引數處理2018-07-26AI
- 科學音訊處理(二):如何使用 Octave 對音訊檔案進行基本數學訊號處理2016-09-30音訊
- [R]檔案處理2016-09-30
- bat處理檔案2011-02-21BAT
- bat檔案處理2010-05-18BAT
- JavaScript檔案處理第二部分:檔案讀取2015-04-01JavaScript
- 用批處理檔案執行備份2005-10-31
- 第14周-專案1-用二進位制檔案處理學生成績2015-06-15
- C語言的本質(20)——預處理之二:條件預處理和包含標頭檔案2014-07-17C語言
- 檔案輸入輸出處理(二)-位元組流2022-06-19
- beetle.express針對websocket的高效能處理2015-05-08ExpressWeb
- 如何使用find和xargs查詢和處理檔案2019-11-17
- 針對字尾刪除檔案的方法2016-09-12
- window 批處理檔案2019-05-10
- python處理檔案2020-09-18Python
- Go xml檔案處理2022-03-15GoXML
- python檔案處理2023-02-26Python
- python 檔案處理2018-05-15Python
- Python 檔案處理2014-10-31Python
- JAVA ZIP 處理檔案2014-09-18Java
- 批處理檔案命令2010-05-13
- 檔案處理函式2015-11-15函式
- Windows批處理檔案2012-10-04Windows
- bat批處理檔案2013-03-07BAT
- 前端怎麼處理二進位制檔案下載2019-07-05前端
- Javascript如何訪問和處理系統檔案2013-10-17JavaScript