スタックダンプの読み方

レジスタのEBPを基準にして読み解いていきます。EBPで示されるアドレスの手前4バイトが呼び出し元アドレス、EBP以降が引数です。EBPで示される場所に格納されている4バイトが関数呼び出し前のEBPアドレスです。
EBPが0x00C00018の場合を例としてスタックを読み解きます。

0x00C00010 : 00 00 00 04 // 引数、または、スタック変数
0x00C00014 : 00 00 00 03 // 引数、または、スタック変数
0x00C00018 : 00 C0 00 28 // 手前のEBPアドレス
0x00C0001C : 00 40 10 40 // 呼び出し元アドレス
0x00C00020 : 00 00 00 02 // 引数、または、スタック変数
0x00C00024 : 00 00 00 01 // 引数、または、スタック変数
0x00C00028 : 00 C0 00 40 // 手前のEBPアドレス
0x00C0002C : 00 40 10 80 // 呼び出し元アドレス

cdecl呼び出し規約や、stdcall呼び出し規約では、引数は右に書かれているものから順にスタックに格納されます。したがってアドレスの大きいものから順に、右から左の引数となります。Pascal呼び出し規約では逆に左から順に格納されます。
C++の場合this ポインタがスタックに追加されます。thisポインタはすべての引数をスタックに格納した後、最後にスタックに積まれます。Windowsでは関数が引数を伴わない場合には、thisポインタをスタックに積まずに、ECXレジスタに格納して関数を呼び出す場合があります。
スタック変数の積まれる順序は保障されません。最適化の結果、順不同にスタックに積まれます。

スタックの取得(ネィテブデバッガを作る2)

デバッグを行う上で実行位置に続いて重要なのがスタックの情報です。
別プロセスのスタック情報を取得するには、ReadProcessMemory APIを使用します。「ネィティブデバッガを作る」で取得したCONTEXT構造体のEspレジスタで示されるアドレス以降のデータを読み出します。

BYTE stackPointer[64];
DWORD readedSize(0);
HANDLE hProcess(0);
hProcess = ::OpenProcess(PROCESS_VM_READ, FALSE, debugEvent->dwProcessId);
if (NULL != hProcess)
{
// スタック領域からメモリを読み出す
if (::ReadProcessMemory(hProcess, (LPVOID )context.Esp, &readBuff, sizeof(readBuff), &readedSize))
{
// スタックダンプを表示する
}
}

ReadProcessMemory APIで確保されていないメモリ領域にアクセスすると、デバッグ対象となっているプロセスで不正なメモリアクセスがおこりDebugEventが発生します。ReadProcessMemoryで不正なメモリアクセスを起こし、DebugEventで再びReadProcessMemoryを呼び出し・・・という無限ループに陥らないように注意が必要です。
ReadProcessMemoryの第二パラメータや第三パラメータで割り当てられていないメモリを指定しても、ReadProcessMemoryはエラーを返さずに正常終了します。ReadProcessMemoryがエラーになるか否かで、正しくメモリを読み出せたか判断することはできません。