12021-06-02
昨日の日記(12021年6月2日)です。 天気は曇り。 電波は悪い。
VRChat
VR感度とは?というワールドへ行きました。
ところで、ハンバーグから温かみとか匂いというのは感じられませんでした。 そういう感覚はあんまり分からないっぽいです。
かわいいですね。
ねじまきさんとは初めてお会いしました。
exeファイルを読んで見る(5日目)
前にエントリーポイントのところを逆アセンブリしましたが、
sub rsp, 28h call 00000A84h add rsp, 28h jmp 00000528h
意味がよくわかっていませんでした。 そこで今回はGhidraを使いました。
************************************************************** * FUNCTION * ************************************************************** int __fastcall entry(void) int EAX:4 <RETURN> mainCRTStartup XREF[2]: Entry Point(*), 140000118(*) entry 1400012a4 48 83 ec 28 SUB RSP,0x28 1400012a8 e8 d7 03 CALL __security_init_cookie void __security_init_cookie(void) 00 00 1400012ad 48 83 c4 28 ADD RSP,0x28 1400012b1 e9 72 fe JMP __scrt_common_main_seh int __scrt_common_main_seh(void) ff ff -- Flow Override: CALL_RETURN (CALL_TERMINATOR)
うおおお、すごい! 切り出してみましょう。
SUB RSP,0x28 CALL __security_init_cookie ADD RSP,0x28 JMP __scrt_common_main_seh
すごい… どうして関数がわかるんでしょうか。
Ghidraくんはデコンパイルできるので、それを使ってみると…
int entry(void) { int iVar1; __security_init_cookie(); iVar1 = __scrt_common_main_seh(); return iVar1; }
おおー! すごいです。
SUB RSP,0x28
はint iVar1;
と返り値のint
に対応しています。
どういうことか説明します。
ここを参照すると、
Microsoft* x64 の呼び出し規約は、レジスター領域の拡張により、fastcall しかありません (x86 では、stdcall、thiscall、fastcall、cdecl など多数あります)。C/C++ 形式の関数を使用する場合のルールは、次のとおりです。
(略)
- スタックは 16 バイトでアライメントされています。”call” 命令は 8 バイトの戻り値をプッシュするため、リーフでない関数はスタック領域を割り当てる際に、値を 16n+8 形式で指定してスタックを調整する必要があります。
などということが書かれています。
うーん 分かるようで分からない説明です。 細かく見てましょう。
スタックは 16 バイトでアライメントされています。
とは、 64ビット版ではスタックは16バイトごとである(読み取るデータは16の倍数のメモリアドレスにある必要がある)ということです。 こういうのを 「16バイト境界にアライン (align:整列,位置合わせ) する」 とか 「16 バイトでアライメントされている」 とか言うみたいですね。 だから16バイトの倍数でないといけないわけです。
次に、
”call” 命令は 8 バイトの戻り値をプッシュするため、リーフでない関数はスタック領域を割り当てる際に、値を 16n+8 形式で指定してスタックを調整する必要があります。
ですが、 リーフ関数とは、 直訳すると「葉っぱの関数」で、 Microsoftの文書 によると、 「非volatileレジスタを変更しない関数」と書かれていますが、 簡単に言うと、 「呼び出しスタックの一番下にのみ現れる関数」で、 もっと簡単にいうと、 「他の関数をコールしない関数」のことです。 つまり、リーフでない関数とは他の関数を呼び出す関数のことですね。
んで、
スタックは 16 バイトでアライメントされていて、
返り値のためにさらに8バイト必要なので、
16 * 2 + 8 = 40バイト、
16進数で28hになるわけです。
(どうして16バイトではなく32バイトを確保するかは後で書きます。)
SUB RSP,0x28
がどうして28hなのかはそういうことです。
さて、次にCALL __security_init_cookie
ですが、
__security_init_cookie
は
Microsoftの公式ドキュメント
があります。
詳しくは説明しませんが、
セキュリティ関連のことらしいですね。
ここ(またしてもMicrosoftの公式ドキュメント)
とか
すなのかたまり(msmaniaさんのブログ)
とかでさらに詳しい説明があります。
__security_init_cookie
関数をデコンパイルすると、
void __security_init_cookie(void) { DWORD DVar1; _FILETIME local_res8; _FILETIME local_res10; uint local_res18; undefined4 uStackX28; if (__security_cookie == 0x2b992ddfa232) { local_res10 = (_FILETIME)0x0; GetSystemTimeAsFileTime((LPFILETIME)&local_res10); local_res8 = local_res10; DVar1 = GetCurrentThreadId(); local_res8 = (_FILETIME)((ulonglong)local_res8 ^ (ulonglong)DVar1); DVar1 = GetCurrentProcessId(); local_res8 = (_FILETIME)((ulonglong)local_res8 ^ (ulonglong)DVar1); QueryPerformanceCounter((LARGE_INTEGER *)&local_res18); __security_cookie = ((ulonglong)local_res18 << 0x20 ^ CONCAT44(uStackX28,local_res18) ^ (ulonglong)local_res8 ^ (ulonglong)&local_res8) & 0xffffffffffff; if (__security_cookie == 0x2b992ddfa232) { __security_cookie = 0x2b992ddfa233; } } __security_cookie_complement = ~__security_cookie; return; }
となるのですが、
DWORD DVar1; _FILETIME local_res8; _FILETIME local_res10; uint local_res18; undefined4 uStackX28;
が対応するアセンブリが、
MOV qword ptr [RSP + local_res20],RBX
ここなんですね。
DWORD
(4バイト)+_FILETIME
(8バイト)+_FILETIME
(8バイト)+uint
(4バイト)+undefined4
(4バイト)
で28バイト、
スタックのアライメントは16バイトなので、
32バイト確保する必要があるわけです。
__security_init_cookie
をちゃんと読む気はないので、
飛ばします。
次に__scrt_common_main_seh
を見てみましょう。
************************************************************** * FUNCTION * ************************************************************** int __fastcall __scrt_common_main_seh(void) int EAX:4 <RETURN> undefined8 Stack[0x10]:8 local_res10 XREF[2]: 14000112d(W), 140001274(R) undefined8 Stack[0x8]:8 local_res8 XREF[2]: 140001128(W), 14000126f(R) undefined1 Stack[-0x18]:1 local_18 XREF[2]: 14000114c(W), 1400011b8(W) __scrt_common_main_seh XREF[1]: entry:1400012b1(c) 140001128 48 89 5c MOV qword ptr [RSP + local_res8],RBX 24 08 14000112d 48 89 74 MOV qword ptr [RSP + local_res10],RSI 24 10 140001132 57 PUSH RDI 140001133 48 83 ec 30 SUB RSP,0x30 140001137 b9 01 00 MOV ECX,0x1 00 00 14000113c e8 2f 03 CALL __scrt_initialize_crt bool __scrt_initialize_crt(__scr 00 00 140001141 84 c0 TEST AL,AL 140001143 0f 84 36 JZ LAB_14000127f 01 00 00 140001149 40 32 f6 XOR SIL,SIL 14000114c 40 88 74 MOV byte ptr [RSP + local_18],SIL 24 20 140001151 e8 de 02 CALL __scrt_acquire_startup_lock bool __scrt_acquire_startup_lock 00 00 140001156 8a d8 MOV BL,AL 140001158 8b 0d 52 MOV ECX,dword ptr [__scrt_current_native_startup_s = ?? 24 00 00 14000115e 83 f9 01 CMP ECX,0x1 140001161 0f 84 23 JZ LAB_14000128a 01 00 00 140001167 85 c9 TEST ECX,ECX 140001169 75 4a JNZ LAB_1400011b5 14000116b c7 05 3b MOV dword ptr [__scrt_current_native_startup_state = ?? 24 00 00 01 00 00 00 140001175 48 8d 15 LEA RDX,[__xi_z] = 64 10 00 00 14000117c 48 8d 0d LEA RCX,[__xi_a] = 45 10 00 00 140001183 e8 aa 0a CALL _initterm_e undefined _initterm_e() 00 00 140001188 85 c0 TEST EAX,EAX 14000118a 74 0a JZ LAB_140001196 14000118c b8 ff 00 MOV EAX,0xff 00 00 140001191 e9 d9 00 JMP LAB_14000126f 00 00 LAB_140001196 XREF[1]: 14000118a(j) 140001196 48 8d 15 LEA RDX,[__xc_z] = 23 10 00 00 14000119d 48 8d 0d LEA RCX,[__xc_a] = 0c 10 00 00 1400011a4 e8 83 0a CALL _initterm undefined _initterm() 00 00 1400011a9 c7 05 fd MOV dword ptr [__scrt_current_native_startup_state = ?? 23 00 00 02 00 00 00 1400011b3 eb 08 JMP LAB_1400011bd LAB_1400011b5 XREF[1]: 140001169(j) 1400011b5 40 b6 01 MOV SIL,0x1 1400011b8 40 88 74 MOV byte ptr [RSP + local_18],SIL 24 20 LAB_1400011bd XREF[1]: 1400011b3(j) 1400011bd 8a cb MOV CL,BL 1400011bf e8 1c 04 CALL __scrt_release_startup_lock void __scrt_release_startup_lock 00 00 1400011c4 e8 cb 05 CALL __scrt_get_dyn_tls_init_callback _func__cdecl_void_void_ptr_ulong 00 00 1400011c9 48 8b d8 MOV RBX,RAX 1400011cc 48 83 38 00 CMP qword ptr [RAX],0x0 1400011d0 74 1e JZ LAB_1400011f0 1400011d2 48 8b c8 MOV RCX,RAX 1400011d5 e8 6e 03 CALL __scrt_is_nonwritable_in_current_image bool __scrt_is_nonwritable_in_cu 00 00 1400011da 84 c0 TEST AL,AL 1400011dc 74 12 JZ LAB_1400011f0 1400011de 45 33 c0 XOR R8D,R8D 1400011e1 41 8d 50 02 LEA EDX,[R8 + 0x2] 1400011e5 33 c9 XOR ECX,ECX 1400011e7 48 8b 03 MOV RAX,qword ptr [RBX] 1400011ea ff 15 a8 CALL qword ptr [->_guard_dispatch_icall] 0f 00 00 LAB_1400011f0 XREF[2]: 1400011d0(j), 1400011dc(j) 1400011f0 e8 a7 05 CALL __scrt_get_dyn_tls_dtor_callback _func__cdecl_void_void_ptr_ulong 00 00 1400011f5 48 8b d8 MOV RBX,RAX 1400011f8 48 83 38 00 CMP qword ptr [RAX],0x0 1400011fc 74 14 JZ LAB_140001212 1400011fe 48 8b c8 MOV RCX,RAX 140001201 e8 42 03 CALL __scrt_is_nonwritable_in_current_image bool __scrt_is_nonwritable_in_cu 00 00 140001206 84 c0 TEST AL,AL 140001208 74 08 JZ LAB_140001212 14000120a 48 8b 0b MOV RCX,qword ptr [RBX] 14000120d e8 50 0a CALL _register_thread_local_exe_atexit_callback undefined _register_thread_local 00 00 LAB_140001212 XREF[2]: 1400011fc(j), 140001208(j) 140001212 e8 0f 0a CALL _get_initial_narrow_environment undefined _get_initial_narrow_en 00 00 140001217 48 8b f8 MOV RDI,RAX 14000121a e8 31 0a CALL __p___argv undefined __p___argv() 00 00 14000121f 48 8b 18 MOV RBX,qword ptr [RAX] 140001222 e8 23 0a CALL __p___argc undefined __p___argc() 00 00 140001227 4c 8b c7 MOV R8,RDI 14000122a 48 8b d3 MOV RDX,RBX 14000122d 8b 08 MOV ECX,dword ptr [RAX] 14000122f e8 cc fd CALL main int main(int _Argc, char * * _Ar ff ff 140001234 8b d8 MOV EBX,EAX 140001236 e8 c5 06 CALL __scrt_is_managed_app bool __scrt_is_managed_app(void) 00 00 14000123b 84 c0 TEST AL,AL 14000123d 74 55 JZ LAB_140001294 14000123f 40 84 f6 TEST SIL,SIL 140001242 75 05 JNZ LAB_140001249 140001244 e8 0d 0a CALL _cexit void _cexit(void) 00 00 LAB_140001249 XREF[1]: 140001242(j) 140001249 33 d2 XOR EDX,EDX 14000124b b1 01 MOV CL,0x1 14000124d e8 b2 03 CALL __scrt_uninitialize_crt bool __scrt_uninitialize_crt(boo 00 00 140001252 8b c3 MOV EAX,EBX 140001254 eb 19 JMP LAB_14000126f
うーん、 main関数に到達するまでの下準備が色々あるんですね。
何はともあれ、
entry
関数から実行が始まり、
entry
関数が__scrt_common_main_seh
関数を呼び出し、
__scrt_common_main_seh
関数がmain
関数を呼び出していることが分かりました。
最後に__scrt_common_main_seh
関数のデコンパイル結果を貼って終わりにしたいと思います。
int __scrt_common_main_seh(void) { bool bVar1; bool bVar2; int iVar3; _func__cdecl_void_void_ptr_ulong_void_ptr **pp_Var4; char **_Env; char **ppcVar5; int *piVar6; uint unaff_EBX; undefined8 in_R9; undefined uVar7; bVar1 = __scrt_initialize_crt(exe); if (bVar1 == false) { __scrt_fastfail(7); } else { bVar1 = false; uVar7 = 0; bVar2 = __scrt_acquire_startup_lock(); unaff_EBX = unaff_EBX & 0xffffff00 | (uint)bVar2; if (__scrt_current_native_startup_state != initializing) { if (__scrt_current_native_startup_state == uninitialized) { __scrt_current_native_startup_state = initializing; iVar3 = _initterm_e(__xi_a,__xi_z); if (iVar3 != 0) { return 0xff; } _initterm(__xc_a,__xc_z); __scrt_current_native_startup_state = initialized; } else { bVar1 = true; uVar7 = 1; } __scrt_release_startup_lock(bVar2); pp_Var4 = __scrt_get_dyn_tls_init_callback(); if ((*pp_Var4 != (_func__cdecl_void_void_ptr_ulong_void_ptr *)0x0) && (bVar2 = __scrt_is_nonwritable_in_current_image(pp_Var4), bVar2 != false)) { (*(code *)&_guard_dispatch_icall)(0,2,0,in_R9,uVar7); } pp_Var4 = __scrt_get_dyn_tls_dtor_callback(); if ((*pp_Var4 != (_func__cdecl_void_void_ptr_ulong_void_ptr *)0x0) && (bVar2 = __scrt_is_nonwritable_in_current_image(pp_Var4), bVar2 != false)) { _register_thread_local_exe_atexit_callback(); } _Env = (char **)_get_initial_narrow_environment(); ppcVar5 = (char **)__p___argv(); ppcVar5 = (char **)*ppcVar5; piVar6 = (int *)__p___argc(); unaff_EBX = main(*piVar6,ppcVar5,_Env); bVar2 = __scrt_is_managed_app(); if (bVar2 != false) { if (!bVar1) { _cexit(); } __scrt_uninitialize_crt(true,false); return (int)unaff_EBX; } goto LAB_140001294; } } __scrt_fastfail(7); LAB_140001294: /* WARNING: Subroutine does not return */ exit(unaff_EBX); }