16

まず、次のケースを考えてみましょう。

以下はプログラムです:

// test.cpp
extern "C" void printf(const char*, ...);

int main() {
        printf("Hello");
}

以下はライブラリです。

// ext.cpp (the external library)
#include <iostream>

extern "C" void printf(const char* p, ...);

void printf(const char* p, ...) {
        std::cout << p << " World!\n";
}

これで、上記のプログラムとライブラリを 2 つの異なる方法でコンパイルできます。

最初の方法は、外部ライブラリをリンクせずにプログラムをコンパイルすることです:

$ g++ test.cpp -o test
$ ldd test
        linux-gate.so.1 =>  (0xb76e8000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7518000)
        /lib/ld-linux.so.2 (0xb76e9000)

上記のプログラムを実行すると、次のように出力されます。

$ ./test 
Hello

2 番目の方法は、外部ライブラリへのリンクを使用してプログラムをコンパイルすることです。

$ g++ -shared -fPIC ext.cpp -o libext.so
$ g++ test.cpp -L./ -lext  -o test
$ export LD_LIBRARY_PATH=./
$ ldd test
        linux-gate.so.1 =>  (0xb773e000)
        libext.so => ./libext.so (0xb7738000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756b000)
        libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb7481000)
        /lib/ld-linux.so.2 (0xb773f000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb743e000)
        libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb7421000)
$ ./test
Hello World!

ご覧のとおり、最初のケースではプログラムはprintffromlibc.soを使用しますが、2 番目のケースではprintffromを使用しlibext.soます。

私の質問は、最初のケースのように取得した実行可能ファイルと libext のオブジェクト コード (.so または .o のいずれか) から、2 番目のケースのように実行可能ファイルを取得することは可能ですか? 言い換えれば、後者で定義されているすべてのシンボルに対して、へのlibc.soリンクをへのリンクに置き換えることは可能ですか?libext.so

**LD_PRELOAD による挿入は、私が望んでいるものではないことに注意してください。必要なライブラリに直接リンクされている実行可能ファイルを取得したい。最初のバイナリと、「静的に」挿入したい外部オブジェクトにしかアクセスできないという事実にもう一度下線を引きます **

4

8 に答える 8

2

バイナリを変更する必要があります。patchelf を見てみましょうhttp://nixos.org/patchelf.html

RPATHまたは「インタープリター」、つまりld-linux-x86-64.soを別のものに設定または変更できます。

ユーティリティの説明から:

動的にリンクされた ELF 実行可能ファイルは、常に動的リンカーまたはインタープリターを指定します。これは、動的にリンクされたすべてのライブラリと共に実行可能ファイルを実際にロードするプログラムです。(カーネルは、実行可能ファイルではなく、インタープリターをロードするだけです。) たとえば、Linux/x86 システムでは、ELF インタープリターは通常、ファイル /lib/ld-linux.so.2 です。

したがって、ライブラリをロードする独自のインタープリターを使用して、問題のバイナリ (つまりテスト) で patchelf を実行することができます...これは難しいかもしれませんが、ld-linux-so のソースコードは利用可能です...

オプション 2 は、ライブラリのリストを自分で変更することです。少なくとも patchelf は、コードがライブラリのリストを反復するという点で開始点を提供します (コードの DT_NEEDED を参照)。

elf 仕様のドキュメントは、順序が実際に重要であることを示しています。

DT_NEEDED: この要素は、ヌル終了文字列の文字列テーブル オフセットを保持し、必要なライブラリの名前を指定します。オフセットは、DT_STRTAB エントリに記録されたテーブルへのインデックスです。これらの名前の詳細については、「共有オブジェクトの依存関係」を参照してください。動的配列には、このタイプのエントリが複数含まれる場合があります。これらのエントリの相対的な順序は重要ですが、他のタイプのエントリとの関係は重要ではありません。

あなたの質問の性質は、あなたがプログラミングに精通していることを示しています :-) patchelf への追加に貢献する良い機会かもしれません... バイナリ内のライブラリの依存関係を変更します。

または、あなたの意図は、patchelf が作成されたことを正確に実行することである可能性があります... とにかく、これが役に立てば幸いです!

于 2013-08-21T22:32:03.380 に答える
0

静的ではありませんが、Anthony Shoumikhin によって作成されたユーティリティを使用して、共有ライブラリに動的に読み込まれたシンボルを独自の関数にリダイレクトできます。elf-hook

一般的な使用法は、編集できないサードパーティの共有ライブラリ内から特定の関数呼び出しをリダイレクトすることです。

サード パーティのライブラリが にあり、ライブラリ内からの呼び出し/tmp/libtest.soをリダイレクトしたいが、他の場所からの への呼び出しは影響を受けないようにしたいとします。printfprintf

サンプルアプリ:

lib.h

#pragma once

void test();

lib.cpp

#include "lib.h"
#include <cstdio>

void test()
{
    printf("hello from libtest");
}

この例では、上記の 2 つのファイルが共有ライブラリにコンパイルされ、次のlibtest.so場所に格納されます。/tmp

main.cpp

#include <iostream>
#include <dlfcn.h>
#include <elf_hook.h>
#include "lib.h"

int hooked_printf(const char* p, ...)
{
    std::cout << p << " [[ captured! ]]\n";
    return 0;
}

int main()
{
    // load the 3rd party shared library
    const char* fn = "/tmp/libtest.so";
    void* h = dlopen(fn, RTLD_LAZY);

    // redirect printf calls made from within libtest.so
    elf_hook(fn, LIBRARY_ADDRESS_BY_HANDLE(h), "printf", (void*)hooked_printf);

    printf("hello from my app\n"); // printf in my app is unaffected

    test(); // test is the entry point to the 3rd party library

    dlclose(h);
    return 0;
}

出力

hello from my app
hello from libtest [[ captured! ]]

ご覧LD_PRELOADのとおり、 を設定せずに独自の関数を挿入することが可能であり、インターセプトされる関数をより細かく制御できるという追加の利点があります。

ただし、関数は静的に挿入されるのではなく、動的にリダイレクトされます

elf-hook ライブラリの GitHub ソースはこちら、Anthony Shoumikhin によって書かれた完全な codeproject 記事はこちら

于 2013-08-26T21:35:22.207 に答える
0

バイナリを変更することは可能です。

たとえば、ghex のようなツールを使用すると、バイナリの 16 進コードを変更できます。コード内で libc.so の各インスタンスを検索し、それを libext.so に置き換えます。

于 2013-08-28T13:33:20.953 に答える