タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2010/07/15/やっぱりバイナリ解析というのは秘儀扱いなのかも | msakamoto-sf | 2010-07-15 19:42:43 |
技術/Windows/PE(Portable Executable)フォーマットの実験/04, 各種構造体のサイズ | msakamoto-sf | 2010-07-14 23:37:07 |
日記/2010/07/14/GetLastError()のエラーメッセージ | msakamoto-sf | 2010-07-14 23:19:36 |
技術/Windows/PE(Portable Executable)フォーマットの実験 | msakamoto-sf | 2010-07-14 22:29:05 |
技術/Windows/PE(Portable Executable)フォーマットの実験/03, TinyPEまとめ | msakamoto-sf | 2010-07-14 12:57:21 |
技術/Windows/実行したプロセスの終了値を取得 | msakamoto-sf | 2010-07-13 23:49:42 |
日記/2010/07/13/ヘッダーファイルの関数宣言にexternって必要? | msakamoto-sf | 2010-07-13 23:13:03 |
技術/Windows | msakamoto-sf | 2010-07-13 12:22:14 |
技術/Windows/PE(Portable Executable)フォーマットの実験/02, 再配置情報で遊ぼう! | msakamoto-sf | 2010-07-13 12:19:18 |
技術/Windows/PE(Portable Executable)フォーマットの実験/01, リンカオプション | msakamoto-sf | 2010-07-13 12:18:41 |
バイナリ解析とかその界隈での、サンプルコードを見ていて、みょうな読みづらさを感じる時がある。
二つ以上のモジュールの関連が、絡み合っていてよく分からなかったり(APIのHOOKで、Hookを仕掛けたりHookとして動作するコードだったりそれが読み込むDLLだったりが、記事によっては一つのモジュールで色々兼用したりしてて分かりづらい)。
アセンブラのコードの場合は、アセンブラの提供するマクロをあまり使わずに冗長だったりマジックナンバーてんこ盛りのコードになっていたり(ESPやEBPからのオフセットとか)。
最初はスキルの方向性が違うのかなと思ったのだけれど、改めて思うに、広く一般に知られたくないコードだから敢えて読みづらいコードにしているのかも知れない。
Webアプリ界隈の考え方だと、Blogであったり記事であったり公の場所で公開するソースコードというのは「理解して貰う」ためのものなので、マクロや適切なコメントを活用し、後々ソースを追いづらくしそうなマジックナンバーの多用を避けたりする。総じて、一般に読みやすい・理解しやすいコードになる。
バイナリ解析やちょっと危ないダークサイド系では、そもそも理解して貰う必要が無い。コンセプトを示せれば良い。わざわざ読みやすくて理解しやすいソースを書いて、濫用・悪用されるのを避けたい・・・という意図が働いているのかも知れない。
・・・まぁ、これも自分が深読みしているだけで、もっと単純に、コンセプト実証を示せたからそれでいいや、という程度にしているだけかも知れないけど。
PEフォーマット関連で頻出する構造体のサイズ一覧
(WinXP SP3, Pentium4, 32bit環境のVisual C++ 2008 Express Edition)
hex, dec : structure typedef -------------------------------- 0x0040, 64 : sizeof(IMAGE_DOS_HEADER) 0x00F8, 248 : sizeof(IMAGE_NT_HEADERS) 0x0014, 20 : sizeof(IMAGE_FILE_HEADER) 0x00E0, 224 : sizeof(IMAGE_OPTIONAL_HEADER) 0x0008, 8 : sizeof(IMAGE_DATA_DIRECTORY) 0x0028, 40 : sizeof(IMAGE_SECTION_HEADER) 0x0012, 18 : sizeof(IMAGE_SYMBOL) 0x0012, 18 : sizeof(IMAGE_AUX_SYMBOL) 0x000A, 10 : sizeof(IMAGE_RELOCATION) 0x0008, 8 : sizeof(IMAGE_BASE_RELOCATION) 0x003C, 60 : sizeof(IMAGE_ARCHIVE_MEMBER_HEADER) 0x0028, 40 : sizeof(IMAGE_EXPORT_DIRECTORY) 0x0004, 4 : sizeof(IMAGE_IMPORT_BY_NAME) 0x0004, 4 : sizeof(IMAGE_THUNK_DATA) 0x0014, 20 : sizeof(IMAGE_IMPORT_DESCRIPTOR) 0x0008, 8 : sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR) 0x0008, 8 : sizeof(IMAGE_BOUND_FORWARDER_REF) 0x0010, 16 : sizeof(IMAGE_RESOURCE_DIRECTORY) 0x0008, 8 : sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) 0x0048, 72 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY)
ソース:
#include <windows.h> #include <stdio.h> #define print_sz(structure) { \ sz = sizeof(structure); \ printf("0x%04X, %4d : sizeof(" #structure ")\n", sz, sz); \ } int main(int argc, char *argv[]) { size_t sz; printf("hex, dec : structure typedef\n"); printf("--------------------------------\n"); print_sz(IMAGE_DOS_HEADER); print_sz(IMAGE_NT_HEADERS); print_sz(IMAGE_FILE_HEADER); print_sz(IMAGE_OPTIONAL_HEADER); print_sz(IMAGE_DATA_DIRECTORY); print_sz(IMAGE_SECTION_HEADER); print_sz(IMAGE_SYMBOL); print_sz(IMAGE_AUX_SYMBOL); print_sz(IMAGE_RELOCATION); print_sz(IMAGE_BASE_RELOCATION); print_sz(IMAGE_ARCHIVE_MEMBER_HEADER); print_sz(IMAGE_EXPORT_DIRECTORY); print_sz(IMAGE_IMPORT_BY_NAME); print_sz(IMAGE_THUNK_DATA); print_sz(IMAGE_IMPORT_DESCRIPTOR); print_sz(IMAGE_BOUND_IMPORT_DESCRIPTOR); print_sz(IMAGE_BOUND_FORWARDER_REF); print_sz(IMAGE_RESOURCE_DIRECTORY); print_sz(IMAGE_RESOURCE_DIRECTORY_ENTRY); print_sz(IMAGE_LOAD_CONFIG_DIRECTORY); return 0; }
を表示するだけのツール。実験コード書く時とか、一々エラーメッセージフォーマット用の関数コピペするのめんどいので、GetLastError()のコードだけ表示して終わりにしたい時。こっちのツールで日本語メッセージを確認する。
gle.cpp:
#include <windows.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { LPTSTR lpMsgBuf; DWORD err; if (2 != argc) { fprintf(stderr, "usage: %s error_code\n", argv[0]); return 1; } err = atol(argv[1]); switch (err) { case 0: fprintf(stderr, "error, not DWORD number: %s\n", argv[1]); return 2; case LONG_MAX: case LONG_MIN: fprintf(stderr, "error, out of range: %s\n", argv[1]); return 3; } FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); printf("%s\n", lpMsgBuf); LocalFree(lpMsgBuf); return 0; }
コンパイル:
> cl gle.cpp
使い方:
> gle 100 ほかのシステム セマフォを作成できません。
アセンブラでヘッダーを手書きし、さらには使われていないヘッダーメンバーにコードや他のヘッダー、DataDirectoryを重ね合わせるという暴虐の限りを尽くした英雄達の記録。
震源地(多分):
日本の挑戦者達の足跡:
コマンドプロンプト or バッチファイル (=cmd.exe) 上:
> foobar.exe ... ... > echo %ERRORLEVEL%
Win32APIで取得するなら、WaitForSingleObject()でプロセスの終了を待ち、GetExitCodeProcess()で取得する。
例(invoker.c):
#include <windows.h> #include <stdio.h> int main(int argc, char *argv[]) { PROCESS_INFORMATION pi; STARTUPINFO si; BOOL r; DWORD rc; if (2 != argc) { fprintf(stderr, "usage: %s target_exe\n", argv[0]); return 1; } ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); if (!CreateProcess( NULL, // LPCTSTR lpApplicationName argv[1], // LPTSTR lpCommandLine NULL, // LPSECURITY_ATTRIBUTES lpProcessAttributes NULL, // LPSECURITY_ATTRIBUTES lpThreadAttributes FALSE, // BOOL bInheritHandles 0, // DWORD dwCreationFlags NULL, // LPVOID lpEnvironment NULL, // LPCTSTR lpCurrentDirectory &si, // LPSTARTUPINFO lpStartupInfo &pi // LPPROCESS_INFORMATION lpProcessInformation )) { fprintf(stderr, "CreateProcess(%s) failed, gle = %d\n", argv[1], GetLastError()); return 2; } if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, INFINITE)) { GetExitCodeProcess(pi.hProcess, &rc); printf("terminate code of %s = %lu\n", argv[1], rc); } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }
cheap2el作ってる時、少し気になってたのでGoogle先生に尋ねてみたら、関数についてはexternをわざわざつける必要は無かったみたいです。
一応ソースの首根っこをメモ。上記Wikipediaから辿れる、"The current Standard (C99 with Technical corrigenda TC1, TC2, and TC3 included)", WG14/N1256 Committee Draft(Septermber 7, 2007) ISO/IEC 9899:TC3 より:
6.2.2 Linkages of identifiers ... [5] If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.
昔C言語勉強してた時に、libを作る時にincludeするときはextern無しで、外部からリンクする為にincludeするときはextern有りにするとかでわざわざ
#ifdef __BUILD_LIB__ #define Extern extern #endif ... Extern int foobar(int a, int b);
とかして、libを作る時と、アプリ側でリンクする時とで define に応じて切り替えてるサンプルを見たことがあったので気になってましたが・・・無意味だったんだなー・・・。
Microsoft Windows プラットフォーム関連
PEファイルの再配置情報を取得、表示してみる。
さらにその発展として、PythonによりDLLをメモリ上にロード、再配置情報とIATを手動で書き換え実行可能にし、実際にエクスポート関数をPythonから呼んでみる。
Pythonは 2.6 を使用。
OSはWindows XP SP3, VCはVC++ 2008 Express Edition, コンパイラとリンカのバージョンは次の通り:
> cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved.
> link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved.(全て表示する)
IMAGE_OPTIONAL_HEADERのメンバのいくつかを、コンパイラやリンカオプションでデフォルト値を変更し、影響を確認してみる。DataDirectoryはまだ触らない。CPUはx86-32bit(Pen4)を使用。
OSはWindows XP SP3, VCはVC++ 2008 Express Edition, コンパイラとリンカのバージョンは次の通り:
> cl Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved.
> link Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved.(全て表示する)