LoginSignup
16
18

More than 5 years have passed since last update.

ビルド後のバイナリー実行ファイルにポストプロセスで変数の値を書き換える方法

Last updated at Posted at 2016-03-07

概要

とある C++ のソフトウェア開発プロジェクトで新人もそろそろ卒業かなという若いイケメンくんが

「先輩!うちのプロジェクトのバイナリー実行ファイルに含まれている変数 mojimoji の値をビルド(翻訳+リンク)した後で書き換えるなんて無茶な事をしたいかもしれないです!!」

そんな状況が発生していたので早起きして業務時間外に Qiita にやり方を書いておくサンタクロース風のシャイなプレゼント方式を取ることにした私だった。(†0)

状況の例

  • a.cxx にはビルド後に書き換えたい変数 mojimojimojisize が含まれているとします。

例えば↓こんなのですね。

static volatile constexpr char mojimoji[] = "abcd1234";
static volatile constexpr unsigned char mojisize = sizeof( mojimoji );

#include <iostream>

auto main() -> int
{
  std::cout << ( const char* )mojimoji << " " << std::to_string( mojisize );
}

g++ -std=c++14 -O3 a.cxx などして翻訳すると 実行ファイル a (mingw なら a.exe ) ができて、これは実行すると abcd1234 9 と表示してくれます。(†1)

さて、例えばこれを ajipon 9 と表示するように C++ プロジェクト自体のリビルド無しで、ポストプロセスだけでどうにかしたいのです。

方法

確認

nm: シンボルエントリーの確認

はじめに、 a.exemojimojimojisize のシンボルエントリーがあるか確認してみましょう:

nm -C a.exe | grep moji すると、

00000000004a7018 d mojimoji
00000000004a7010 d mojisize

ありました。(†2)このように出てくる状態なら簡単にどうにかなるでしょう。たぶん。

この表示からは、バイナリーの実行時の仮想メモリー上のアドレス(今回は使いません)、 d なのでデータセクションに居ること、そしてシンボル名が mojimoji などで存在しているとわかります。

objdump: シンボルの位置

セクション情報

objdump -x a.exe | grep -10 Sections: などしてセクション情報を確認しましょう:

          pc+0x04: alloc small area: rsp = rsp - 0x28
        Handler: 00000000004a6780.
        User data:
          000: ff ff 01 00
 00000000004d4998 (rva: 000d4998): 00000000004a6d40 - 00000000004a6d4a
        Version: 1, Flags: none
        Nbr codes: 2, Prologue size: 0x04, Frame offset: 0x0, Frame reg: rbp
          pc+0x04: FPReg: rbp = rsp + 0x0 (info = 0x0)
          pc+0x01: push rbp

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         000a5db0  0000000000401000  0000000000401000  00000600  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA
  1 .data         00002f28  00000000004a7000  00000000004a7000  000a6400  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  2 .rdata        0000fbe0  00000000004aa000  00000000004aa000  000a9400  2**6
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .pdata        0000b04c  00000000004ba000  00000000004ba000  000b9000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .xdata        0000e9a0  00000000004c6000  00000000004c6000  000c4200  2**2

.data セクションはバイナリーファイル上のオフセットアドレス 000a6400 に居るとわかりました。†3

シンボルのアドレス

続いて、 objdump -x a.exe | grep moji などして mojimoji のアドレスオフセットを確認しましょう:

[134](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 0) 0x0000000000000018 _ZL8mojimoji
[138](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 0) 0x0000000000000010 _ZL8mojisize

_ZL8mojimoji (マングリングされた mojimoji)と _ZL8mojisize (マングリングされた mojisize)がセクションのアドレスオフセット 0x180x10 にそれぞれ居るとわかります。

ここまでにわかっている事を整理

  • mojimoji.data セクションに居る
  • .data セクションはファイルオフセット 0x0a6400 バイトに居る
  • mojimoji ( マングリングされた状態だと _ZL8mojimoji ) はセクションのオフセット 0x18 に居る
    • mojisize_ZL8mojisize ) はセクションのオフセット 0x10 に居る

と、言うわけで、バイナリーファイル a.exe のオフセットで:

  • 0x0a6400 + 0x18 = 0x0a6418 : mojimoji が居る
  • 0x0a6400 + 0x10 = 0x0a6410 : mojisize が居る

となります。

実際のデータを確認

od -A x -j 0x0a6418 -a a.exe | head -n1 してバイナリーファイル a.exe のファイルオフセットアドレス 0x0a6418 からの mojimoji のバイト列をASCII文字のダンプで確認してみます:

0a6418   a   b   c   d   1   2   3   4 nul nul nul nul nul nul nul nul

居ましたヽ(´ー`)ノ

od -A x -j 0x0a6410 -t d1 a.exe | head -n1 して 0x0a6410mojisize を 10進数のダンプで確認してみます:

0a6410    9    0    0    0    0    0    0    0   97   98   99  100   49   50   51   52

居ましたヽ(´ー`)ノ

バイナリーファイルの変数を書き換えて実行

cp a.exe b.exe などしてコピーした b.exe0x0a6418 からの 9 bytes を "ajipon\0\0\0" に書き換えて b.exe を実行してみましょう: †4

./b
ajipon 9

できましたヽ(´ー`)ノ

  • †0: 記事冒頭の概要本文中のどうでもよさそうな表現はフィクションです。
  • †1: volatile を付けないと -O3 でシンボルが吹き飛ぶ可能性があります。nmなどでシンボルが見つからない場合の原因となる可能性があります。
  • †2: nm のオプション -C を付けないと C++ のマングリングされたシンボル名が表示される事になります。複雑な階層構造になっていたりするとひうまんの脳には辛いので C++er は nm-Cc++-std=c++14 と同じくらい標準的に付けるものだと思って差し支えありません。
  • †3: grep せずに > a.objdump.txt| less などして読むと出力にちゃんとカラムがそれぞれ何であるか丁寧に出力されていたりします。やり方としての実用上は不要ですが1度は出力全体を眺めておく事をお勧めします。
  • †4: コマンドだけでバイナリー差分を書き換えるのは面倒かなと思ったので今回は書き換えに Bz を使いました。
16
18
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
18