30782


例えばC言語で下記のように128バイトの領域を確保したとします。

[code]

    const char TABLE[128] = {0};
    
[/code]

このシンボルを含んだオブジェクトファイルをリンクして生成されたelfファイルに対して、後からTABLEが指すアドレスの中身を差し替える、ということがやりたいのです。

elfフォーマットを勉強して、シンボルテーブルをパースして・・・といった手順を踏めば実現できそうに思えますが、今回はそこまで手間をかけている時間もないので、何かツールでできればと思っています。

対称はARMのelfです。binutilsのツールでどうにかできないでしょうか？


CC BY-SA 3.0
2016-11-29T14:53:11.663
30778
2016-12-22T14:33:00.433
17238
question
c++ c
elfファイルの特定のシンボルの中身を後から差し替えたい


`gdb --write` を使うと手軽、かもしれません。 実行するわけではないので、ターゲットが ARM でもクロス gdb が使えると思います。

ただし、手元の Ubuntu 16.10 (amd64) の GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1)
では終了時に Segmentation fault となったり、書き換えが反映されなかったりと、動作があやしいです。 [Use gdb to Modify
Binary](https://stackoverflow.com/q/26173850/4937930) なども参照してください。

[code]

    $ cat hoge.c
    #include <stdio.h>
    const char TABLE[128] = {0};
    int main() { printf("%s\n", TABLE); return 0; }
    
    $ cat hoge.gdb
    set *(0 + (char *)&TABLE) = 'h'
    set *(1 + (char *)&TABLE) = 'o'
    set *(2 + (char *)&TABLE) = 'g'
    set *(3 + (char *)&TABLE) = 'e'
    p TABLE ← これをやらないと書き換えが反映されない…
    quit
    
    $ gcc -g hoge.c
    $ gdb --write -q -x hoge.gdb a.out
    Reading symbols from a.out...done.
    $1 = "hoge", '\000' <repeats 123 times>
    Segmentation fault (コアダンプ)
    $ ./a.out
    hoge
    
[/code]


CC BY-SA 3.0
2016-11-29T17:51:35.237
30781
2016-11-29T17:51:35.237
2017-05-23T12:38:56.083
-1
9873
30778
answer


`readelf` コマンドを使って、まずは `TABLE` 変数の「メモリアドレス」を表示してみます。ソースコードは yaegashi
さんの回答にあるものと同じであるとします。

[code]

    $ gcc -g -o a a.c # デバッグオプションを付ける
    $ readelf -s a | grep -w TABLE
        70: 080484e0   128 OBJECT  GLOBAL DEFAULT   16 TABLE
    
[/code]

`TABLE` 変数のメモリアドレスは `080484e0` となっています。`TABLE` 変数が格納されている場所を調べてみると、

[code]

    $ objdump -t a | grep -w TABLE
    SYMBOL TABLE:
    080484e0 g     O .rodata    00000080              TABLE
    
[/code]

`.rodata` セクションであることが判ります。

次に、ELF の `.rodata` セクションの情報を表示します。

[code]

    $ LC_MESSAGES=C readelf -S a | sed -rn '/^Section Headers:/,+1p;/\.rodata/{p;q;}'
    Section Headers:
      [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
      [16] .rodata           PROGBITS        080484c0 0004c0 0000a0 00   A  0
    
[/code]

`TABLE` 変数のメモリアドレスは `080484e0`、`.rodata` セクションは `080484c0` で、ファイルオフセットは
`0004c0` です。したがって、`TABLE` 変数のファイルオフセットは以下の様にして求めることができます。

[code]

    $ echo $((0x80484e0 - 0x80484c0 + 0x4c0))
    1248
    
[/code]

それでは実際に `TABLE` 変数の内容を書き換えてみましょう。

[code]

    $ printf 'Hello World!' | dd of=a bs=1 seek=1248 count=12 conv=notrunc
    12+0 records in
    12+0 records out
    12 bytes copied, 0.00128388 s, 9.3 kB/s
    $ ./a
    Hello World!
    
[/code]


CC BY-SA 3.0
2016-11-29T19:36:05.780
30782
2016-12-22T14:33:00.433
2016-12-22T14:33:00.433
30778
answer


まずソースの例は他の方の回答そのままで。

[code]

    $ cat hoge.c
    #include <stdio.h>
    const char TABLE[128] = {0};
    int main() { printf("%s\n", TABLE); return 0; }
    $ gcc hoge.c
    
[/code]

`const char` なら定数なので .rodata セクションに書かれるはずなので

[code]

    hdr=$(objdump -h a.out | fgrep .rodata)
    set -- $hdr
    RODATA_VMA=$4
    RODATA_OFFSET=$6
    
[/code]

と、まず `.rodata` セクションの VMA とファイルオフセットを取得し、次に

[code]

    TABLE_VMA=$(objdump -t -j .rodata a.out | awk '($6 == "TABLE"){print $1}')
    
[/code]

で `TABLE` の相対アドレスを取得、最後にそのファイルオフセット計算します

[code]

    file_addr=$(awk "END{print 0x${RODATA_OFFSET} + 0x${TABLE_VMA} - 0x${RODATA_VMA}}" /dev/null)
    
[/code]

必ずあるであろう awk を使ってますが、hex address の計算ができれば他のツールでも何でもいいです。最後に、書き換えは dd で

[code]

    printf 'hoge\x00' | dd of=a.out bs=1 seek=${file_addr} conv=notrunc 
    ./a.out
    hoge
    
[/code]

dd でのバイナリ編集は <http://qiita.com/albatross/items/a2e3c690875b321096d7> を参考にしました。

VMA と SYMBOL のアドレスが一致しない場合のチェックとか色々セーフティーチェックがないし、私も ELF フォーマットについては全く知らない（適当に
web と man 読んだだけ、学習時間約１時間）ので、at your own risk でお願いします。


CC BY-SA 3.0
2016-11-29T21:18:26.433
30783
2016-11-29T21:18:26.433
2521
30778
answer