web-dev-qa-db-ja.com

objdumpを使用して1つの関数を逆アセンブルする方法は?

システムにバイナリがインストールされています。特定の関数の逆アセンブリを確認したいと思います。 objdumpを使用することが望ましいですが、他のソリューションも同様に受け入れられます。

この質問 境界アドレスのみを知っていれば、コードの一部を逆アセンブルできるかもしれないことを学びました。 この回答 から、分割デバッグシンボルを単一のファイルに戻す方法を学びました。

しかし、その単一のファイルを操作し、すべてのコードを分解することさえできます(つまり、開始アドレスまたは停止アドレスはないが、プレーン-dパラメータをobjdumpに追加)、まだそのシンボルがどこにも表示されない。問題の関数が静的であり、エクスポートされない限り、これは理にかなっています。それでも、valgrindは関数名を報告するため、どこかに保存する必要があります。

デバッグセクションの詳細を見ると、.debug_strセクション、しかしこれをアドレス範囲に変えることができるツールを知りません。

68
MvG

最も簡単なアプローチとしてgdbを使用することをお勧めします。次のように、ワンライナーとして実行することもできます。

gdb -batch -ex 'file /bin/ls' -ex 'disassemble main'
67
Tom Tromey

gdb disassemble/rsはソースおよび生のバイトも表示します

この形式では、objdump -S出力に非常に近くなります。

gdb -batch -ex "disassemble/rs $FUNCTION" "$EXECUTABLE"

main.c

#include <assert.h>

int myfunc(int i) {
    i = i + 2;
    i = i * 2;
    return i;
}

int main(void) {
    assert(myfunc(1) == 6);
    assert(myfunc(2) == 8);
    return 0;
}

コンパイルと逆アセンブル

gcc -O0 -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
gdb -batch -ex "disassemble/rs myfunc" main.out

分解:

Dump of assembler code for function myfunc:
main.c:
3       int myfunc(int i) {
   0x0000000000001135 <+0>:     55      Push   %rbp
   0x0000000000001136 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000001139 <+4>:     89 7d fc        mov    %edi,-0x4(%rbp)

4           i = i + 2;
   0x000000000000113c <+7>:     83 45 fc 02     addl   $0x2,-0x4(%rbp)

5           i = i * 2;
   0x0000000000001140 <+11>:    d1 65 fc        shll   -0x4(%rbp)

6           return i;
   0x0000000000001143 <+14>:    8b 45 fc        mov    -0x4(%rbp),%eax

7       }
   0x0000000000001146 <+17>:    5d      pop    %rbp
   0x0000000000001147 <+18>:    c3      retq   
End of assembler dump.

Ubuntu 16.04、GDB 7.11.1でテスト済み。

objdump + awkの回避策

次の場所にあるように段落を印刷します。 https://unix.stackexchange.com/questions/82944/how-to-grep-for-text-in-a-file-and-display-the-paragraph-that -has-the-text

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <FUNCTION>/'

例えば。:

objdump -d main.out | awk -v RS= '/^[[:xdigit:]]+ <myfunc>/'

与えるだけ:

0000000000001135 <myfunc>:
    1135:   55                      Push   %rbp
    1136:   48 89 e5                mov    %rsp,%rbp
    1139:   89 7d fc                mov    %edi,-0x4(%rbp)
    113c:   83 45 fc 02             addl   $0x2,-0x4(%rbp)
    1140:   d1 65 fc                shll   -0x4(%rbp)
    1143:   8b 45 fc                mov    -0x4(%rbp),%eax
    1146:   5d                      pop    %rbp
    1147:   c3                      retq   

-Sを使用する場合、コードのコメントに可能なシーケンスが含まれている可能性があるため、フェイルプルーフの方法はないと思います...しかし、ほとんどの場合、次のように動作します。

objdump -S main.out | awk '/^[[:xdigit:]]+ <FUNCTION>:$/{flag=1;next}/^[[:xdigit:]]+ <.*>:$/{flag=0}flag'

適応: awk/sedで複数回発生する可能性のある2つのマーカーパターン間の行を選択する方法

メーリングリストの返信

メーリングリストには2010年のスレッドがあり、それは不可能だと書かれています: https://sourceware.org/ml/binutils/2010-04/msg00445.html

トムによって提案されたgdb回避策に加えて、彼らは-ffunction-sectionでコンパイルする別の(より悪い)回避策についてもコメントします。

