编译原理实验语义分析_Windows MVSC编译器实现Xtended Flow Guard(XFG)保护机制的原理分析...
一、前言
近期,微軟正在開發(fā)Xtended Flow Guard(XFG),這是Control Flow Guard(控制流防護(hù),CFG)的演進(jìn)版本,作為其自身的控制流完整性實(shí)現(xiàn)。XFG通過不同類型函數(shù)原型的哈希值,限制間接控制流的轉(zhuǎn)移。在這篇文章中,深入討論了MSVC編譯器是如何生成XFG函數(shù)的原型哈希值。
二、概述
2014年,微軟推出了名為“Control Flow Integrity”(控制流防護(hù),CFG)的控制流完整性(CFI)解決方案。在此前,已經(jīng)有很多研究人員對CFG展開了廣泛的研究。隨著時間的推移,接連發(fā)現(xiàn)了許多繞過CFG的方法。其中一些繞過依賴于實(shí)現(xiàn)上的問題(例如JIT編譯器的集成,或可以濫用敏感的API),但最終都得到了解決。但是,有一個設(shè)計上的問題始終存在——CFG沒有提供有效調(diào)用目標(biāo)的任何粒度。任何受保護(hù)的間接調(diào)用都被允許去調(diào)用任何有效的調(diào)用目標(biāo)。在體積較大的二進(jìn)制文件中,有效的調(diào)用目標(biāo)可能會達(dá)到上千個,這就讓攻擊者擁有了足夠的靈活性,可以通過串聯(lián)有效的C++虛擬函數(shù)來繞過CFG(例如:偽造的面向?qū)ο缶幊淘O(shè)計,COOP)。
我們把時間線快進(jìn)幾年。微軟一直在開發(fā)CFG的改進(jìn)版本,被稱為“Xtended Flow Guard”(XFG)。XFG通過類型簽名檢查限制間接調(diào)用或跳轉(zhuǎn),從而提供了更細(xì)粒度的CFI。XFG背后的一個關(guān)鍵概念是,在編譯時將基于類型簽名的哈希分配給那些可以作為間接調(diào)用/跳轉(zhuǎn)目標(biāo)的函數(shù)。然后,在XFG指示的間接調(diào)用點(diǎn)上,進(jìn)行哈希檢查,僅允許具有預(yù)期簽名哈希的函數(shù)。
幾周前,研究員Connor McGarr發(fā)表了一篇文章,名為《漏洞利用開發(fā):在巖石和XFG之間》,文章說明了XFG的工作方式及其潛在弱點(diǎn)。這篇文章激發(fā)了我的好奇心,因此我希望使用IDA Pro和Windbg,以了解XFG哈希是如何生成的。
在撰寫本文時,Windows 10 Insider Preview(開發(fā)者)版本中已經(jīng)應(yīng)用了XFG。如果想要編譯支持XFG的程序,需要使用Visual Studio 2019預(yù)覽版。
本文的分析基于Visual Studio 2019 16.8.0版本 Preview 2.1的二進(jìn)制文件:
· c1.dll version 19.28.29213.0
· c2.dll version 19.28.29213.0
這篇文章重點(diǎn)介紹如何針對C語言源代碼生成XFG哈希。盡管初步看起來C++代碼的哈希算法看起來非常相似,但是我們尚未研究其具體細(xì)節(jié)。由于這篇文章篇幅較長,所以分為了幾個部分。首先,從XFG哈希的快速入門開始。然后,分析如何對函數(shù)進(jìn)行哈希處理,詳細(xì)介紹如何對不同的C類型進(jìn)行哈希處理。最后,我們檢查應(yīng)用于計算哈希的一些最終轉(zhuǎn)換,并通過嘗試計算哈希來得出結(jié)論。
三、XFG哈希快速入門
我們先從一個非常簡單的C語言程序開始,定義一個名為FPTR ([1])的函數(shù)指針類型,該函數(shù)聲明一個帶有兩個float參數(shù)并返回另一個float的函數(shù)。函數(shù)main聲明一個類型為FPTR、名為fptr的函數(shù)指針變量,該變量設(shè)置為函數(shù)foo ([2])的地址,該函數(shù)的原型與FPTR類型匹配。最后,在[3]的位置,調(diào)用fptr指向的函數(shù),并將值1.00001和2.00002作為參數(shù)傳遞。
? ? #include ?< stdio.h >?
[1] typedef float (* FPTR)(float, float);
? ? float foo(float val1, float val2){
? ? ? ? printf("I received float values %f and %f\n", val1, val2);
? ? ? ? return (val2 - val1);
? ? }
? ? int main(int argc, char **argv){
[2] ? ? FPTR fptr = foo;
? ? ? ? printf("Calling function pointer...\n");
[3] ? ? fptr(1.00001, 2.00002);
? ? ? ? return 0;
? ? }
我們使用以下命令行,用VS 2019 Preview的x64本地工具命令提示符中編譯了上述源代碼。這里使用到了/guard:xfg標(biāo)志,以啟用XFG。
> ?cl /Zi /guard:xfg example1.c
反匯編后的主要函數(shù)如下所示:
main ? ? ?; int __cdecl main(int argc, const char **argv, const char **envp)
main
main ? ? ?var_18 ? ? ? ? ?= qword ptr -18h
main ? ? ?var_10 ? ? ? ? ?= qword ptr -10h
main ? ? ?arg_0 ? ? ? ? ? = dword ptr ?8
main ? ? ?arg_8 ? ? ? ? ? = qword ptr ?10h
main
main ? ? ? ? ?mov ? ? [rsp+arg_8], rdx
main+5 ? ? ? ?mov ? ? [rsp+arg_0], ecx
main+9 ? ? ? ?sub ? ? rsp, 38h
main+D ? ? ? ?lea ? ? rax, foo
main+14 ? ? ? mov ? ? [rsp+38h+var_18], rax
main+19 ? ? ? lea ? ? rcx, aCallingFunctio ; "Calling function pointer...\n"
main+20 ? ? ? call ? ?printf
main+25 ? ? ? mov ? ? rax, [rsp+38h+var_18]
main+2A ? ? ? mov ? ? [rsp+38h+var_10], rax
main+2F ? ? ? mov ? ? r10, 99743F3270D52870h
main+39 ? ? ? movss ? xmm1, cs:__real@40000054
main+41 ? ? ? movss ? xmm0, cs:__real@3f800054
main+49 ? ? ? mov ? ? rax, [rsp+38h+var_10]
main+4E ? ? ? call ? ?cs:__guard_xfg_dispatch_icall_fptr
main+54 ? ? ? xor ? ? eax, eax
main+56 ? ? ? add ? ? rsp, 38h
main+5A ? ? ? retn
main+5A ? main ? ? ? ? ? ?endp
我們可以在main+0x2F處看到,對于在main + 0x4E處之后的函數(shù)指針調(diào)用,R10寄存器被設(shè)置為預(yù)期的基于類型的哈希(0x99743F3270D52870)。通過函數(shù)指針調(diào)用的函數(shù)是foo,我們可以驗(yàn)證其原型哈希(由函數(shù)開頭的前8個字節(jié)表示)是否與預(yù)期的哈希匹配。這意味著,函數(shù)foo是main+0x4E上間接調(diào)用的有效目標(biāo)。準(zhǔn)確的說,原型哈希位于foo函數(shù)(0x99743F3270D52871)之前的8個字節(jié),與我們在R10寄存器(0x99743F3270D52870)中看到的預(yù)期哈希相匹配,除了第0位之外。
.text:0000000140001008 ? ? ? ? ? ? ? ? dq 99743F3270D52871h
foo
foo ? ? ?; =============== S U B R O U T I N E ================================
foo ? ? ?; float __fastcall foo(float val1, float val2)
foo ? ? ?foo ? ? ? ? ? ? proc near ? ? ? ? ? ? ? ; DATA XREF: main+D
foo
foo ? ? ?arg_0 ? ? ? ? ? = dword ptr ?8
foo ? ? ?arg_8 ? ? ? ? ? = dword ptr ?10h
foo
foo ? ? ? ? ?movss ? [rsp+arg_8], xmm1
foo+6 ? ? ? ?movss ? [rsp+arg_0], xmm0
foo+C ? ? ? ?sub ? ? rsp, 28h
foo+10 ? ? ? cvtss2sd xmm0, [rsp+28h+arg_8]
foo+16 ? ? ? cvtss2sd xmm1, [rsp+28h+arg_0]
foo+1C ? ? ? movaps ?xmm2, xmm0
foo+1F ? ? ? movq ? ?r8, xmm2
foo+24 ? ? ? movq ? ?rdx, xmm1
foo+29 ? ? ? lea ? ? rcx, _Format ? ?; "I received float values %f and %f\n"
foo+30 ? ? ? call ? ?printf
foo+35 ? ? ? movss ? xmm0, [rsp+28h+arg_8]
foo+3B ? ? ? subss ? xmm0, [rsp+28h+arg_0]
foo+41 ? ? ? add ? ? rsp, 28h
foo+45 ? ? ? retn
foo+45 ? foo ? ? ? ? ? ? endp
但是無需擔(dān)心這里的差異,因?yàn)樵赬FG調(diào)度函數(shù)(ntdll!LdrpDispatchUserCallTargetXFG)的開始處,R10的第0位被設(shè)置了,導(dǎo)致預(yù)期哈希值和函數(shù)哈希值在第0位上的差異沒有意義。
LdrpDispatchUserCallTargetXFG ? ? ?LdrpDispatchUserCallTargetXFG proc near
LdrpDispatchUserCallTargetXFG ? ? ?; __unwind { // LdrpICallHandler
LdrpDispatchUserCallTargetXFG ? ? ? ? ?or ? ? ?r10, 1
LdrpDispatchUserCallTargetXFG+4 ? ? ? ?test ? ?al, 0Fh
LdrpDispatchUserCallTargetXFG+6 ? ? ? ?jnz ? ? short loc_180094337
LdrpDispatchUserCallTargetXFG+8 ? ? ? ?test ? ?ax, 0FFFh
LdrpDispatchUserCallTargetXFG+C ? ? ? ?jz ? ? ?short loc_180094337
LdrpDispatchUserCallTargetXFG+E ? ? ? ?cmp ? ? r10, [rax-8]
LdrpDispatchUserCallTargetXFG+12 ? ? ? jnz ? ? short loc_180094337
LdrpDispatchUserCallTargetXFG+14 ? ? ? jmp ? ? rax
四、哈希函數(shù)類型
MSVC編譯器由兩個部分組成——前端和后端。前端是特定于某一語言的,負(fù)責(zé)讀取源代碼、詞法、解析、進(jìn)行語義分析和發(fā)出IL(中間語言)。后端是特定于某一目標(biāo)體系結(jié)構(gòu)的,它讀取前端生成的IL,進(jìn)行優(yōu)化,并為特定體系結(jié)構(gòu)生成代碼。
函數(shù)原型哈希的生成是由語言前端進(jìn)行的。這意味著在編譯C語言代碼時,C語言前端(c1.dll)負(fù)責(zé)生成原型哈希,而在編譯C++代碼時,C++前端(c1xx.dll)負(fù)責(zé)這個任務(wù)。
一旦相應(yīng)的語言前端生成了原型哈希,就會由編譯器后端(在這里是x64的后端c2.dll)執(zhí)行一些最終轉(zhuǎn)換。接下來,我們將詳細(xì)介紹在編譯C代碼時創(chuàng)建原型哈希的每個步驟。
在使用/guard:xfg標(biāo)志編譯C語言源代碼時,編譯器前端會調(diào)用c1!XFGHelper__ComputeHash_1函數(shù),以計算要處理的函數(shù)的原型哈希。
c1!XFGHelper__ComputeHash_1函數(shù)創(chuàng)建一個類型為XFGHelper::XFGHasher的對象,該對象負(fù)責(zé)收集正在處理的函數(shù)的類型信息,并根據(jù)收集的類型信息生成原型哈希。XFGHelper::XFGHasher使用std::vector的實(shí)例存儲所有即將被計算哈希的類型信息,并且提供了在計算哈希的整個過程中調(diào)用的多個方法:
? ? XFGHelper::XFGHasher::add_function_type()
? ? XFGHelper::XFGHasher::add_type()
? ? XFGHelper::XFGHasher::get_hash()
? ? XFGHelper::XFGTypeHasher::compute_hash()
? ? XFGHelper::XFGTypeHasher::hash_indirection()
? ? XFGHelper::XFGTypeHasher::hash_tag()
? ? XFGHelper::XFGTypeHasher::hash_primitive()
在初始化XFGHelper::XFGHasher的實(shí)例后,XFGHelper__ComputeHash_1函數(shù)調(diào)用XFGHelper::XFGHasher::add_function_type(),將XFGHelper::XFGHasher實(shí)例和一個Type_t對象作為參數(shù)傳遞,該對象包含有關(guān)哈希函數(shù)的類型信息。
XFGHelper__ComputeHash_1 ? ? ?XFGHelper__ComputeHash_1 proc near
XFGHelper__ComputeHash_1
XFGHelper__ComputeHash_1 ? ? ?arg_0 ? ? ? ? ? = qword ptr ?8
XFGHelper__ComputeHash_1 ? ? ?arg_8 ? ? ? ? ? = qword ptr ?10h
XFGHelper__ComputeHash_1 ? ? ?arg_10 ? ? ? ? ?= qword ptr ?18h
[...]
XFGHelper__ComputeHash_1+79 ? ? ? ?xorps ? xmm0, xmm0
XFGHelper__ComputeHash_1+7C ? ? ? ?movdqu ?cs:xfg_hasher, xmm0 ; zero inits xfg_hasher
[...]
XFGHelper__ComputeHash_1+B1 ? ? ? ?mov ? ? rdx, rbp ? ? ? ?; rdx = Type_t containing function information
XFGHelper__ComputeHash_1+B4 ? ? ? ?lea ? ? rbp, xfg_hasher
XFGHelper__ComputeHash_1+BB ? ? ? ?mov ? ? rcx, rbp
XFGHelper__ComputeHash_1+BE ? ? ? ?call ? ?XFGHelper::XFGHasher::add_function_type(Type_t const *,XFGHelper::VirtualInfoFromDeclspec)
XFGHelper__ComputeHash_1+C3 ? ? ? ?mov ? ? rdx, rsi ? ? ? ?; rdx = function- > return_type (struct Type_t *)
XFGHelper__ComputeHash_1+C6 ? ? ? ?mov ? ? rcx, rbp ? ? ? ?; this
XFGHelper__ComputeHash_1+C9 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type(Type_t const *) ; (step 5)
函數(shù)XFGHelper::XFGHasher::add_function_type將檢索有關(guān)哈希函數(shù)的4條信息,從XFGHelper::XFGHasher::add_function_type返回后,通過調(diào)用XFGHelper::XFGHasher::add_type可以再添加一條信息,如上面的反匯編列出的XFGHelper__ComputeHash_1+C9所示。這些信息存儲在XFGHelper::XFGHasher實(shí)例的std::vector中:
(1)4個字節(jié),指示函數(shù)的參數(shù)數(shù)量;
(2)每個函數(shù)參數(shù)有8個字節(jié),保存其參數(shù)的哈希值;
(3)1個字節(jié),指示函數(shù)是否是可變參數(shù)(是否使用可變數(shù)量的參數(shù));
(4)4個字節(jié),指示函數(shù)使用的調(diào)用約定;
(5)8個字節(jié),存保存函數(shù)返回類型的哈希值。
4.1 參數(shù)數(shù)量
XFGHelper::XFGHasher::add_function_type函數(shù)首先將一個DWORD添加到std::vector,以指示該函數(shù)的參數(shù)數(shù)量。這個數(shù)字可能會受到可變數(shù)量的參數(shù)、來自__declspec的虛擬信息的影響(我懷疑這可能是在C++的XFG實(shí)現(xiàn)中的一些重用代碼)。簡而言之,這里我們考慮的參數(shù)數(shù)量,就是在函數(shù)原型中聲明的實(shí)際參數(shù)數(shù)量,如果函數(shù)用到了可變數(shù)量的參數(shù),那么就是-1,如果函數(shù)具有來自__declspec的虛擬信息,則為-1。
XFGHelper::XFGHasher::add_function_type+18 ? ? ? ?mov ? ? rsi, [rdx+10h] ?; rsi = function_info- > FunctionTypeInfo
XFGHelper::XFGHasher::add_function_type+1C ? ? ? ?mov ? ? rbx, rcx
XFGHelper::XFGHasher::add_function_type+1F ? ? ? ?mov ? ? rcx, rsi ? ? ? ?; this
XFGHelper::XFGHasher::add_function_type+22 ? ? ? ?movzx ? r14d, r8b
XFGHelper::XFGHasher::add_function_type+26 ? ? ? ?mov ? ? r15, rdx
XFGHelper::XFGHasher::add_function_type+29 ? ? ? ?call ? ?FunctionTypeInfo_t::RealNumberOfParameters(void)
XFGHelper::XFGHasher::add_function_type+2E ? ? ? ?mov ? ? rcx, rsi ? ? ? ?; this
XFGHelper::XFGHasher::add_function_type+31 ? ? ? ?mov ? ? r9d, eax ? ? ? ?; r9 = real_number_of_params
XFGHelper::XFGHasher::add_function_type+34 ? ? ? ?call ? ?FunctionTypeInfo_t::IsVarArgsFunction(void)
XFGHelper::XFGHasher::add_function_type+39 ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGHasher::add_function_type+3D ? ? ? ?lea ? ? rbp, [r9-1] ? ? ; rbp = real_number_of_params - 1
XFGHelper::XFGHasher::add_function_type+41 ? ? ? ?test ? ?al, al ? ? ? ? ?; is variadic function?
XFGHelper::XFGHasher::add_function_type+43 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGHasher::add_function_type+46 ? ? ? ?cmovz ? rbp, r9 ? ? ? ? ; if not variadic, rbp = real_number_of_params
XFGHelper::XFGHasher::add_function_type+4A ? ? ? ?test ? ?r8b, r8b ? ? ? ?; does it have virtual info from __declspec?
XFGHelper::XFGHasher::add_function_type+4D ? ? ? ?lea ? ? r9, [rsp+48h+arg_14]
XFGHelper::XFGHasher::add_function_type+52 ? ? ? ?lea ? ? r8, [rsp+48h+arg_10]
XFGHelper::XFGHasher::add_function_type+57 ? ? ? ?lea ? ? eax, [rbp-1] ? ?; number of params = rbp - 1
XFGHelper::XFGHasher::add_function_type+5A ? ? ? ?cmovz ? eax, ebp ? ? ? ?; if no virtual info from __declspec, number of params = rbp
XFGHelper::XFGHasher::add_function_type+5D ? ? ? ?mov ? ? [rsp+48h+arg_10], eax ; value to add = number of params (dword)
XFGHelper::XFGHasher::add_function_type+5D ? ? ? ? ? ? ? ? ? ? ; [step 1]
XFGHelper::XFGHasher::add_function_type+61 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
4.2 每個參數(shù)的類型哈希
接下來,XFGHelper::XFGHasher::add_function_type進(jìn)入一個循環(huán),在該循環(huán)中,它計算每個函數(shù)參數(shù)類型的哈希,然后將每個類型哈希(8個字節(jié))添加到std::vector。
對于集中特殊情況(類型& 0x10f == 0x103、類型& 0x103 == 0x101)有特殊處理,但是對于大多數(shù)參數(shù)類型,將返回到loc_180105541。在這個位置,如果需要(調(diào)用Type_t::clearModifiersAndQualifiers),則會清除表示要處理的參數(shù)類型的Type_t對象的限定符(例如const (0x800)和volatile (0x40)),然后清除8個字節(jié)的哈希。通過調(diào)用XFGHelper::XFGHasher::add_type,將參數(shù)類型添加到std::vector,我們可以在XFGHelper::XFGHasher::add_function_type+CC看到。至于XFGHelper::XFGHasher::add_type是如何精確計算給定Type_t的哈希,我們在后續(xù)章節(jié)進(jìn)行分析。
最后,如果還有更多參數(shù)需要哈希,就會跳轉(zhuǎn)到循環(huán)的開始部分。
XFGHelper::XFGHasher::add_function_type+6E ? loc_1801054F6:
XFGHelper::XFGHasher::add_function_type+6E ? ? ? ?mov ? ? rax, [rsi] ? ? ?; rax = &function_info- > params
XFGHelper::XFGHasher::add_function_type+71 ? ? ? ?mov ? ? rcx, [rax+rdi*8] ; rcx = function_info- > params[i] (Type_t)
XFGHelper::XFGHasher::add_function_type+75 ? ? ? ?mov ? ? edx, [rcx] ? ? ?; edx = params[i].type
XFGHelper::XFGHasher::add_function_type+77 ? ? ? ?mov ? ? eax, edx
XFGHelper::XFGHasher::add_function_type+79 ? ? ? ?and ? ? eax, 10Fh
XFGHelper::XFGHasher::add_function_type+7E ? ? ? ?cmp ? ? eax, 103h ? ? ? ; params[i].type & 0x10f == 0x103 ?
XFGHelper::XFGHasher::add_function_type+83 ? ? ? ?jnz ? ? short loc_18010552C
XFGHelper::XFGHasher::add_function_type+85 ? ? ? ?cmp ? ? edx, 8103h ? ? ?; params[i].type == 0x8103 ?
XFGHelper::XFGHasher::add_function_type+8B ? ? ? ?jz ? ? ?short loc_18010554E
XFGHelper::XFGHasher::add_function_type+8D ? ? ? ?mov ? ? r8d, [rcx+4]
XFGHelper::XFGHasher::add_function_type+91 ? ? ? ?lea ? ? edx, [rax-1]
XFGHelper::XFGHasher::add_function_type+94 ? ? ? ?mov ? ? rcx, [rcx+8]
XFGHelper::XFGHasher::add_function_type+98 ? ? ? ?btr ? ? r8d, 1Fh
XFGHelper::XFGHasher::add_function_type+9D ? ? ? ?call ? ?Type_t::createType(Type_t const *,uint,mod_t,bool)
XFGHelper::XFGHasher::add_function_type+A2 ? ? ? ?jmp ? ? short loc_18010554B
XFGHelper::XFGHasher::add_function_type+A4 ? ; --------------------------------------------------------------
XFGHelper::XFGHasher::add_function_type+A4
XFGHelper::XFGHasher::add_function_type+A4 ? loc_18010552C:
XFGHelper::XFGHasher::add_function_type+A4 ? ? ? ?and ? ? edx, 103h
XFGHelper::XFGHasher::add_function_type+AA ? ? ? ?cmp ? ? edx, 101h ? ? ? ; params[i].type & 0x103 == 0x101 ?
XFGHelper::XFGHasher::add_function_type+B0 ? ? ? ?jnz ? ? short loc_180105541
XFGHelper::XFGHasher::add_function_type+B2 ? ? ? ?call ? ?Type_t::decayFunctionType(void)
XFGHelper::XFGHasher::add_function_type+B7 ? ? ? ?jmp ? ? short loc_18010554B
XFGHelper::XFGHasher::add_function_type+B9 ? ; --------------------------------------------------------------
XFGHelper::XFGHasher::add_function_type+B9
XFGHelper::XFGHasher::add_function_type+B9 ? loc_180105541:
XFGHelper::XFGHasher::add_function_type+B9 ? ? ? ?mov ? ? edx, 8C0h ? ? ? ; discards qualifiers 0x800 (const) | 0x80 | 0x40 (volatile)
XFGHelper::XFGHasher::add_function_type+BE ? ? ? ?call ? ?Type_t::clearModifiersAndQualifiers(mod_t)
XFGHelper::XFGHasher::add_function_type+C3
XFGHelper::XFGHasher::add_function_type+C3 ? loc_18010554B:
XFGHelper::XFGHasher::add_function_type+C3 ? ? ? ? ? ? ? ? ? ? ; XFGHelper::XFGHasher::add_function_type+B7↑j
XFGHelper::XFGHasher::add_function_type+C3 ? ? ? ?mov ? ? rcx, rax
XFGHelper::XFGHasher::add_function_type+C6
XFGHelper::XFGHasher::add_function_type+C6 ? loc_18010554E:
XFGHelper::XFGHasher::add_function_type+C6 ? ? ? ?mov ? ? rdx, rcx ? ? ? ?; struct Type_t *
XFGHelper::XFGHasher::add_function_type+C9 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGHasher::add_function_type+CC ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type(Type_t const *) ; adds hash of params[i] type
XFGHelper::XFGHasher::add_function_type+CC ? ? ? ? ? ? ? ? ? ? ; [step 2]
XFGHelper::XFGHasher::add_function_type+D1 ? ? ? ?inc ? ? rdi
XFGHelper::XFGHasher::add_function_type+D4 ? ? ? ?cmp ? ? rdi, rbp ? ? ? ?; counter ?< ?number_of_params ?
XFGHelper::XFGHasher::add_function_type+D7 ? ? ? ?jb ? ? ?short loc_1801054F6 ; if so, loop
4.3 可變參數(shù)函數(shù)
下一步是向std::vector添加一個字節(jié),指示該函數(shù)是否可接受可變數(shù)量的參數(shù)。在大多數(shù)情況下,當(dāng)函數(shù)不包含來自__declspec的虛擬信息時,會采用以下代碼路徑:
XFGHelper::XFGHasher::add_function_type+D9 ? ? ? ?mov ? ? rcx, rsi ? ? ? ?; this = functioninfo
XFGHelper::XFGHasher::add_function_type+DC ? ? ? ?call ? ?FunctionTypeInfo_t::IsVarArgsFunction(void)
XFGHelper::XFGHasher::add_function_type+E1 ? ? ? ?mov ? ? r8b, al ? ? ? ? ; r8b = is_var_args_function
XFGHelper::XFGHasher::add_function_type+E4 ? ? ? ?test ? ?r14b, r14b ? ? ?; contains virtual info from __declspec?
XFGHelper::XFGHasher::add_function_type+E7 ? ? ? ?jz ? ? ?short loc_1801055EB
[...]
XFGHelper::XFGHasher::add_function_type+163 ?loc_1801055EB:
XFGHelper::XFGHasher::add_function_type+163 ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGHasher::add_function_type+167 ? ? ? ?lea ? ? r9, [rsp+48h+arg_10+1]
XFGHelper::XFGHasher::add_function_type+16C ? ? ? ?mov ? ? byte ptr [rsp+48h+arg_10], r8b ; value to add = is_var_args_function (byte)
XFGHelper::XFGHasher::add_function_type+16C ? ? ? ?; [step 3]
XFGHelper::XFGHasher::add_function_type+171 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGHasher::add_function_type+174 ? ? ? ?lea ? ? r8, [rsp+48h+arg_10]
XFGHelper::XFGHasher::add_function_type+179 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
4.4 調(diào)用約定
最后,XFGHelper::XFGHasher::add_function_type將一個4字節(jié)的值添加到std::vector,以指示該函數(shù)使用的調(diào)用約定。在Intel x64體系結(jié)構(gòu)中沒有太多的調(diào)用約定,這一點(diǎn)與x86不太一樣。默認(rèn)的x64調(diào)用約定在寄存器RCX、RDX、R8和R9中傳遞整數(shù)型參數(shù),而浮點(diǎn)型參數(shù)通過XMM0-XMM3傳遞。該默認(rèn)調(diào)用約定在內(nèi)部用0x201值來表示,但是由于在將其保存到std::vector之前,使用& 0x0F進(jìn)行了屏蔽(請參考下面的反匯編),因此我們很可能會看到一個值為0x00000001的DWORD寫入std::vector。
下面展示了將調(diào)用約定數(shù)據(jù)添加到std::vector的代碼。
XFGHelper::XFGHasher::add_function_type+17E ? ? ? ?mov ? ? eax, [r15+4] ? ?; eax = function_info- > calling_convention
XFGHelper::XFGHasher::add_function_type+182 ? ? ? ?lea ? ? r9, [rsp+48h+arg_14]
XFGHelper::XFGHasher::add_function_type+187 ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGHasher::add_function_type+18B ? ? ? ?lea ? ? r8, [rsp+48h+arg_10]
XFGHelper::XFGHasher::add_function_type+190 ? ? ? ?and ? ? eax, 0Fh ? ? ? ?; eax = calling_convention & 0xF
XFGHelper::XFGHasher::add_function_type+193 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGHasher::add_function_type+196 ? ? ? ?mov ? ? [rsp+48h+arg_10], eax ; value to add = calling_convention & 0xF (size = dword)
XFGHelper::XFGHasher::add_function_type+196 ? ? ? ? ? ? ? ? ? ? ?; [step 4]
XFGHelper::XFGHasher::add_function_type+19A ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
4.5 返回類型的哈希
數(shù)據(jù)的第五部分,也是最后一個組成部分,用于獲取函數(shù)原型哈希,它無法在XFGHelper::XFGHasher::add_function_type中檢索到,而是在返回后立即添加的。在下面的代碼中我們看到,它調(diào)用XFGHelper::XFGHasher::add_type,為表示返回類型的Type_t計算8字節(jié)的哈希,并將計算出來的8字節(jié)哈希值添加到std::vector。
XFGHelper__ComputeHash_1+BE ? ? ? ?call ? ?XFGHelper::XFGHasher::add_function_type(Type_t const *,XFGHelper::VirtualInfoFromDeclspec)
XFGHelper__ComputeHash_1+C3 ? ? ? ?mov ? ? rdx, rsi ? ? ? ?; rdx = function- > return_type (struct Type_t *)
XFGHelper__ComputeHash_1+C6 ? ? ? ?mov ? ? rcx, rbp ? ? ? ?; this
XFGHelper__ComputeHash_1+C9 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type(Type_t const *) ; (step 5)
4.6 最后一步:計算收集的原型數(shù)據(jù)的哈希值
如果該函數(shù)包含來自__declspec的虛擬信息,則會從該信息中生成一個附加的8字節(jié)類型的哈希,并將其添加到std::vector。但是,在測試期間,我沒能實(shí)現(xiàn)這種特殊情況。如前所述,虛擬信息可能不適用于C語言代碼。
無論是否存在來自__declspec的虛擬信息,XFGHelper__ComputeHash_1函數(shù)都可以通過調(diào)用XFGHelper::XFGHasher::get_hash函數(shù)來完成:
XFGHelper__ComputeHash_1+CE ? ? ? ?test ? ?rbx, rbx ? ? ? ?; contains virtual info from __declspec?
XFGHelper__ComputeHash_1+D1 ? ? ? ?jz ? ? ?short loc_1801052EF
[...]
XFGHelper__ComputeHash_1+103 ?loc_1801052EF:
XFGHelper__ComputeHash_1+103 ? ? ? ? ? ? ? ? ?mov ? ? rcx, rbp ? ? ? ?; this
XFGHelper__ComputeHash_1+106 ? ? ? ? ? ? ? ? ?mov ? ? rbx, [rsp+38h+arg_0]
XFGHelper__ComputeHash_1+10B ? ? ? ? ? ? ? ? ?mov ? ? rbp, [rsp+38h+arg_8]
XFGHelper__ComputeHash_1+110 ? ? ? ? ? ? ? ? ?mov ? ? rsi, [rsp+38h+arg_10]
XFGHelper__ComputeHash_1+115 ? ? ? ? ? ? ? ? ?add ? ? rsp, 30h
XFGHelper__ComputeHash_1+119 ? ? ? ? ? ? ? ? ?pop ? ? rdi
XFGHelper__ComputeHash_1+11A ? ? ? ? ? ? ? ? ?jmp ? ? XFGHelper::XFGHasher::get_hash(void)
XFGHelper__ComputeHash_1+11A ?XFGHelper__ComputeHash_1 endp
XFGHelper::XFGHasher::get_hash對在std::vector中收集的類型數(shù)據(jù)進(jìn)行哈希處理。我們在XFGHelper::XFGHasher::get_hash+5F中看到,選擇的哈希算法是SHA256,僅返回生成的SHA256摘要的前8個字節(jié):
XFGHelper::XFGHasher::get_hash(void) ? ? ?public: unsigned __int64 XFGHelper::XFGHasher::get_hash(void)const proc near
[...]
XFGHelper::XFGHasher::get_hash(void)+18 ? ? ? ?mov ? ? dl, 3 ? ? ? ? ? ; algorithm_ids[3] == CALG_SHA_256
XFGHelper::XFGHasher::get_hash(void)+1A ? ? ? ?lea ? ? rcx, [rsp+58h+hHash] ; phHash
XFGHelper::XFGHasher::get_hash(void)+1F ? ? ? ?call ? ?HashAPIWrapper::HashAPIWrapper(uchar)
XFGHelper::XFGHasher::get_hash(void)+24 ? ? ? ?nop
XFGHelper::XFGHasher::get_hash(void)+25 ? ? ? ?mov ? ? r8, [rbx+8]
XFGHelper::XFGHasher::get_hash(void)+29 ? ? ? ?sub ? ? r8, [rbx] ? ? ? ; dwDataLen
XFGHelper::XFGHasher::get_hash(void)+2C ? ? ? ?xor ? ? r9d, r9d ? ? ? ?; dwFlags
XFGHelper::XFGHasher::get_hash(void)+2F ? ? ? ?mov ? ? rdx, [rbx] ? ? ?; pbData
XFGHelper::XFGHasher::get_hash(void)+32 ? ? ? ?mov ? ? rcx, [rsp+58h+hHash] ; hHash
XFGHelper::XFGHasher::get_hash(void)+37 ? ? ? ?call ? ?cs:__imp_CryptHashData
XFGHelper::XFGHasher::get_hash(void)+3D ? ? ? ?test ? ?eax, eax
XFGHelper::XFGHasher::get_hash(void)+3F ? ? ? ?jnz ? ? short loc_180105822
[...]
XFGHelper::XFGHasher::get_hash(void)+4A ? loc_180105822:
XFGHelper::XFGHasher::get_hash(void)+4A ? ? ? ?mov ? ? r8d, 20h ; ' ' ?; unsigned int
XFGHelper::XFGHasher::get_hash(void)+50 ? ? ? ?lea ? ? rdx, [rsp+58h+sha256_digest] ; unsigned __int8 *
XFGHelper::XFGHasher::get_hash(void)+55 ? ? ? ?lea ? ? rcx, [rsp+58h+hHash] ; this
XFGHelper::XFGHasher::get_hash(void)+5A ? ? ? ?call ? ?HashAPIWrapper::GetHash(uchar *,ulong)
XFGHelper::XFGHasher::get_hash(void)+5F ? ? ? ?mov ? ? rbx, qword ptr [rsp+58h+sha256_digest] ; *** only returns first 8 bytes of SHA256 hash
XFGHelper::XFGHasher::get_hash(void)+64 ? ? ? ?mov ? ? rcx, [rsp+58h+hHash] ; hHash
XFGHelper::XFGHasher::get_hash(void)+69 ? ? ? ?call ? ?cs:__imp_CryptDestroyHash
XFGHelper::XFGHasher::get_hash(void)+6F ? ? ? ?test ? ?eax, eax
XFGHelper::XFGHasher::get_hash(void)+71 ? ? ? ?jnz ? ? short loc_180105854
[...]
XFGHelper::XFGHasher::get_hash(void)+7C ? loc_180105854:
XFGHelper::XFGHasher::get_hash(void)+7C ? ? ? ?mov ? ? rax, rbx
XFGHelper::XFGHasher::get_hash(void)+7F ? ? ? ?mov ? ? rcx, [rsp+58h+var_10]
XFGHelper::XFGHasher::get_hash(void)+84 ? ? ? ?xor ? ? rcx, rsp ? ? ? ?; StackCookie
XFGHelper::XFGHasher::get_hash(void)+87 ? ? ? ?call ? ?__security_check_cookie
XFGHelper::XFGHasher::get_hash(void)+8C ? ? ? ?add ? ? rsp, 50h
XFGHelper::XFGHasher::get_hash(void)+90 ? ? ? ?pop ? ? rbx
XFGHelper::XFGHasher::get_hash(void)+91 ? ? ? ?retn
五、哈希類型
到目前為止,我們知道函數(shù)原型哈希是基于五個信息構(gòu)建的。其中三個是普通值(參數(shù)數(shù)量、一個布爾值用于標(biāo)識函數(shù)是否參數(shù)可變、一個數(shù)字值表示正在使用的調(diào)用約定),而另外兩個是類型哈希(每個函數(shù)參數(shù)的類型哈希,以及返回類型的哈希)。在這一章中,我們將了解如何對類型(編譯器內(nèi)部使用Type_t對象表示)進(jìn)行哈希處理。
類型在XFGHelper::XFGHasher::add_type函數(shù)中計算哈希。它調(diào)用XFGHelper__GetHashForType,隨后返回該類型的8字節(jié)哈希,然后通過調(diào)用std::vector::_Insert_range()將8字節(jié)哈希存儲在std::vector中。
.text:00000001801056A0 public: void XFGHelper::XFGHasher::add_type(class Type_t const *) proc near
.text:00000001801056A0 arg_0 ? ? ? ? ? = qword ptr ?8
.text:00000001801056A0 arg_8 ? ? ? ? ? = byte ptr ?10h
.text:00000001801056A0
.text:00000001801056A0 ? ? ? ?push ? ?rbx
.text:00000001801056A2 ? ? ? ?sub ? ? rsp, 30h
.text:00000001801056A6 ? ? ? ?mov ? ? rbx, rcx
.text:00000001801056A9 ? ? ? ?mov ? ? rcx, rdx ? ? ? ?; rcx = Type_t
.text:00000001801056AC ? ? ? ?call ? ?XFGHelper__GetHashForType
.text:00000001801056B1 ? ? ? ?mov ? ? rdx, [rbx+8]
.text:00000001801056B5 ? ? ? ?lea ? ? r9, [rsp+38h+arg_8]
.text:00000001801056BA ? ? ? ?lea ? ? r8, [rsp+38h+arg_0]
.text:00000001801056BF ? ? ? ?mov ? ? [rsp+38h+arg_0], rax ; value to add = hash (qword)
.text:00000001801056C4 ? ? ? ?mov ? ? rcx, rbx
.text:00000001801056C7 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
.text:00000001801056CC ? ? ? ?add ? ? rsp, 30h
.text:00000001801056D0 ? ? ? ?pop ? ? rbx
.text:00000001801056D1 ? ? ? ?retn
我們來看看XFGHelper__GetHashForType是如何為指定的Type_t生成8字節(jié)哈希的。首先,它通過對std:Tree::emplace()的調(diào)用,來檢查指定類型的哈希是否存在于它保存的緩存中,我們可以在XFGHelper__GetHashForType+AF發(fā)現(xiàn)這一點(diǎn)。如果滿足條件,則只返回緩存的類型哈希。這樣,就可以避免一遍又一遍地計算已知類型的哈希值。
如果在緩存中沒有找到類型哈希,則通過調(diào)用XFGHelper::XFGTypeHasher::compute_hash從頭開始計算哈希,將會使用要計算哈希的類型數(shù)據(jù)構(gòu)建std::vector,最后調(diào)用XFGHelper::XFGHasher::get_hash,它會生成std::vector中包含的數(shù)據(jù)的SHA256摘要,并返回這個摘要的前8個字節(jié)。
XFGHelper__GetHashForType ? ? ?XFGHelper__GetHashForType proc near
[...]
XFGHelper__GetHashForType+A3 ? ? ? ?lea ? ? r9, [rbp+arg_8]
XFGHelper__GetHashForType+A7 ? ? ? ?lea ? ? r8, [rbp+Type_t]
XFGHelper__GetHashForType+AB ? ? ? ?lea ? ? rdx, [rbp+xfg_type_hasher]
XFGHelper__GetHashForType+AF ? ? ? ?call ? ?std::_Tree < std::_Tmap_traits < Type_t const *,unsigned __int64,std::less < Type_t const * > ,std::allocator < std::pair < Type_t const * const,unsigned __int64 > ?> ,0 > ?> ::_Emplace < Type_t const * &,int > (Type_t const * &,int &&)
XFGHelper__GetHashForType+B4 ? ? ? ?mov ? ? rbx, qword ptr [rbp+xfg_type_hasher]
XFGHelper__GetHashForType+B8 ? ? ? ?cmp ? ? byte ptr [rbp+xfg_type_hasher+8], 0 ; hash for type was found in cache?
XFGHelper__GetHashForType+BC ? ? ? ?jz ? ? ?short loc_18010544D ; if so, just return the cached hash
XFGHelper__GetHashForType+BE ? ? ? ?xor ? ? edi, edi ? ? ? ?; otherwise, compute the hash of the type
XFGHelper__GetHashForType+C0 ? ? ? ?xorps ? xmm0, xmm0
XFGHelper__GetHashForType+C3 ? ? ? ?movdqu ?[rbp+xfg_type_hasher], xmm0
XFGHelper__GetHashForType+C8 ? ? ? ?and ? ? [rbp+var_10], rdi
XFGHelper__GetHashForType+CC ? ? ? ?mov ? ? [rbp+var_8], 1
XFGHelper__GetHashForType+D0 ? ? ? ?mov ? ? rdx, [rbp+Type_t] ; struct Type_t *
XFGHelper__GetHashForType+D4 ? ? ? ?lea ? ? rcx, [rbp+xfg_type_hasher] ; this
XFGHelper__GetHashForType+D8 ? ? ? ?call ? ?XFGHelper::XFGTypeHasher::compute_hash(Type_t const *)
XFGHelper__GetHashForType+DD ? ? ? ?nop
XFGHelper__GetHashForType+DE ? ? ? ?cmp ? ? [rbp+var_8], dil
XFGHelper__GetHashForType+E2 ? ? ? ?jz ? ? ?short loc_180105434
XFGHelper__GetHashForType+E4 ? ? ? ?lea ? ? rcx, [rbp+xfg_type_hasher] ; this
XFGHelper__GetHashForType+E8 ? ? ? ?call ? ?XFGHelper::XFGHasher::get_hash(void)
[...]
這些是XFGHelper::XFGTypeHasher::compute_hash收集的特定類型信息:
(1)從類型限定符得到的1個字節(jié)(從Type_t對象的偏移量4處獲取);
(2)指示類型的1個字節(jié)(指針、union/struct/enum、原始類型);
(3)一些特定于類型的數(shù)據(jù),具體取決于類型屬于(2)中的哪一個類型。
接下來,我們將詳細(xì)分析這三部分信息。
5.1 類型限定符
第一部分是限定符,作為DWORD存儲在Type_t對象的偏移量4的位置。關(guān)于const (0x800)和volatile (0x40)限定符的信息將被組合寫入到std::vector單字節(jié)中。這個新字節(jié)的第一位負(fù)責(zé)指示是否存在const限定符,第二位指示是否存在volatile類型的限定符。
XFGHelper::XFGTypeHasher::compute_hash+1B ? ? ? ?call ? ?Type_t::getFirstNonArrayType(void)
XFGHelper::XFGTypeHasher::compute_hash+20 ? ? ? ?mov ? ? rcx, rdi ? ? ? ?; this
XFGHelper::XFGTypeHasher::compute_hash+23 ? ? ? ?mov ? ? r8d, [rax+4] ? ?; r8d = Type_t- > qualifiers
XFGHelper::XFGTypeHasher::compute_hash+27 ? ? ? ?shr ? ? r8d, 0Bh
XFGHelper::XFGTypeHasher::compute_hash+2B ? ? ? ?and ? ? r8b, 1
XFGHelper::XFGTypeHasher::compute_hash+2F ? ? ? ?movzx ? r9d, r8b ? ? ? ?; r9d = (Type_t- > qualifiers ?> ?> ?0xB) & 1 (has_const_qualifier)
XFGHelper::XFGTypeHasher::compute_hash+33 ? ? ? ?call ? ?Type_t::getFirstNonArrayType(void)
XFGHelper::XFGTypeHasher::compute_hash+38 ? ? ? ?lea ? ? r8, [rbp+arg_0]
XFGHelper::XFGTypeHasher::compute_hash+3C ? ? ? ?mov ? ? edx, [rax+4] ? ?; edx = Type_t- > qualifiers
XFGHelper::XFGTypeHasher::compute_hash+3F ? ? ? ?mov ? ? al, r9b ? ? ? ? ; al = has_const_qualifier
XFGHelper::XFGTypeHasher::compute_hash+42 ? ? ? ?or ? ? ?al, 2 ? ? ? ? ? ; al = has_const_qualifier | 2
XFGHelper::XFGTypeHasher::compute_hash+44 ? ? ? ?and ? ? dl, 40h ? ? ? ? ; dl = Type_t- > qualifiers & 0x40 (has_volatile_qualifier)
XFGHelper::XFGTypeHasher::compute_hash+47 ? ? ? ?movzx ? ecx, al ? ? ? ? ; qualifiers_info = has_const_qualifier | 2
XFGHelper::XFGTypeHasher::compute_hash+4A ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::compute_hash+4E ? ? ? ?cmovz ? ecx, r9d ? ? ? ?; if it doesn't have volatile qualifier, then
XFGHelper::XFGTypeHasher::compute_hash+4E ? ? ? ? ? ? ? ? ? ? ; qualifiers_info = has_const_qualifier
XFGHelper::XFGTypeHasher::compute_hash+52 ? ? ? ?lea ? ? r9, [rbp+arg_1]
XFGHelper::XFGTypeHasher::compute_hash+56 ? ? ? ?mov ? ? [rbp+arg_0], cl ; value to insert (size = byte)
XFGHelper::XFGTypeHasher::compute_hash+59 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::compute_hash+5C ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
5.2 類型組
如果存儲在Type_t中的類型值設(shè)置為0x100,則它是一個指針。通過將值為3的字節(jié)寫入到std::vector來發(fā)出信號。
XFGHelper::XFGTypeHasher::compute_hash+61 ? ? ? ?test ? ?dword ptr [rdi], 100h ; *Type_t & 0x100 == 0 ?
XFGHelper::XFGTypeHasher::compute_hash+67 ? ? ? ?jz ? ? ?short loc_180105762
XFGHelper::XFGTypeHasher::compute_hash+69 ? ? ? ?mov ? ? rdx, [rbx+8] ? ?; if not, it's a pointer
XFGHelper::XFGTypeHasher::compute_hash+6D ? ? ? ?lea ? ? r9, [rbp+arg_1]
XFGHelper::XFGTypeHasher::compute_hash+71 ? ? ? ?lea ? ? r8, [rbp+arg_0]
XFGHelper::XFGTypeHasher::compute_hash+75 ? ? ? ?mov ? ? [rbp+arg_0], 3 ?; value to insert: POINTER_TYPE (3)
XFGHelper::XFGTypeHasher::compute_hash+79 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::compute_hash+7C ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
如果類型不是指針,則會檢查存儲在Type_t & 0x600的值是否為0,以確認(rèn)它是union、struct還是enum。請注意,0x600是0x200 | 0x400,其中0x200表示enum類型,0x400表示union和struct。如果滿足這個條件,會將值為2的字節(jié)寫入std::vector。
XFGHelper::XFGTypeHasher::compute_hash+8E ? loc_180105762:
XFGHelper::XFGTypeHasher::compute_hash+8E ? ? ? ?test ? ?dword ptr [rdi], 600h ; *Type_t & (0x400 | 0x200) == 0 ?
XFGHelper::XFGTypeHasher::compute_hash+94 ? ? ? ?jz ? ? ?short loc_180105790
XFGHelper::XFGTypeHasher::compute_hash+96 ? ? ? ?mov ? ? rdx, [rbx+8] ? ?; if not, it's a union/struct/enum
XFGHelper::XFGTypeHasher::compute_hash+9A ? ? ? ?lea ? ? r9, [rbp+arg_1]
XFGHelper::XFGTypeHasher::compute_hash+9E ? ? ? ?lea ? ? r8, [rbp+arg_0]
XFGHelper::XFGTypeHasher::compute_hash+A2 ? ? ? ?mov ? ? [rbp+arg_0], 2 ?; value to insert: UNION_STRUCT_OR_ENUM_TYPE (2)
XFGHelper::XFGTypeHasher::compute_hash+A6 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::compute_hash+A9 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
最后,如果類型既不是指針,也不是union/struct/enum,則采用默認(rèn)情況。如果類型是泛型的,則不會將任何內(nèi)容寫入到std::vector(但這是一種邊界情況,僅影響設(shè)置了值0x1000的類型,以及標(biāo)識為值0x8103的類型)。否則,如果是絕大多數(shù)的基本類型,會將值為1的字節(jié)添加到std::vector。
XFGHelper::XFGTypeHasher::compute_hash+BC ? loc_180105790:
XFGHelper::XFGTypeHasher::compute_hash+BC ? ? ? ?mov ? ? rcx, rdi ? ? ? ?; this
XFGHelper::XFGTypeHasher::compute_hash+BF ? ? ? ?call ? ?Type_t::isGeneric(void)
XFGHelper::XFGTypeHasher::compute_hash+C4 ? ? ? ?test ? ?al, al
XFGHelper::XFGTypeHasher::compute_hash+C6 ? ? ? ?jz ? ? ?short loc_1801057A2
XFGHelper::XFGTypeHasher::compute_hash+C8 ? ? ? ?mov ? ? byte ptr [rbx+18h], 0
XFGHelper::XFGTypeHasher::compute_hash+CC ? ? ? ?jmp ? ? short epilog
XFGHelper::XFGTypeHasher::compute_hash+CE ? loc_1801057A2:
XFGHelper::XFGTypeHasher::compute_hash+CE ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::compute_hash+D2 ? ? ? ?lea ? ? r9, [rbp+arg_1]
XFGHelper::XFGTypeHasher::compute_hash+D6 ? ? ? ?lea ? ? r8, [rbp+arg_0]
XFGHelper::XFGTypeHasher::compute_hash+DA ? ? ? ?mov ? ? [rbp+arg_0], 1 ?; value to insert: PRIMITIVE_TYPE (1)
XFGHelper::XFGTypeHasher::compute_hash+DE ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::compute_hash+E1 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
5.3 特定類型的數(shù)據(jù)
5.3.1 指針類型的哈希
對于指針類型,在將值為3的字節(jié)寫入std::vector后,將調(diào)用XFGHelper::XFGTypeHasher::hash_indirection函數(shù)。這里的指針定義要更為寬泛,因?yàn)槠渲邪ㄋ兄禐?x100的Type_t對象。除了常規(guī)的C指針外,還包括一種內(nèi)部函數(shù)對象(由函數(shù)指針引用)和數(shù)組。
XFGHelper::XFGTypeHasher::compute_hash+81 ? ? ? ?mov ? ? rdx, rdi ? ? ? ?; struct Type_t *
XFGHelper::XFGTypeHasher::compute_hash+84 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGTypeHasher::compute_hash+87 ? ? ? ?call ? ?XFGHelper::XFGTypeHasher::hash_indirection
XFGHelper::XFGTypeHasher::compute_hash+8C ? ? ? ?jmp ? ? short epilog
顧名思義,函數(shù)XFGHelper::XFGTypeHasher::hash_indirection將由指向std::vector的指針引用的類型的哈希值添加。其行為取決于所處理的指針的類型:
(1)如果是函數(shù)指針(Type_t值為0x106),或Type_t值為0x102的通用指針,則通過調(diào)用XFGHelper::XFGHasher::add_type添加指針引用的Type_t的哈希,再加上值為2的字節(jié)。對于函數(shù)指針,指針引用的Type_t是一種內(nèi)部函數(shù)對象,Type_t值為0x101,這意味著它也在XFGHelper::XFGTypeHasher::hash_indirection中進(jìn)行處理。
XFGHelper::XFGTypeHasher::hash_indirection+15 ? ? ? ?mov ? ? ecx, [rdx] ? ? ?; ecx = *Type_t
XFGHelper::XFGTypeHasher::hash_indirection+17 ? ? ? ?mov ? ? eax, ecx
XFGHelper::XFGTypeHasher::hash_indirection+19 ? ? ? ?and ? ? eax, 10Fh
[...]
XFGHelper::XFGTypeHasher::hash_indirection+25 ? ? ? ?sub ? ? eax, 1 ? ? ? ? ?; case 0x102 (general pointer):
XFGHelper::XFGTypeHasher::hash_indirection+28 ? ? ? ?jz ? ? ?short loc_1801058E3
[...]
XFGHelper::XFGTypeHasher::hash_indirection+2F ? ? ? ?cmp ? ? eax, 3 ? ? ? ? ?; case 0x106 (function pointer):
XFGHelper::XFGTypeHasher::hash_indirection+32 ? ? ? ?jz ? ? ?short loc_1801058E3
[...]
XFGHelper::XFGTypeHasher::hash_indirection+6B ? loc_1801058E3:
XFGHelper::XFGTypeHasher::hash_indirection+6B ? ? ? ?mov ? ? dil, 2 ? ? ? ? ?; will be written to std::vector
XFGHelper::XFGTypeHasher::hash_indirection+6E ? ? ? ?jmp ? ? short loc_1801058F6
[...]
XFGHelper::XFGTypeHasher::hash_indirection+7E ? loc_1801058F6:
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ?mov ? ? rdx, [rsi+8] ? ?; rdx = ptr to the Type_t referenced by the pointer
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ? ? ? ? ? ? ? ; (return type in the case of functions)
XFGHelper::XFGTypeHasher::hash_indirection+82 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGTypeHasher::hash_indirection+85 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type
XFGHelper::XFGTypeHasher::hash_indirection+8A ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::hash_indirection+8E ? ? ? ?lea ? ? r9, [rsp+38h+arg_8+1]
XFGHelper::XFGTypeHasher::hash_indirection+93 ? ? ? ?lea ? ? r8, [rsp+38h+arg_8]
XFGHelper::XFGTypeHasher::hash_indirection+98 ? ? ? ?mov ? ? byte ptr [rsp+38h+arg_8], dil ; value to insert (size = byte)
XFGHelper::XFGTypeHasher::hash_indirection+9D ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::hash_indirection+A0 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
如果它是一個函數(shù)對象(Type_t值為0x101,通常由Type_t值為0x106的函數(shù)指針引用),它通過調(diào)用XFGHelper::XFGHasher::add_function_type函數(shù)以及函數(shù)返回類型的哈希值來添加函數(shù)原型的哈希值,再加上一個值為1的字節(jié)。
XFGHelper::XFGTypeHasher::hash_indirection+15 ? ? ? ?mov ? ? ecx, [rdx] ? ? ?; ecx = *Type_t
XFGHelper::XFGTypeHasher::hash_indirection+17 ? ? ? ?mov ? ? eax, ecx
XFGHelper::XFGTypeHasher::hash_indirection+19 ? ? ? ?and ? ? eax, 10Fh
XFGHelper::XFGTypeHasher::hash_indirection+1E ? ? ? ?sub ? ? eax, 101h ? ? ? ; case 0x101 (function):
XFGHelper::XFGTypeHasher::hash_indirection+23 ? ? ? ?jz ? ? ?short loc_1801058E8
[...]
XFGHelper::XFGTypeHasher::hash_indirection+70 ? ? ? ?xor ? ? r8d, r8d
XFGHelper::XFGTypeHasher::hash_indirection+73 ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::hash_indirection+76 ? ? ? ?mov ? ? dil, 1 ? ? ? ? ?; this is written to std::vector at the end of this function
XFGHelper::XFGTypeHasher::hash_indirection+79 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_function_type(Type_t const *,XFGHelper::VirtualInfoFromDeclspec)
XFGHelper::XFGTypeHasher::hash_indirection+7E
XFGHelper::XFGTypeHasher::hash_indirection+7E ? loc_1801058F6:
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ? ? ? ? ? ? ? ; XFGHelper::XFGTypeHasher::hash_indirection+6E↑j
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ?mov ? ? rdx, [rsi+8] ? ?; rdx = ptr to the Type_t referenced by the pointer
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ? ? ? ? ? ? ? ; (return type in the case of functions)
XFGHelper::XFGTypeHasher::hash_indirection+82 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGTypeHasher::hash_indirection+85 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type
XFGHelper::XFGTypeHasher::hash_indirection+8A ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::hash_indirection+8E ? ? ? ?lea ? ? r9, [rsp+38h+arg_8+1]
XFGHelper::XFGTypeHasher::hash_indirection+93 ? ? ? ?lea ? ? r8, [rsp+38h+arg_8]
XFGHelper::XFGTypeHasher::hash_indirection+98 ? ? ? ?mov ? ? byte ptr [rsp+38h+arg_8], dil ; value to insert (size = byte)
XFGHelper::XFGTypeHasher::hash_indirection+9D ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::hash_indirection+A0 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
最后,如果它是一個數(shù)組(Type_t值為0x103),則會寫入一個QWORD,其中包含數(shù)組中元素的數(shù)量、數(shù)組元素類型的哈希、值為6的單字節(jié)。
XFGHelper::XFGTypeHasher::hash_indirection+15 ? ? ? ?mov ? ? ecx, [rdx] ? ? ?; ecx = *Type_t
XFGHelper::XFGTypeHasher::hash_indirection+17 ? ? ? ?mov ? ? eax, ecx
XFGHelper::XFGTypeHasher::hash_indirection+19 ? ? ? ?and ? ? eax, 10Fh
[...]
XFGHelper::XFGTypeHasher::hash_indirection+2A ? ? ? ?sub ? ? eax, 1 ? ? ? ? ?; case 0x103 (array passed by pointer):
XFGHelper::XFGTypeHasher::hash_indirection+2D ? ? ? ?jz ? ? ?short loc_1801058B2
[...]
XFGHelper::XFGTypeHasher::hash_indirection+3A ? loc_1801058B2:
XFGHelper::XFGTypeHasher::hash_indirection+3A ? ? ? ?lea ? ? eax, [rcx-4103h]
XFGHelper::XFGTypeHasher::hash_indirection+40 ? ? ? ?mov ? ? dil, 6 ? ? ? ? ?; will be written to std::vector
XFGHelper::XFGTypeHasher::hash_indirection+43 ? ? ? ?test ? ?eax, 0FFFFBFFFh
XFGHelper::XFGTypeHasher::hash_indirection+48 ? ? ? ?jz ? ? ?short loc_1801058AC
XFGHelper::XFGTypeHasher::hash_indirection+4A ? ? ? ?mov ? ? rax, [rdx+10h] ?; rax = number of elems in array
XFGHelper::XFGTypeHasher::hash_indirection+4E ? ? ? ?lea ? ? r9, [rsp+38h+arg_10]
XFGHelper::XFGTypeHasher::hash_indirection+53 ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::hash_indirection+57 ? ? ? ?lea ? ? r8, [rsp+38h+arg_8]
XFGHelper::XFGTypeHasher::hash_indirection+5C ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::hash_indirection+5F ? ? ? ?mov ? ? [rsp+38h+arg_8], rax ; value to insert: number of elems in array (size = qword)
XFGHelper::XFGTypeHasher::hash_indirection+64 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
XFGHelper::XFGTypeHasher::hash_indirection+69 ? ? ? ?jmp ? ? short loc_1801058F6
[...]
XFGHelper::XFGTypeHasher::hash_indirection+7E ? loc_1801058F6
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ?mov ? ? rdx, [rsi+8] ? ?; rdx = ptr to the Type_t referenced by the pointer
XFGHelper::XFGTypeHasher::hash_indirection+7E ? ? ? ? ? ? ? ? ? ? ; (return type in the case of functions)
XFGHelper::XFGTypeHasher::hash_indirection+82 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGTypeHasher::hash_indirection+85 ? ? ? ?call ? ?XFGHelper::XFGHasher::add_type
XFGHelper::XFGTypeHasher::hash_indirection+8A ? ? ? ?mov ? ? rdx, [rbx+8]
XFGHelper::XFGTypeHasher::hash_indirection+8E ? ? ? ?lea ? ? r9, [rsp+38h+arg_8+1]
XFGHelper::XFGTypeHasher::hash_indirection+93 ? ? ? ?lea ? ? r8, [rsp+38h+arg_8]
XFGHelper::XFGTypeHasher::hash_indirection+98 ? ? ? ?mov ? ? byte ptr [rsp+38h+arg_8], dil ; value to insert (size = byte)
XFGHelper::XFGTypeHasher::hash_indirection+9D ? ? ? ?mov ? ? rcx, rbx
XFGHelper::XFGTypeHasher::hash_indirection+A0 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
5.3.2 union/struct/enum類型的哈希
在處理union/struct/enum時,將值為2的字節(jié)寫入std::vector后,函數(shù)XFGHelper::XFGTypeHasher::compute_hash調(diào)用XFGHelper::XFGTypeHasher::hash_tag,在RDX中將指向Symbol_t的指針作為參數(shù)傳遞,其中包括union/struct/enum類型的可讀名稱的對象。
XFGHelper::XFGTypeHasher::compute_hash+AE ? ? ? ?mov ? ? rdx, [rdi+10h] ?; struct Symbol_t *
XFGHelper::XFGTypeHasher::compute_hash+B2 ? ? ? ?mov ? ? rcx, rbx ? ? ? ?; this
XFGHelper::XFGTypeHasher::compute_hash+B5 ? ? ? ?call ? ?XFGHelper::XFGTypeHasher::hash_tag(Symbol_t *)
XFGHelper::XFGTypeHasher::hash_tag調(diào)用XFGHelper::XFGHasher::add_string,將union/struct/enum的名稱添加到std::vector(命名情況下)。如果union/struct/enum是匿名的,則會將字符串“ < unnamed > ”添加到std::vector。
XFGHelper::XFGHasher::add_string ? ? ?public: void XFGHelper::XFGHasher::add_string(class Symbol_t *) proc near
XFGHelper::XFGHasher::add_string ? ? ? ? ? sub ? ? rsp, 38h
XFGHelper::XFGHasher::add_string+4 ? ? ? ? cmp ? ? byte ptr [rdx+11h], 4
XFGHelper::XFGHasher::add_string+8 ? ? ? ? jnz ? ? short loc_18010568B
XFGHelper::XFGHasher::add_string+A ? ? ? ? mov ? ? r8, [rdx]
XFGHelper::XFGHasher::add_string+D ? ? ? ? mov ? ? eax, [r8+10h]
XFGHelper::XFGHasher::add_string+11 ? ? ? ?shr ? ? eax, 16h
XFGHelper::XFGHasher::add_string+14 ? ? ? ?test ? ?al, 1 ? ? ? ? ? ; union/struct/enum is named?
XFGHelper::XFGHasher::add_string+16 ? ? ? ?jz ? ? ?short loc_180105674
XFGHelper::XFGHasher::add_string+18 ? ? ? ?lea ? ? r9, aUnnamed+9 ?; ""
XFGHelper::XFGHasher::add_string+1F ? ? ? ?lea ? ? r8, aUnnamed ? ?; " < unnamed > "
XFGHelper::XFGHasher::add_string+26
XFGHelper::XFGHasher::add_string+26 ? loc_180105666:
XFGHelper::XFGHasher::add_string+26 ? ? ? ?mov ? ? rdx, [rcx+8]
XFGHelper::XFGHasher::add_string+2A ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
XFGHelper::XFGHasher::add_string+2F ? ? ? ?add ? ? rsp, 38h
XFGHelper::XFGHasher::add_string+33 ? ? ? ?retn
XFGHelper::XFGHasher::add_string+34 ? ; ---------------------------------------------------------------------------
XFGHelper::XFGHasher::add_string+34
XFGHelper::XFGHasher::add_string+34 ? loc_180105674:
XFGHelper::XFGHasher::add_string+34 ? ? ? ?mov ? ? r8, [r8+8] ? ? ?; r8 = union/struct/enum name
XFGHelper::XFGHasher::add_string+38 ? ? ? ?or ? ? ?r9, 0FFFFFFFFFFFFFFFFh
XFGHelper::XFGHasher::add_string+3C
XFGHelper::XFGHasher::add_string+3C ? loc_18010567C:
XFGHelper::XFGHasher::add_string+3C ? ? ? ?inc ? ? r9
XFGHelper::XFGHasher::add_string+3F ? ? ? ?cmp ? ? byte ptr [r8+r9], 0
XFGHelper::XFGHasher::add_string+44 ? ? ? ?jnz ? ? short loc_18010567C
XFGHelper::XFGHasher::add_string+46 ? ? ? ?add ? ? r9, r8 ? ? ? ? ?; r9 points to end of string
XFGHelper::XFGHasher::add_string+49 ? ? ? ?jmp ? ? short loc_180105666
之后,函數(shù)XFGHelper::XFGTypeHasher::hash_tag中有一個代碼分支,可以在某些情況下將字符串" < local > "添加到需要計算哈希的數(shù)據(jù)中。我們對此沒有進(jìn)行太多研究,但它可能處理了本地范圍的union/struct/enum的情況。
XFGHelper::XFGTypeHasher::hash_tag+4D ? ? ? ?mov ? ? rbx, [rbx+18h]
XFGHelper::XFGTypeHasher::hash_tag+51 ? ? ? ?test ? ?rbx, rbx
XFGHelper::XFGTypeHasher::hash_tag+54 ? ? ? ?jnz ? ? short loc_180105A16
XFGHelper::XFGTypeHasher::hash_tag+56 ? ? ? ?jmp ? ? short loc_180105A76
XFGHelper::XFGTypeHasher::hash_tag+58 ? ; ---------------------------------------------------------------------------
XFGHelper::XFGTypeHasher::hash_tag+58
XFGHelper::XFGTypeHasher::hash_tag+58 ? loc_180105A5C:
XFGHelper::XFGTypeHasher::hash_tag+58 ? ? ? ?mov ? ? rdx, [rdi+8]
XFGHelper::XFGTypeHasher::hash_tag+5C ? ? ? ?lea ? ? r9, aLocal+7 ? ?; ""
XFGHelper::XFGTypeHasher::hash_tag+63 ? ? ? ?lea ? ? r8, aLocal ? ? ?; " < local > "
XFGHelper::XFGTypeHasher::hash_tag+6A ? ? ? ?mov ? ? rcx, rdi
XFGHelper::XFGTypeHasher::hash_tag+6D ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
5.3.3 原始類型的哈希
在處理原始類型時(在Type_t值中未設(shè)置0x100、0x200或0x400的原始類型),在將值為1的字節(jié)寫入std::vector后,函數(shù)XFGHelper::XFGTypeHasher::compute_hash會調(diào)用XFGHelper::XFGTypeHasher::hash_primitive。
XFGHelper::XFGTypeHasher::hash_primitive基本上是一個很大的switch語句,它將Type_t值映射到代表原始類型的一組不同常量。然后,將得到的常數(shù)(單個字節(jié))添加到std::vector。例如,對于以Type_t 0x26表示的浮點(diǎn)型,該函數(shù)將一個值為0x0B的字節(jié)添加到std::vector。
XFGHelper::XFGTypeHasher::hash_primitive ? ? ?private: void XFGHelper::XFGTypeHasher::hash_primitive(class Type_t const *) proc near
XFGHelper::XFGTypeHasher::hash_primitive ? ? ? ? ? sub ? ? rsp, 38h
XFGHelper::XFGTypeHasher::hash_primitive+4 ? ? ? ? mov ? ? eax, [rdx]
XFGHelper::XFGTypeHasher::hash_primitive+6 ? ? ? ? mov ? ? r10, rcx
XFGHelper::XFGTypeHasher::hash_primitive+9 ? ? ? ? and ? ? eax, 1FFFh
XFGHelper::XFGTypeHasher::hash_primitive+E ? ? ? ? cmp ? ? eax, 40h ; '@'
XFGHelper::XFGTypeHasher::hash_primitive+11 ? ? ? ?ja ? ? ?loc_1801059D4
XFGHelper::XFGTypeHasher::hash_primitive+17 ? ? ? ?jz ? ? ?loc_1801059D0 ? ; case 0x40:
XFGHelper::XFGTypeHasher::hash_primitive+1D ? ? ? ?cmp ? ? eax, 1Ah
XFGHelper::XFGTypeHasher::hash_primitive+20 ? ? ? ?ja ? ? ?short loc_18010599E
[...]
XFGHelper::XFGTypeHasher::hash_primitive+6E ? loc_18010599E:
XFGHelper::XFGTypeHasher::hash_primitive+6E ? ? ? ?sub ? ? eax, 1Bh ? ? ? ?; case 0x1B:
XFGHelper::XFGTypeHasher::hash_primitive+71 ? ? ? ?jz ? ? ?short loc_1801059CC
XFGHelper::XFGTypeHasher::hash_primitive+73 ? ? ? ?sub ? ? eax, 1 ? ? ? ? ?; case 0x1C:
XFGHelper::XFGTypeHasher::hash_primitive+76 ? ? ? ?jz ? ? ?short loc_1801059C8
XFGHelper::XFGTypeHasher::hash_primitive+78 ? ? ? ?sub ? ? eax, 2 ? ? ? ? ?; case 0x1E:
XFGHelper::XFGTypeHasher::hash_primitive+7B ? ? ? ?jz ? ? ?short loc_1801059C4
XFGHelper::XFGTypeHasher::hash_primitive+7D ? ? ? ?sub ? ? eax, 8 ? ? ? ? ?; case 0x26 (float):
XFGHelper::XFGTypeHasher::hash_primitive+80 ? ? ? ?jz ? ? ?short loc_1801059C0
[...]
XFGHelper::XFGTypeHasher::hash_primitive+90 ? loc_1801059C0:
XFGHelper::XFGTypeHasher::hash_primitive+90 ? ? ? ?mov ? ? cl, 0Bh ? ? ? ? ; primitive_type = 0xB (float)
XFGHelper::XFGTypeHasher::hash_primitive+92 ? ? ? ?jmp ? ? short loc_1801059DE
[...]
XFGHelper::XFGTypeHasher::hash_primitive+AE ? loc_1801059DE:
XFGHelper::XFGTypeHasher::hash_primitive+AE ? ? ? ?mov ? ? rdx, [r10+8]
XFGHelper::XFGTypeHasher::hash_primitive+B2 ? ? ? ?lea ? ? r9, [rsp+38h+arg_9]
XFGHelper::XFGTypeHasher::hash_primitive+B7 ? ? ? ?mov ? ? [rsp+38h+arg_8], cl ; value to add: primitive_type
XFGHelper::XFGTypeHasher::hash_primitive+BB ? ? ? ?lea ? ? r8, [rsp+38h+arg_8]
XFGHelper::XFGTypeHasher::hash_primitive+C0 ? ? ? ?mov ? ? rcx, r10
XFGHelper::XFGTypeHasher::hash_primitive+C3 ? ? ? ?call ? ?std::vector < uchar > ::_Insert_range < uchar const * > (std::_Vector_const_iterator < std::_Vector_val < std::_Simple_types < uchar > ?> ?> ,uchar const *,uchar const *,std::forward_iterator_tag)
六、最終轉(zhuǎn)換
到目前為止,我們已經(jīng)深入描述了C編譯器前端如何為XFG機(jī)制來計算函數(shù)原型的哈希。我們可以用類似Python的偽代碼來進(jìn)行概括,函數(shù)的哈希是通過以下方式構(gòu)建的:
hash = ?sha256(number_of_params +
? ? ? ? ? ? ? type_hash(params[0]) +
? ? ? ? ? ? ? type_hash(params[...]) +
? ? ? ? ? ? ? type_hash(params[n]) +
? ? ? ? ? ? ? is_variadic +
? ? ? ? ? ? ? calling_convention +
? ? ? ? ? ? ? type_hash(return_type)
? ? ? ? )[0:8]
XFG函數(shù)哈希是SHA256摘要的一部分,僅保留了前8個字節(jié),因此與完整的SHA256哈希相比,它們的抗沖突性有所降低,但是我們可以預(yù)期,不同的XFG哈希可以在一定程度上保證哈希的功能。
但是,如果針對特定的二進(jìn)制文件,檢查其XFG哈希(這里選擇了ntdll.dll),我們會注意到,它們似乎沒有64位上熵:
function 0x180001a30 - > ?prototype hash: 0x8d952e0d365aa071
function 0x180001b50 - > ?prototype hash: 0xe2198f4a3c515871
function 0x180001dc0 - > ?prototype hash: 0xbeac2e06165fc871
function 0x180001de0 - > ?prototype hash: 0xfaec0e7f70d92371
function 0x180001fc0 - > ?prototype hash: 0xc5d11eb750d75871
function 0x180002030 - > ?prototype hash: 0xe8bcaf9a10586871
function 0x180002040 - > ?prototype hash: 0xc3110f087e584871
function 0x1800020b0 - > ?prototype hash: 0xdbc1261858d2f871
function 0x1800023a0 - > ?prototype hash: 0xda690f3e36531a71
其背后的原因是,由編譯器前端(c1.dll)生成的SHA256片段,在實(shí)際寫入到生成的目標(biāo)文件之前,會由編譯器后端(c2.dll)進(jìn)行最終轉(zhuǎn)換。確切的說,c2.dll中的XfgIlVisitor::visit_I_XFG_HASH函數(shù)將兩個掩碼應(yīng)用到了截斷的SHA256哈希上:
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+5B ? ? ? ?mov ? ? rcx, 8000060010500070h
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+65 ? ? ? ?mov ? ? r13, 0FFFDBFFF7EDFFB70h
[...]
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+E9 ? ? ? ?mov ? ? rdx, [rax] ? ? ?; rdx = 8 bytes of SHA256 hash
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+EC ? ? ? ?add ? ? rax, 8
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+F0 ? ? ? ?and ? ? rdx, r13 ? ? ? ?; hash &= 0FFFDBFFF7EDFFB70h
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+F3 ? ? ? ?mov ? ? [rbx], rax
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+F6 ? ? ? ?or ? ? ?rdx, rcx ? ? ? ?; hash |= 8000060010500070h
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+F9 ? ? ? ?mov ? ? ecx, r9d ? ? ? ?; this
XfgIlVisitor::visit_I_XFG_HASH(tagILMAP *)+FC ? ? ? ?call ? ?XFG::TiSetHash(ulong,unsigned __int64,tagMOD *)
這就是之所以XFG哈希基于SHA256,但看起來也不像完全隨機(jī)的原因。不過,我們不清楚為什么要使用這些掩碼。
七、嘗試進(jìn)行哈希計算
為了確保我們已經(jīng)正確理解了如何生成XFG哈希,我們嘗試進(jìn)行手動的哈希計算。假設(shè)我們使用以下原型計算函數(shù)的哈希值:
void *memcpy(
? ?void *dest,
? ?const void *src,
? ?size_t count
);
我們需要找出構(gòu)成函數(shù)原型的五條數(shù)據(jù):
(1)參數(shù)數(shù)量;
(2)為每個參數(shù)輸入哈希值;
(3)是否具有可變參數(shù)函數(shù);
(4)調(diào)用約定;
(5)返回類型的哈希值。
其中的1、3、4都很簡單:
(1)參數(shù)數(shù)量 - >? DWORD,值為3;
(3)是否具有可變參數(shù)函數(shù) - >? 值為0的字節(jié);
(4)調(diào)用約定 - >? 默認(rèn)值(值為0x201和0xF == 0x1的DWORD)。
因此,我們來計算更復(fù)雜的部分——每個參數(shù)的類型哈希,以及返回類型的類型哈希。
7.1 參數(shù)1的類型哈希
第一個參數(shù)的類型為void *,該類型由以下內(nèi)容的Type_t表示:
00000102 00000200 [+ pointer to referenced Type_t]
我們需要尋找3個數(shù)據(jù),來產(chǎn)生類型哈希:
(1)類型限定符 - >? 值為0的字節(jié);
(2)類型組:指針 - >? 值為3的字節(jié);
(3)特定類型的數(shù)據(jù):這是一個通用指針 - >? 引用類型的哈希(在這里有遞歸)+值為2的字節(jié)。
為了遞歸計算引用類型(void)的哈希,該類型由Type_t表示,其內(nèi)容如下:
00000040 00000000
我們需要構(gòu)建如下數(shù)據(jù):
(1)類型限定符 - >? 值為0的字節(jié);
(2)類型組:原始類型 - >? 值為1的字節(jié);
(3)特定類型的數(shù)據(jù):對于Type_t 0x40(void),XFGHelper::XFGTypeHasher::hash_primitive寫入一個值為0x0E的字節(jié)。
7.2 參數(shù)2的類型哈希
第二個參數(shù)的類型為const void *。該類型由具有以下內(nèi)容的Type_t表示:
00000102 00000200 [+ pointer to referenced Type_t]
我們需要構(gòu)建的數(shù)據(jù)如下:
(1)類型限定符 - >? 值為0的字節(jié);
(2)類型組:指針 - >? 值為3的字節(jié);
(3)特定類型的數(shù)據(jù):這是一個通用指針 - >? 引用類型的哈希(在這里有遞歸)+值為2的字節(jié)。
為了遞歸計算引用類型(void)的哈希,該類型由Type_t表示,其內(nèi)容如下:
00000040 00000000
我們需要構(gòu)建如下數(shù)據(jù):
(1)類型限定符:具有const限定符 - >? 編碼為值為1的字節(jié);
(2)類型組:原始類型 - >? 值為1的字節(jié);
(3)特定類型的數(shù)據(jù):對于Type_t 0x40(void),XFGHelper::XFGTypeHasher::hash_primitive寫入一個值為0x0E的字節(jié)。
7.3 參數(shù)3的類型哈希
第三個參數(shù)的類型為size_t。該類型由具有以下內(nèi)容的Type_t表示:
00004019 00000000
我們需要構(gòu)建的數(shù)據(jù)如下:
(1)類型限定符 - >? 值為0的字節(jié);
(2)類型組:原始類型 - >? 值為1的字節(jié);
(3)特定類型的數(shù)據(jù):對于Type_t 0x4019(無符號長長整型),XFGHelper::XFGTypeHasher::hash_primitive寫入一個值為0x88的字節(jié)。
7.4 返回類型的類型哈希
返回類型為void *,與該函數(shù)的第一個參數(shù)相同,因此這里只需要重復(fù)即可:
(1)類型限定符 - >? 值為0的字節(jié);
(2)類型組:指針 - >? 值為3的字節(jié);
(3)特定類型的數(shù)據(jù):這是一個通用指針 - >? 引用類型的哈希(在這里有遞歸)+值為2的字節(jié)。
對于引用類型(void)的哈希進(jìn)行遞歸計算:
(1)類型限定符:值為0的字節(jié);
(2)類型組:原始類型 - >? 值為1的字節(jié);
(3)特定類型的數(shù)據(jù):對于Type_t 0x40(void),XFGHelper::XFGTypeHasher::hash_primitive寫入一個值為0x0E的字節(jié)。
7.5 組合
我們將所有數(shù)據(jù)組合到一起:
# Number of params
03 00 00 00
# type hash of param 1 (void *)
SHA256(
? ? 00 ?#qualifiers
? ? 03 ?# type group: pointer
? ? # type hash of referenced type (void)
? ? SHA256(
? ? ? ? 00 ?# qualifiers
? ? ? ? 01 ?# type group: primitive type
? ? ? ? 0E ?# hash of primitive type: void - > ?0x0E
? ? )[0:8]
? ? 02 ?# regular pointer
)[0:8]
# type hash of param 2 (const void *)
SHA256(
? ? 00 ?# qualifiers
? ? 03 ?# type group: pointer
? ? # type hash of referenced type (const void)
? ? SHA256(
? ? ? ? 01 ?# qualifiers: const
? ? ? ? 01 ?# type group: primitive type
? ? ? ? 0E ?# hash of primitive type: void - > ?0x0E
? ? )[0:8]
? ? 02 ?# regular pointer
)[0:8]
# type hash of param 3 (size_t)
SHA256(
? ? 00 ?# qualifiers
? ? 01 ?# type group: primitive type
? ? 88 ?# hash of primitive type: unsigned long long - > ?0x88
)[0:8]
# is variadic
00
# calling convention
01 00 00 00
# type hash of return value (void *)
SHA256(
? ? 00 ?# qualifiers
? ? 03 ?# type group: pointer
? ? # type hash of referenced type (void)
? ? SHA256(
? ? ? ? 00 ?# qualifiers
? ? ? ? 01 ?# type group: primitive type
? ? ? ? 0E ?# hash of primitive type: void - > ?0x0E
? ? )[0:8]
? ? 02 ?# regular pointer
)[0:8]
以下Python代碼獲取該數(shù)據(jù)的SHA256摘要,并將其截斷為前8個字節(jié),以獲取與編譯器前端發(fā)出的哈希相同的哈希值。最后,它將編譯器后端的兩個掩碼進(jìn)行應(yīng)用,形成最終形式的XFG哈希。
import struct
import hashlib
def truncated_hash(data):
? ? return hashlib.sha256(data).digest()[0:8]
def apply_backend_masks(hash):
? ? hash = hash & 0xFFFDBFFF7EDFFB70
? ? hash = hash | 0x8000060010500070
? ? return hash
def main():
? ? # number of params
? ? data ?= struct.pack(' < L', 3)
? ? # type hash of first param (void *)
? ? data += truncated_hash(b'\x00\x03' + truncated_hash(b'\x00\x01\x0e') + b'\x02')
? ? # type hash of second param (const void *)
? ? data += truncated_hash(b'\x00\x03' + truncated_hash(b'\x01\x01\x0e') + b'\x02')
? ? # type hash of third param (size_t)
? ? data += truncated_hash(b'\x00\x01\x88')
? ? # is variadic
? ? data += struct.pack(' < B', 0x0)
? ? # calling convention (default)
? ? data += struct.pack(' < L', 0x201 & 0x0F)
? ? # type hash of return type (void *)
? ? data += truncated_hash(b'\x00\x03' + truncated_hash(b'\x00\x01\x0e') + b'\x02')
? ? print(f'Data to be hashed: {data} ({len(data)} bytes)')
? ? frontend_hash = struct.unpack(' < Q', truncated_hash(data))[0]
? ? print(f'Hash generated by the frontend: 0x{frontend_hash:x}')
? ? final_hash = apply_backend_masks(frontend_hash)
? ? print(f'[*] Final XFG hash: 0x{final_hash:x}')
Python代碼的輸出結(jié)果如下:
> ?python test.py
Data to be hashed: b'\x03\x00\x00\x00\xf5\x97x > [J`\xb0\x17\x80\xb8\xc0[\x1b\xd0\xd8#\x14\xb4\xba\x91\xc7\xf6j\x00\x01\x00\x00\x00\xf5\x97x > [J`\xb0' (41 bytes)
Hash generated by the frontend: 0x1da7d393d6b63a72
[*] Final XFG hash: 0x9da5979356d63a70
如果我們使用函數(shù)指針編譯一些代碼,以調(diào)用其原型與我們在本章中討論過的原型相匹配的函數(shù),就會看到,我們手工計算的XFG哈希值與MSVC生成的哈希完全匹配(參閱分配的值)。在下面的反匯編中的main+0x8E處注冊了R10。
main+1C ? ? ? ?lea ? ? rax, my_memcpy
main+23 ? ? ? ?mov ? ? [rsp+78h+var_50], rax
[...]
main+6A ? ? ? ?lea ? ? rcx, aCallingFunctio ; "Calling function pointer...\n"
main+71 ? ? ? ?call ? ?printf
main+76 ? ? ? ?lea ? ? rcx, Str ? ? ? ?; "a test"
main+7D ? ? ? ?call ? ?strlen
main+82 ? ? ? ?cdqe
main+84 ? ? ? ?mov ? ? rcx, [rsp+78h+var_50]
main+89 ? ? ? ?mov ? ? [rsp+78h+var_48], rcx
main+8E ? ? ? ?mov ? ? r10, 9DA5979356D63A70h
main+98 ? ? ? ?mov ? ? r8, rax
main+9B ? ? ? ?lea ? ? rdx, aATest_0 ? ; "a test"
main+A2 ? ? ? ?lea ? ? rcx, [rsp+78h+var_28]
main+A7 ? ? ? ?mov ? ? rax, [rsp+78h+var_48]
main+AC ? ? ? ?call ? ?cs:__guard_xfg_dispatch_icall_fptr
八、總結(jié)
在這篇文章中,我們分享了MSVC編譯器是如何為C語言程序生成XFG哈希的所有詳細(xì)信息。除了探討后續(xù)的漏洞利用緩解措施細(xì)節(jié)外,我們還可以深入了解編譯器內(nèi)部原理。
請注意,目前XFG僅存在于Windows Insider Preview版本中,因此在這個CFI解決方案進(jìn)入到Windows 10正式版本之前,本文所描述的細(xì)節(jié)可能還會被微軟進(jìn)行調(diào)整。
目前暫時不清楚,為什么編譯器后端對前端生成的哈希要使用兩個位掩碼,為什么哈希在函數(shù)啟動前使用第0位存儲,而未設(shè)置第0位的就要存儲在XFG的調(diào)用目標(biāo)中。
最后,非常有趣的是,我們可以看看C++編譯器前端(c1xx.dll)計算XFG哈希值的方式的不同之處。如果迅速瀏覽這個二進(jìn)制文件,會發(fā)現(xiàn)哈希算法看起來與C語言的算法非常相似,但是考慮到繼承、C++類型限定符、修飾符這類獨(dú)有概念,還是有可能會進(jìn)行調(diào)整的。
參考及來源:https://blog.quarkslab.com/how-the-msvc-compiler-generates-xfg-function-prototype-hashes.html
總結(jié)
以上是生活随笔為你收集整理的编译原理实验语义分析_Windows MVSC编译器实现Xtended Flow Guard(XFG)保护机制的原理分析...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xaml修改后台代码的值_Django定
- 下一篇: windows cmd post请求_c