Modifying a Dynamic Library Without Changing the Source Code


Modifying a Dynamic Library Without Changing the Source Code/元のプログラムコードを変更せずに動的プログラムライブラリを変更
By Greg Kroah-Hartman/繁体字中国語翻訳:Jim Huang(黄敬群)-jserv AT kaffe.org -
Created 2004-11-02 00:00/繁体字中国語翻訳時間:2005-01-16
透過LD_PRELAAD環境変数は自分のプログラムコードを置く簡単な動作です
プログラムライブラリ(library)を変更せずに動的プログラムライブラリでどのような結果が得られるか知りたい場合があります(最近glibcを構築してみましたか?)あるいは、CPUに対して処理したり、特定のUSBメッセージが送信されないようにしたり、類似の操作をしたりするために、プログラムライブラリのいくつかの関数(functions)を上書きしたい場合があります.これらの動作はLDを透過することができる.PRELAAD環境変数を実現し、必要なプログラムコードを簡単に置くことができます.
例えば、共有プログラムライブラリを構築することができます.例えば、shim.so(訳注:「shim」という言葉は英語で隙間に挟まれたガスケットを意味します)、このダイナミックプログラムライブラリを他の共有プログラムライブラリの前にロードしたいと思っています.私たちが実行したいプログラムが「test」になったら、以下のコマンドを使用できます.

LD_PRELOAD=/home/greg/test/shim.so ./test


以上のコマンドはglibcに、/home/greg/testディレクトリからshimをロードするように通知します.soダイナミックライブラリは、実行をロードします./testに必要な他のダイナミックライブラリ.
このshimプログラムライブラリを構築するのはかなり簡単です.kernel開発者のAnton Blanchardの古い手紙に感謝します.このような例について言及しています.
では、libusbプログラムライブラリが他のプログラムに呼び出された状況を記録するように設計しましょう.libusbはuser spaceのプログラムライブラリであり、kernel driverを介してUSBデバイスを操作する必要がなく、USBデバイスの情報にアクセスすることができます.
libusbが提供するusbを通じてOpen()式を出発し、以下のプログラムコードフラグメントは、本式へのアクセス状況を記録するために使用される.

usb_dev_handle *usb_open(struct usb_device *dev)
{
static usb_dev_handle *(*libusb_open)
(struct usb_device *dev) = NULL;
void *handle;
usb_dev_handle *usb_handle;
char *error;
if (!libusb_open) {
handle = dlopen("/usr/lib/libusb.so",
RTLD_LAZY);
if (!handle) {
fputs(dlerror(), stderr);
exit(1);
}
libusb_open = dlsym(handle, "usb_open");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s/n", error);
exit(1);
}
}
printf("calling usb_open(%s)/n", dev->filename);
usb_handle = libusb_open(dev);
printf("usb_open() returned %p/n", usb_handle);
return usb_handle;
}


次の方法でコンパイルできます.

gcc -Wall -O2 -fpic -shared -ldl -o shim.so shim.c


以上の命令は元のプログラムコードshimから実行する.c,shimという名前を確立する.soの共有プログラムライブラリ.テストプログラムが前述のLD_を透過している場合PRELAAD方式が実行されると、テストプログラム内のすべてのコールusb_Open()の呼び出しは、libusb.soの中ではなくshim.cの中の実作を使用します.私の手紙の実作では、以下のdlopenコールを通じて、実際のlibusbプログラムライブラリをロードしてみます.

handle = dlopen("/usr/lib/libusb.so",
RTLD_LAZY);


RTLDに注意LAZYのパラメータは、すべてのsymbolをローダにすぐに解析させたくないからです.逆にshimにsoの中のプログラムコードが必要なとき、解析の動作をします.
dlopen()式が成功すると、プログラムは真実を指すusb_を求めます.Open関数式のpointer(すなわちlibusb.soに格納されたusb_open()エントリポイント)は、dlsymコールを介して取得される.

libusb_open = dlsym(handle, "usb_open");


以上のコールが成功すればshim.soの手紙式は現在libusbの中の手紙式の本当のpointer(進入点)を持っている.これにより、任意の時間に呼び出すことができ、私たちの予想のために、記録動作が完了した後に、以下のように呼び出すことができます.

printf("calling usb_open(%p)/n", dev);
usb_handle = libusb_open(dev);


これにより、プログラムコードは、(実際の)関数呼び出し後であるが、元のプログラムに戻る前に、いくつかの動作を行うことができる.
例えばshimにsoは以下の出力方式を提供する.

calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061100
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_open(002)
usb_open() returned 0x8061120


より複雑な式を記録するために、例えばusb_control_Message()は、以前にusbに追加したものも使用します.Openの動作:

int usb_control_msg(usb_dev_handle *dev,
int requesttype,
int request,
int value,
int index,
char *bytes,
int size,
int timeout)
{
static int(*libusb_control_msg)
(usb_dev_handle *dev,
int requesttype, int request,
int value, int index, char *bytes,
int size, int timeout) = NULL;
void *handle;
int ret, i;
char *error;
if (!libusb_control_msg) {
handle = dlopen("/usr/lib/libusb.so", RTLD_LAZY);
if (!handle) {
fputs(dlerror(), stderr);
exit(1);
}
libusb_control_msg = dlsym(handle, "usb_control_msg");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s/n", error);
exit(1);
}
}
printf("calling usb_control_msg(%p, %04x, "
"%04x, %04x, %04x, %p, %d, %d)/n"
"/tbytes = ", dev, requesttype,
request, value, index, bytes, size,
timeout);
for (i = 0; i < size; ++i)
printf ("%02x ", (unsigned char)bytes[i]);
printf("/n");
ret = libusb_control_msg(dev, requesttype,
request, value,
index, bytes, size,
timeout);
printf("usb_control_msg(%p) returned %d/n"
"/tbytes = ", dev, ret);
for (i = 0; i < size; ++i)
printf ("%02x ", (unsigned char)bytes[i]);
printf("/n");
return ret;
}


透過LD_PREDLOAD=shim.soはテストプログラムを実行し、以下の出力を得る.

usb_open() returned 0x8061100
calling usb_control_msg(0x8061100, 00c0, 0013, 6c7e, c41b, 0x80610a8, 8, 1000)
bytes = c9 ea e7 73 2a 36 a6 7b
usb_control_msg(0x8061100) returned 8
bytes = 81 93 1a c4 85 27 a0 73
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_control_msg(0x8061120, 00c0, 0017, 9381, c413, 0x8061100, 8, 1000)
bytes = 39 83 1d cc 85 27 a0 73
usb_control_msg(0x8061120) returned 8
bytes = 35 15 51 2e 26 52 93 43
calling usb_open(002)
usb_open() returned 0x8061120
calling usb_control_msg(0x8061120, 00c0, 0017, 9389, c413, 0x8061108, 8, 1000)
bytes = 80 92 1b c6 e3 a3 fa 9d
usb_control_msg(0x8061120) returned 8
bytes = 80 92 1b c6 e3 a3 fa 9d


LD_を使ってPRELAAD環境変数は、ダイナミックライブラリの前に、自分の実作を簡単に置くことができます.
Greg Kroah-Hartman currently is the Linux kernel maintainer for a variety of different driver subsystems. He works for IBM, doing Linux kernel-related things, and can be reached at [email protected] [1].
Links [1] http://www.linuxjournal.com/
Source URL: http://www.linuxjournal.com/article/7795