3

unix/linux についてもっと知りたいのですが、この質問が頭に浮かびました。たとえば、静的/動的ライブラリ (.a または .so) を作成し、c/c++ ソース コードとヘッダー ファイルを紛失したとします。デフォルトの nm 出力にはシンボルの名前が表示されますが、ヘッダーを作成するには、戻り値の型とパラメーターの数/型を知る必要があります。この追加情報を取得して、特定のライブラリのヘッダーをリバース エンジニアリングすることはできますか?

4

2 に答える 2

3

C と C++ にタグを付けましたが、答えは 2 つの間でわずかに異なります。

C++ の場合、クラスのメソッド名には、シンボル名に埋め込まれた型情報があります。ライブラリをコンパイルしたコンパイラがどのような名前マングリングを行ったかを理解する必要があります。

Cの場合、それを行うための本当にきれいな方法はありません。アセンブリを分解し、関数が受け取るパラメーターの数を把握するために、書き込まれずに読み取られるレジスターとスタック領域を分析できます。これには、ライブラリをコンパイルしたコンパイラが使用する呼び出し規約の知識が必要です。

同様に、各パラメータがアセンブリでどのように使用されているかを確認できます。ロード命令で使用されている場合は、何らかのポインターである可能性が高く、算術で使用されている場合は、何らかの整数である可能性があります。

戻り値の型については、一見意味のあるものが戻り命令の前に戻りレジスタに配置されているかどうかを確認できます。繰り返しますが、これには、プラットフォームの呼び出し規則に関する知識が必要です。

これは、ARM アセンブリでどのように行うかの例です。

ARM のパラメータはレジスタ r0 から r3 に渡され、戻り値はレジスタ r0 に格納されることを知っています。それを念頭に置いて、リバースエンジニアリングを開始できます。2 つの関数のアセンブリを見て、関数のプロトタイプが何であったかを考えてみましょう。

00000000 <func1>:
   0:   e3510000    cmp r1, #0
   4:   0a000007    beq 28 <func1+0x28>
   8:   e0801001    add r1, r0, r1
   c:   e1a03000    mov r3, r0
  10:   e3a00000    mov r0, #0
  14:   e4d32001    ldrb    r2, [r3], #1
  18:   e1530001    cmp r3, r1
  1c:   e0800002    add r0, r0, r2
  20:   1afffffb    bne 14 <func1+0x14>
  24:   e12fff1e    bx  lr
  28:   e1a00001    mov r0, r1
  2c:   e12fff1e    bx  lr

ここを見ると、r0 と r1 は両方とも、何かが書き込まれる前に読み取られます。また、r2 と r3 が読み取られる前に書き込まれていることもわかります。func1したがって、には最大 2 つのパラメーターがあると推測できます。

また、r0 が r3 に移動され、ldrbメモリからバイトをロードする命令である へのアドレスとして使用されることもわかります。したがって、最初のパラメーターはポインターであると推測します。この命令は 1 バイトしかロードしないため、何らかの 1 バイト データ型へのポインタである可能性があることもわかります。

r1 の 2 番目のパラメーターは、比較命令と加算命令以外では使用されないように見えるため、整数である可能性があります。

それぞれbx lr(return-to-caller 命令) の前に、何かが r0 に配置されるため、関数が何らかの値を返すと推測されます。

この関数が提示された場合、関数のプロトタイプは次のようになると思います。

int func1(unsigned char *, int);

オリジナル:

unsigned int func1(void *, unsigned int);

ここに別の関数があります

00000030 <func2>:
  30:   e0822001    add r2, r2, r1
  34:   e5c02000    strb    r2, [r0]
  38:   e12fff1e    bx  lr

これはとても簡単です。

r0、r1、r2 はすべて、書き込まれる前に読み取られることがわかります。したがって、関数が 3 つのパラメーターを受け取ることが推測できます。strbr0 は命令 (ストアバイト)へのアドレスとして使用されるため、おそらくポインターです。繰り返しますが、1バイトしか格納しないため、おそらくバイトサイズのデータ​​型へのポインターです。

他の 2 つは加算命令でのみ使用されるため、おそらく整数です。

最後に r0 には何も配置されていないように見えるため、関数は最初のパラメーターを返すか、値を返しません。

プロトタイプは次のいずれかになると思います

void func2(unsigned char *, int, int);
unsigned char *func2(unsigned char *, int, int);

オリジナル:

void func2(char *, char, char);
于 2013-06-30T05:28:06.037 に答える
0

呼び出し元/呼び出し先の規則はプロセッサの命令セットごとに異なり、c ライブラリと c++ ライブラリを一緒に使用しているときに名前マングリングを既に認識していることを念頭に置いて、次の方法を試すことができます。

 gdb <executable>
 ....
 disas <function name>
 ....
 Here you can make a wild guess about the type of return value and parameters using the bit size of those values written on stack making use of assembly code.
于 2013-06-30T05:27:48.993 に答える