Nicolas CliftonはWONTFIXを与えました https://sourceware.org/ml/binutils/2015-07/msg00004.html 、おそらくGDBの回避策がそのユースケースをカバーしているためです。

Objdumpを使用して1つの単一関数を逆アセンブルします

私には2つの解決策があります:

1.コマンドラインベース

この方法は完全に機能し、非常に短いです。 objdump-dオプションとpipeを使用してawk。逆アセンブルされた出力は次のようになります

000000000000068a <main>:
68a:    55                      Push   %rbp
68b:    48 89 e5                mov    %rsp,%rbp
68e:    48 83 ec 20             sub    $0x20,%rsp

sectionまたはfunctionは空行で区切られます。したがって、[〜#〜] fs [〜#〜](フィールドセパレーター)を改行に変更し、[ 〜#〜] rs [〜#〜](レコード区切り記号)を2回改行すると、$ 1フィールド内で見つけるだけなので、推奨機能を簡単に検索できます。

objdump -d name_of_your_obj_file | awk -F"\n" -v RS="\n\n" '$1 ~ /main/'

もちろん、mainを出力したい任意の関数に置き換えることができます。

2. Bashスクリプト

この問題のための小さなbashスクリプトを作成しました。単にコピーして、たとえばdasmファイル。

#!/bin/bash
# Author: abu
# Description: puts disassembled objectfile to std-out

if [ $# = 2 ]; then
        sstrg="^[[:xdigit:]]{2,}+.*<$2>:$"
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '$1 ~ /'"$sstrg"'/'
Elif [ $# = 1 ]; then
        objdump -d $1 | awk -F"\n" -v RS="\n\n" '{ print $1 }'
else
    echo "You have to add argument(s)"
    echo "Usage:   "$0 " arg1 arg2"  
    echo "Description: print disassembled label to std-out"
    echo "             arg1: name of object file"
    echo "             arg2: name of function to be disassembled"
    echo "         "$0 " arg1    ... print labels and their rel. addresses" 
fi

x-accessを変更して、たとえば:

chmod +x dasm
./dasm test main

これは、スクリプトでgdbを呼び出すよりもmuch速いです。 objdumpを使用する方法とは別に、ライブラリをメモリにロードするnotため、安全です!


Vitaly Fadeevは、このスクリプトにオートコンプリートをプログラムしました。これは本当にすてきな機能であり、入力を高速化します。

スクリプトは here にあります。

11
abu_bua

これは、遅延がないことを除いて、gdbソリューションと同じように機能します(オフセットをゼロにシフトするという点で)(gdbソリューションは約150msかかりますが、PCでは約5msでジョブが完了します):

objdump_func:

#!/bin/sh
# $1 -- function name; rest -- object files
fn=$1; shift 1
exec objdump -d "$@" | 
awk " /^[[:xdigit:]].*<$fn>/,/^\$/ { print \$0 }" |
awk -F: -F' '  'NR==1 {  offset=strtonum("0x"$1); print $0; } 
                NR!=1 {  split($0,a,":"); rhs=a[2]; n=strtonum("0x"$1); $1=sprintf("%x", n-offset); printf "%4s:%s\n", $1,rhs }'
4
PSkocik

他の回答と比較してobjdumpの出力を解析するためにawkの使用を簡素化するには:

objdump -d filename | sed '/<functionName>:/,/^$/!d'
3
fcr

./dasmのBash補完

完全なシンボル名から このソリューション (D langバージョン):

  • dasm testと入力してから TabTab、すべての関数のリストを取得します。
  • dasm test mと入力してから TabTab mで始まるすべての関数が表示されます。または、関数が1つしか存在しない場合は、自動補完されます。

ファイル/etc/bash_completion.d/dasm

# bash completion for dasm
_dasm()
{
    local cur=${COMP_WORDS[COMP_CWORD]}

    if [[ $COMP_CWORD -eq 1 ]] ; then
    # files
    COMPREPLY=( $( command ls *.o -F 2>/dev/null | grep "^$cur" ) )

    Elif [[ $COMP_CWORD -eq 2 ]] ; then
    # functions
    OBJFILE=${COMP_WORDS[COMP_CWORD-1]}

    COMPREPLY=( $( command nm --demangle=dlang $OBJFILE | grep " W " | cut -d " " -f 3 | tr "()" "  " | grep "$cur" ) )

    else
    COMPREPLY=($(compgen -W "" -- "$cur"));
    fi
}

complete -F _dasm dasm
2
Vitaly Fadeev