RCE Tips ■Structure Dataタイプの使い方 ●グローバル変数の構造体 もともとのプログラム。 #include #include struct struct1 { int a; int b; char name[10]; short c; }; struct struct1 strct; int main(void) { strct.a = 10; strct.b = 20; strncpy(strct.name, "name", 10); strct.c = 30; return 0; } こういったプログラムだった。構造体のデータ構造がグローバルで宣言されている。 これをそのままIDAで読むと .text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:00401000 _main proc near ; CODE XREF: ___tmainCRTStartup+15Ap .text:00401000 .text:00401000 argc = dword ptr 8 .text:00401000 argv = dword ptr 0Ch .text:00401000 envp = dword ptr 10h .text:00401000 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 mov dword ptr byte_40B740, 0Ah .text:0040100D mov dword ptr unk_40B744, 14h .text:00401017 push 0Ah ; Count .text:00401019 push offset Source ; "name" .text:0040101E push offset unk_40B748 ; Dest .text:00401023 call _strncpy .text:00401028 add esp, 0Ch .text:0040102B mov word ptr unk_40B752, 1Eh .text:00401034 xor eax, eax .text:00401036 pop ebp .text:00401037 retn .text:00401037 _main endp のような形になる。代入している定数値から、40B740あたりがグローバル構造体の先頭のような気がするが、わかりにくい。 そこで、ユーザ定義の構造体を作成してやり、それを逆アセンブル結果に反映させてやる。 00000000 struc_1 struc ; (sizeof=0x16) 00000000 field_0 dd ? 00000004 field_4 dd ? 00000008 field_8 db 10 dup(?) ; string(C) 00000012 field_12 dd ? 00000016 struc_1 ends 00000016 上記のような構造体を独自に定義してやる。そして、40B740のアドレスにポインタを合わせ、[Edit]→[Struct Var]→struc1としてやると、その構造体定義が40B740のアドレスに反映される。 .data:0040B740 unk_40B740 db ? ; ; DATA XREF: _main+3w .data:0040B741 db ? ; .data:0040B742 db ? ; .data:0040B743 db ? ; .data:0040B744 db ? ; ; DATA XREF: _main+Dw .data:0040B745 db ? ; .data:0040B746 db ? ; .data:0040B747 db ? ; こうなっていたのが、 .data:0040B740 stru_40B740 struc_2 ; DATA XREF: _main+3w .data:0040B740 ; _main+Dw ... .data:0040B756 db ? ; .data:0040B757 db ? ; こうなる。 それでアセンブラコードを見直してみると、 .text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:00401000 _main proc near ; CODE XREF: ___tmainCRTStartup+15Ap .text:00401000 .text:00401000 argc = dword ptr 8 .text:00401000 argv = dword ptr 0Ch .text:00401000 envp = dword ptr 10h .text:00401000 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 mov stru_40B740.field_0, 0Ah .text:0040100D mov stru_40B740.field_4, 14h .text:00401017 push 0Ah ; Count .text:00401019 push offset Source ; "name" .text:0040101E push offset stru_40B740.field_8 ; Dest .text:00401023 call _strncpy .text:00401028 add esp, 0Ch .text:0040102B mov word ptr stru_40B740.field_12, 1Eh .text:00401034 xor eax, eax .text:00401036 pop ebp .text:00401037 retn .text:00401037 _main endp このように構造体の各フィールドが反映されている。 ●スタック上の構造体 構造体は同じで、メインの中で定義した(ローカル変数)。 int main(void) { struct struct1 strct; strct.a = 10; strct.b = 20; strncpy(strct.name, "name", 10); strct.c = 30; return 0; } 通常はこのように逆アセンブルされる .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 sub esp, 18h .text:00401006 mov eax, dword_40A010 .text:0040100B xor eax, ebp .text:0040100D mov [ebp+var_4], eax .text:00401010 mov dword ptr [ebp-18h], 10 .text:00401017 mov dword ptr [ebp-14h], 20 .text:0040101E push 0Ah ; Count .text:00401020 push offset Source ; "name" .text:00401025 lea eax, [ebp-10h] .text:00401028 push eax ; Dest .text:00401029 call _strncpy .text:0040102E add esp, 0Ch .text:00401031 mov word ptr [ebp-6], 30 .text:00401037 xor eax, eax .text:00401039 mov ecx, [ebp+var_4] .text:0040103C xor ecx, ebp .text:0040103E call sub_401174 .text:00401043 mov esp, ebp .text:00401045 pop ebp .text:00401046 retn .text:00401046 _main endp ebp-18hあたりで構造体に値を代入しているのがわかるので、そこでstack frameを表示して先ほどと同様に構造体を定義して、適用する。 すると、 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 sub esp, 18h .text:00401006 mov eax, dword_40A010 .text:0040100B xor eax, ebp .text:0040100D mov [ebp+var_4], eax .text:00401010 mov [ebp+var_18.field_0], 10 .text:00401017 mov [ebp+var_18.field_4], 20 .text:0040101E push 0Ah ; Count .text:00401020 push offset Source ; "name" .text:00401025 lea eax, [ebp+var_18.field_8] .text:00401028 push eax ; Dest .text:00401029 call _strncpy .text:0040102E add esp, 0Ch .text:00401031 mov [ebp+var_18.field_12], 30 .text:00401037 xor eax, eax .text:00401039 mov ecx, [ebp+var_4] .text:0040103C xor ecx, ebp .text:0040103E call sub_401174 .text:00401043 mov esp, ebp .text:00401045 pop ebp .text:00401046 retn .text:00401046 _main endp のように構造体部分のフィールドが反映され、読みやすくなる。 ●ヘッダファイルを読み込む 手動で構造体の定義を書くのが面倒な場合、Cのヘッダファイルがあえれば自動で構造体定義を作成することができる。 [File]→[Load File]→[Parsing C Header] これでパースしたいヘッダファイルを指定すると、そのヘッダファイルで定義されている構造体を解析してくれ、Standard Structureへ追加してくれる。 次にStructures SubWindowで、[Insert]ボタンを押すと構造体定義のためのダイアログが出力される、ここでAdd Standard Structureを選び、先ほどヘッダファイルから読み込んだ構造体を指定すると、ユーザ定義の構造体として読み込んでくれる。 あとは、StackFrameで先ほどと同じ操作をすれば、 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 sub esp, 18h .text:00401006 mov eax, dword_40A010 .text:0040100B xor eax, ebp .text:0040100D mov [ebp+var_4], eax .text:00401010 mov [ebp+var_18.a], 10 .text:00401017 mov [ebp+var_18.b], 20 .text:0040101E push 0Ah ; Count .text:00401020 push offset Source ; "name" .text:00401025 lea eax, [ebp+var_18.name] .text:00401028 push eax ; Dest .text:00401029 call _strncpy .text:0040102E add esp, 0Ch .text:00401031 mov [ebp+var_18.c], 30 .text:00401037 xor eax, eax .text:00401039 mov ecx, [ebp+var_4] .text:0040103C xor ecx, ebp .text:0040103E call sub_401174 .text:00401043 mov esp, ebp .text:00401045 pop ebp .text:00401046 retn このような形で構造体を反映させることができる。