CEO of Backflip180, LLC.
ここでは、API Spooferを使って乱数を制御します。 乱数を使うアプリケーションでは、実行結果が予測可能ではないため、テストを行うのが困難です。 そこで、乱数を制御して決まった値を返すようにすることで、テストをやりやすくします。
インストール方法についてはAPI Spooferのインストールを参照してください。
ここでは、乱数の値を3つ表示するだけの簡単なアプリケーションを使います。 ソースは次のようになります。
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
srand((unsigned) time(NULL));
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
このコードをコンパイルし、何度か実行してみます。
$ gcc test.c
$ ./a.out
1913766507
1688256889
1376930303
$ ./a.out
1611767276
430798400
957049420
$ ./a.out
230746457
238110194
1602118775
毎回実行結果が違うことが確認できます。
それではAPI Spooferを使って乱数を制御してみましょう。 次のようにapi_spooferコマンドを実行し、コードを生成します。
$ api_spoofer a.out > rand_spoofer.c
$ gcc -ldl -shared -fPIC rand_spoofer.c -o rand_spoofer.so
無事にrand_spoofer.soが生成されたらLD_PRELOADにrand_spoofer.soを指定して実行してみます。
$ LD_PRELOAD=rand_spoofer.so ./a.out
1536164458
389977121
200945937
$ LD_PRELOAD=rand_spoofer.so ./a.out
1233129365
1276903885
1928278290
$ LD_PRELOAD=rand_spoofer.so ./a.out
1233129365
1276903885
1928278290
このように、以前と変わらず乱数が表示されることを確認します。 次に、rand_spoofer.cのsrandを書き換えることで初期化するシードを毎回同じにします。
void srand(unsigned int __seed)
{
typedef void (*ftype)(unsigned int);
((ftype)dlsym(RTLD_NEXT, "srand"))(1);
}
コンパイルして再度実行します。
$ gcc -fPIC -shared rand_spoofer.c -ldl -o rand_spoofer.so
$ LD_PRELOAD=rand_spoofer.so ./a.out
1804289383
846930886
1681692777
$ LD_PRELOAD=rand_spoofer.so ./a.out
1804289383
846930886
1681692777
$ LD_PRELOAD=rand_spoofer.so ./a.out
1804289383
846930886
1681692777
毎回同じ結果になることがわかります。 今度は直接randの戻り値を上書きしてみます。
int rand()
{
return 10;
}
同様にコンパイルして実行してみます。
$ gcc -fPIC -shared rand_spoofer.c -ldl -o rand_spoofer.so
$ LD_PRELOAD=rand_spoofer.so ./a.out
10
10
10
$ LD_PRELOAD=rand_spoofer.so ./a.out
10
10
10
$ LD_PRELOAD=rand_spoofer.so ./a.out
10
10
10
このように、上書きした値が常に返るようになっていることがわかります。
今回はAPI Spooferを使って、srandやrandを上書きすることで、実行ファイルに手を加えることなく、乱数を制御する方法を紹介しました。 このテクニックを応用することで、乱数を使ったアプリケーションの実行結果を制御することが可能になります。