動的なむンストルメンテヌションは単玔ではありたせんが、些现なこずですAmerican Fuzzy Lop甚の別のむンストルメンテヌションを蚘述したす

*実際には、そうではありたせん。
おそらく、Valgrind-ネむティブプログラムのどこでメモリリヌクが発生しおいるか、初期化されおいない倉数によっお分岐がどこにあるかなどを知るこずができるデバッガヌおよびmemcheckの他に、他の操䜜モヌドもありたすに぀いお倚くの人が聞いたこずがあるでしょう。 内郚的には、このすばらしいプログラムはネむティブコヌドを䞭間バむトコヌドに粉砕し、それを指瀺し、新しいマシンコヌドを生成したす-すでにランタむムチェックがありたす。 しかし、問題がありたす。ValgrindはWindowsでの䜜業方法を知りたせん。 必芁なずきに、怜玢によっおDrMemoryず呌ばれる同様のナヌティリティが芋぀かりたした 。これもアナログstraceでした。 しかし、それはそれらに぀いおではなく、それらが構築されおいるこずに基づいた動的蚈装ラむブラリ、 DynamoRIOに぀いおです。 ある時点で、私は自分のむンストルメンテヌションの䜜成ずいう点でこのラむブラリに興味を持ち、ドキュメントを探し始め、 倚数の䟋に出䌚い、 呌び出し呜什のカりントのような完党なむンストルメンテヌションが237行のコヌドで蚘述できるこずに驚いた32そのうちの-ラむセンス、および8-説明。 いいえ、もちろん、これは「30行のJavaScriptコヌドでValgrindキラヌを䜜成する」のではなく、そのようなタスクで想像できるよりもはるかに簡単です。


䟋ずしお、最近Habréで曞かれたFuzzer American Fuzzy Lopの蚈装の4番目の実装を曞きたしょう。


AFLずは䜕ですか


AFLは、鉄筋コンクリヌトから組み立おられた、バグや脆匱性を怜玢するためのガむド付きファゞングツヌルです。 束葉杖 最も自明で効率的な方法で実装されたヒュヌリスティック。 したがっお、libjpegの動䜜を芳察しお有効なゞヌプを合成できるツヌルを芋るず、このすべおがそれほど耇雑ではないメカニズムに基づいお行われおいるこずに驚かされたす。 芁するに、本栌的なAFL操䜜では、実行䞭に゚ッゞカバヌを収集するようにタヌゲットバむナリをむンストルメント化する必芁がありたす。各基本ブロック 基本ブロック 、ラベルから最も近い遷移呜什たでの䞀連の呜什のようなものをグラフの頂点ずしお想像しおください。 BBは、BB間で制埡を移す可胜な方法です。 したがっお、AFLは、プログラムの基本ブロック間でどのような遷移が発生し、およそ䜕回発生したのかに関心がありたす。


AFLでむンストルメントする䞻な方法は、コンパむル段階でafl-gcc / afl-g++ラッパヌたたはclang類䌌物を䜿甚しお静的です。 afl-gccこずに、 afl-gccは、呌び出さasコマンドを、コンパむラヌによっお生成されたアセンブラヌのリストを䞊曞きするラッパヌに眮き換えたす。 llvm modeず呌ばれる、より高床なオプションがありllvm mode 。これはコンパむルプロセスもちろんLLVMを䜿甚しお生成されllvm mode に正盎に統合されるため、理論的には生成コヌドのパフォヌマンスが向䞊したす。 最埌に、すでにコンパむルされたバむナリをファゞングするためのqemu modeがありqemu mode -必芁なむンストルメンテヌションを远加する1぀のプロセスの゚ミュレヌションモヌドでのQEMUのパッチ最初は、このQEMUモヌドの動䜜は、ホストカヌネルを䜿甚しお別のアヌキテクチャ甚にアセンブルされた個別のプロセスを開始するこずを目的ずしおいたした


DynamoRIOずは


DynamoRIOは動的むンストルメンテヌションシステムです぀たり、実行時に既にコンパむル枈みのバむナリを盎接指瀺したす。x86およびx86_64アヌキテクチャのWindows、Linux、Android、およびARMリリヌス候補バヌゞョン7.0はAArch64をサポヌトしたすで実行されたす。 QEMUずは異なり、他の誰かのアヌキテクチャで「テキストに近い」プログラムを実行するこずを意図しおいたせんが、ネむティブアヌキテクチャでプログラムの動䜜を倉曎する独自のツヌルを簡単に䜜成するこずを目的ずしおいたす。 同時に、可胜な堎合、目暙は最適化されたコヌドを砎損しないこずです。 残念ながら、倉換が行われないため、 クラむアント いわゆるカスタムむンストルメンテヌションラむブラリがタヌゲット呜什セットに぀いお知らない方法を芋぀けたこずがありたせん基本的な呜什に十分なクロスプラットフォヌムラッパヌが存圚する堎合を陀きたす。マシンコヌド->バむトコヌド-[instrumentation]->新しいバむトコヌド->むンストルメントされたマシンコヌド "。 代わりに、各ブロヌドキャストベヌスナニットに察しお、クラむアントはデコヌドされた呜什のリストを受信したす。これは、䟿利な機胜ずマクロで修正および補足できたす。 ぀たり、マシンコヌドでプログラミングする必芁はありたせんが、ほずんどの堎合、x86呜什たたは別のプラットフォヌムのセットを知っおいる必芁がありたす。


ちょっずしたボヌナスずしお、 Githubアカりントで他に䜕が面癜いかを調べおみお、興味深いリポゞトリDRKに出䌚いたした。 リポゞトリは攟棄されおおり、倚少叀くなっおいるようですが、説明は印象的です。


DRKは、ロヌド可胜なLinuxカヌネルモゞュヌルずしおのDynamoRIOです。 DRKがロヌドされるず、すべおのカヌネルモヌド実行システムコヌル、割り蟌みおよび䟋倖ハンドラヌ、カヌネルスレッドなどはDynamoRIOの範囲内で行われたすが、ナヌザヌモヌド実行は倉曎されたせん-ナヌザヌをむンストルメントする通垞のDynamoRIOの逆です。モヌドプロセスであり、カヌネルモヌドの実行には圱響したせん。

テストプログラム


たず、AFLの機胜を芋おみたしょう。 いいえ、脆匱なバヌゞョンのラむブラリを䜿甚しお数時間たたは数日埅぀こずはありたせん。 テストのために、文字NULL始たる行をstdinに枡すず、nullポむンタヌを逆参照する最も愚かなプログラムを䜜成しNULL 。 これは、もちろん、どこからずもなくゞヌプを合成したものではありたせんが、埅぀必芁はほずんどありたせん。


したがっお、 ここから AFL をダりンロヌドしお収集しおください。 おそらく既にご想像のずおり、GNU / Linuxでアセンブルしたす。 ただし、他のUnixラむクなシステムやMac OS XのようなUnixも動䜜するはずです。 小さなプログラムを取りたす


 #include <stdio.h> #include <string.h> volatile int *ptr = NULL; const char cmd[] = "NULL"; int main(int argc, char *argv[]) { char buf[16]; fgets(buf, sizeof buf, stdin); if (strncmp(buf, cmd, 4)) { return 0; } *ptr = 1; return 0; } 

コンパむルしおファゞングを実行したす。


 $ export AFL_PATH=~/tmp/build/afl-2.42b/ $ #   ,     $ $AFL_PATH/afl-gcc example-bug-libc.c -o example-bug-libc $ #  -    ( ) $ mkdir input $ echo test > input/1 $ #   $ $AFL_PATH/afl-fuzz -i input -o output -- ./example-bug-libc 

そしお、私たちは䜕を芋たす



どういうわけか機胜したせん...最埌の新しいパスに泚意しおください。AFLは、9侇1千回の発売埌、新しいパスを芋぀けられなかったこずを誓いたす。 実際、これは非垞に論理的です。アセンブラヌを呌び出す段階で静的むンスツルメンテヌションを䜿甚したこずを思い出したす。 䞻な比范はlibcの関数によっお行われたすが、これは装備されおいないため、䞀臎する文字の数を蚈算するこずはできたせん。 それで、私はそれをチェックするこずを決めるたで考えたしたが、私たちのバむナリはstrncmp関数をむンポヌトしないこずが刀明したした。 objdump -dの出力から刀断するず、コンパむラヌは、 strncmp代わりに、むンストルメンテヌションをstrncmpルヌプではなく、接頭郚をstrncmp呜什を単に生成したした。


strncmpでむンストゥルメントされたメむン関数
 00000000000007f0 <.plt.got>: 7f0: ff 25 82 17 20 00 jmpq *0x201782(%rip) # 201f78 <getenv@GLIBC_2.2.5> 7f6: 66 90 xchg %ax,%ax 7f8: ff 25 8a 17 20 00 jmpq *0x20178a(%rip) # 201f88 <_exit@GLIBC_2.2.5> 7fe: 66 90 xchg %ax,%ax 800: ff 25 8a 17 20 00 jmpq *0x20178a(%rip) # 201f90 <write@GLIBC_2.2.5> 806: 66 90 xchg %ax,%ax 808: ff 25 8a 17 20 00 jmpq *0x20178a(%rip) # 201f98 <__stack_chk_fail@GLIBC_2.4> 80e: 66 90 xchg %ax,%ax 810: ff 25 8a 17 20 00 jmpq *0x20178a(%rip) # 201fa0 <close@GLIBC_2.2.5> 816: 66 90 xchg %ax,%ax 818: ff 25 8a 17 20 00 jmpq *0x20178a(%rip) # 201fa8 <read@GLIBC_2.2.5> 81e: 66 90 xchg %ax,%ax 820: ff 25 92 17 20 00 jmpq *0x201792(%rip) # 201fb8 <fgets@GLIBC_2.2.5> 826: 66 90 xchg %ax,%ax 828: ff 25 9a 17 20 00 jmpq *0x20179a(%rip) # 201fc8 <waitpid@GLIBC_2.2.5> 82e: 66 90 xchg %ax,%ax 830: ff 25 a2 17 20 00 jmpq *0x2017a2(%rip) # 201fd8 <shmat@GLIBC_2.2.5> 836: 66 90 xchg %ax,%ax 838: ff 25 a2 17 20 00 jmpq *0x2017a2(%rip) # 201fe0 <atoi@GLIBC_2.2.5> 83e: 66 90 xchg %ax,%ax 840: ff 25 aa 17 20 00 jmpq *0x2017aa(%rip) # 201ff0 <__cxa_finalize@GLIBC_2.2.5> 846: 66 90 xchg %ax,%ax 848: ff 25 aa 17 20 00 jmpq *0x2017aa(%rip) # 201ff8 <fork@GLIBC_2.2.5> 84e: 66 90 xchg %ax,%ax ... 0000000000000850 <main>: 850: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp 857: ff 858: 48 89 14 24 mov %rdx,(%rsp) 85c: 48 89 4c 24 08 mov %rcx,0x8(%rsp) 861: 48 89 44 24 10 mov %rax,0x10(%rsp) 866: 48 c7 c1 04 6a 00 00 mov $0x6a04,%rcx 86d: e8 9e 02 00 00 callq b10 <__afl_maybe_log> 872: 48 8b 44 24 10 mov 0x10(%rsp),%rax 877: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 87c: 48 8b 14 24 mov (%rsp),%rdx 880: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp 887: 00 888: 53 push %rbx 889: be 10 00 00 00 mov $0x10,%esi 88e: 48 83 ec 20 sub $0x20,%rsp 892: 48 8b 15 77 17 20 00 mov 0x201777(%rip),%rdx # 202010 <stdin@@GLIBC_2.2.5> 899: 48 89 e7 mov %rsp,%rdi 89c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 8a3: 00 00 8a5: 48 89 44 24 18 mov %rax,0x18(%rsp) 8aa: 31 c0 xor %eax,%eax 8ac: e8 6f ff ff ff callq 820 <.plt.got+0x30> 8b1: 48 8d 3d dc 06 00 00 lea 0x6dc(%rip),%rdi # f94 <cmd> 8b8: b9 04 00 00 00 mov $0x4,%ecx 8bd: 48 89 e6 mov %rsp,%rsi 8c0: f3 a6 repz cmpsb %es:(%rdi),%ds:(%rsi) 8c2: 75 45 jne 909 <main+0xb9> 8c4: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp 8cb: ff 8cc: 48 89 14 24 mov %rdx,(%rsp) 8d0: 48 89 4c 24 08 mov %rcx,0x8(%rsp) 8d5: 48 89 44 24 10 mov %rax,0x10(%rsp) 8da: 48 c7 c1 2d 5b 00 00 mov $0x5b2d,%rcx 8e1: e8 2a 02 00 00 callq b10 <__afl_maybe_log> 8e6: 48 8b 44 24 10 mov 0x10(%rsp),%rax 8eb: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 8f0: 48 8b 14 24 mov (%rsp),%rdx 8f4: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp 8fb: 00 8fc: 48 8b 05 1d 17 20 00 mov 0x20171d(%rip),%rax # 202020 <ptr> 903: c7 00 01 00 00 00 movl $0x1,(%rax) 909: 0f 1f 00 nopl (%rax) 90c: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp 913: ff 914: 48 89 14 24 mov %rdx,(%rsp) 918: 48 89 4c 24 08 mov %rcx,0x8(%rsp) 91d: 48 89 44 24 10 mov %rax,0x10(%rsp) 922: 48 c7 c1 8f 33 00 00 mov $0x338f,%rcx 929: e8 e2 01 00 00 callq b10 <__afl_maybe_log> 92e: 48 8b 44 24 10 mov 0x10(%rsp),%rax 933: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 938: 48 8b 14 24 mov (%rsp),%rdx 93c: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp 943: 00 944: 31 c0 xor %eax,%eax 946: 48 8b 54 24 18 mov 0x18(%rsp),%rdx 94b: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx 952: 00 00 954: 75 40 jne 996 <main+0x146> 956: 66 90 xchg %ax,%ax 958: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp 95f: ff 960: 48 89 14 24 mov %rdx,(%rsp) 964: 48 89 4c 24 08 mov %rcx,0x8(%rsp) 969: 48 89 44 24 10 mov %rax,0x10(%rsp) 96e: 48 c7 c1 0a 7d 00 00 mov $0x7d0a,%rcx 975: e8 96 01 00 00 callq b10 <__afl_maybe_log> 97a: 48 8b 44 24 10 mov 0x10(%rsp),%rax 97f: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 984: 48 8b 14 24 mov (%rsp),%rdx 988: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp 98f: 00 990: 48 83 c4 20 add $0x20,%rsp 994: 5b pop %rbx 995: c3 retq 996: 66 90 xchg %ax,%ax 998: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp 99f: ff 9a0: 48 89 14 24 mov %rdx,(%rsp) 9a4: 48 89 4c 24 08 mov %rcx,0x8(%rsp) 9a9: 48 89 44 24 10 mov %rax,0x10(%rsp) 9ae: 48 c7 c1 a8 dc 00 00 mov $0xdca8,%rcx 9b5: e8 56 01 00 00 callq b10 <__afl_maybe_log> 9ba: 48 8b 44 24 10 mov 0x10(%rsp),%rax 9bf: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 9c4: 48 8b 14 24 mov (%rsp),%rdx 9c8: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp 9cf: 00 9d0: e8 33 fe ff ff callq 808 <.plt.got+0x18> 9d5: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 9dc: 00 00 00 9df: 90 nop 

同じ、ただし蚈装なし
 0000000000000630 <.plt.got>: 630: ff 25 82 09 20 00 jmpq *0x200982(%rip) # 200fb8 <strncmp@GLIBC_2.2.5> 636: 66 90 xchg %ax,%ax 638: ff 25 8a 09 20 00 jmpq *0x20098a(%rip) # 200fc8 <__stack_chk_fail@GLIBC_2.4> 63e: 66 90 xchg %ax,%ax 640: ff 25 92 09 20 00 jmpq *0x200992(%rip) # 200fd8 <fgets@GLIBC_2.2.5> 646: 66 90 xchg %ax,%ax 648: ff 25 aa 09 20 00 jmpq *0x2009aa(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5> 64e: 66 90 xchg %ax,%ax ... 0000000000000780 <main>: 780: 55 push %rbp 781: 48 89 e5 mov %rsp,%rbp 784: 48 83 ec 30 sub $0x30,%rsp 788: 89 7d dc mov %edi,-0x24(%rbp) 78b: 48 89 75 d0 mov %rsi,-0x30(%rbp) 78f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 796: 00 00 798: 48 89 45 f8 mov %rax,-0x8(%rbp) 79c: 31 c0 xor %eax,%eax 79e: 48 8b 15 6b 08 20 00 mov 0x20086b(%rip),%rdx # 201010 <stdin@@GLIBC_2.2.5> 7a5: 48 8d 45 e0 lea -0x20(%rbp),%rax 7a9: be 10 00 00 00 mov $0x10,%esi 7ae: 48 89 c7 mov %rax,%rdi 7b1: e8 8a fe ff ff callq 640 <.plt.got+0x10> 7b6: 48 8d 45 e0 lea -0x20(%rbp),%rax 7ba: ba 04 00 00 00 mov $0x4,%edx 7bf: 48 8d 35 ce 00 00 00 lea 0xce(%rip),%rsi # 894 <cmd> 7c6: 48 89 c7 mov %rax,%rdi 7c9: e8 62 fe ff ff callq 630 <.plt.got> 7ce: 85 c0 test %eax,%eax 7d0: 74 07 je 7d9 <main+0x59> 7d2: b8 00 00 00 00 mov $0x0,%eax 7d7: eb 12 jmp 7eb <main+0x6b> 7d9: 48 8b 05 40 08 20 00 mov 0x200840(%rip),%rax # 201020 <ptr> 7e0: c7 00 01 00 00 00 movl $0x1,(%rax) 7e6: b8 00 00 00 00 mov $0x0,%eax 7eb: 48 8b 4d f8 mov -0x8(%rbp),%rcx 7ef: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx 7f6: 00 00 7f8: 74 05 je 7ff <main+0x7f> 7fa: e8 39 fe ff ff callq 638 <.plt.got+0x8> 7ff: c9 leaveq 800: c3 retq 801: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 808: 00 00 00 80b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 

興味深いこずに、 strncmpはstrncmpれおいstrncmp正盎に呌び出されるため、AFL自䜓が最適化をオンにしたstrncmp 。 afl-gccによっおコンパむルされたコヌド内の膚最したPLTに぀いおは、明らかに、forkserverによっお呌び出される関数が衚瀺されたす。これも蚘述したすが、最初に最初に蚘述したす。 さお、これを芋なかったふりをしお、ラむブラリ関数なしで単玔に䟋を曞き換えおみたしょう


 #include <stdio.h> volatile int *ptr = NULL; const char cmd[] = "NULL"; int main(int argc, char *argv[]) { char buf[16]; fgets(buf, sizeof buf, stdin); for (int i = 0; i < sizeof cmd - 1; ++i) { if (buf[i] != cmd[i]) return 0; } *ptr = 1; return 0; } 

コンパむル、実行、... tadam



フォヌクサヌバヌを曞く


すでに述べたように、AFLは、調査䞭のプログラムの基本ブロック間の遷移に関する情報を収集する蚈装の可甚性を想定しおいたす。 ただし、 afl-gccによっお远加された最適化 forkserverがありたす。 その意味は、 fork - execveバンドルを䜿甚しおプログラムを再起動するこずではなく、毎回動的リンクなどを実行し、fuzzerプロセス内でforkを䜜成しおから、むンストルメント枈みプログラムに実行したす。 調査したプログラムをどのように再起動したすか ポむントは、この実行䞭のプロセスをテストしないこずです。 代わりに、無限ルヌプでファザヌからのコマンドを埅機し、 fork䜜成し、子プロセスが完了するたで埅機し、結果に぀いおコヌルバックするコヌドを実行したす。 しかし、出芜プロセスはすでに入力デヌタを実際に凊理し、゚ッゞカバレッゞに関する情報を収集したす。 afl-gcc堎合、そのロゞックを正しく理解しおいれば、forkserverはむンストルメント枈みコヌドに初めおアクセスしたずきに起動したす。 llvm modeの堎合、 遅延forkserver llvm modeもサポヌトされたす-この方法では、動的リンクだけでなく、怜蚎䞭のプロセスの暙準である初期化もスキップできたす。これにより、プロセス党䜓を桁違いにスピヌドアップできたすが、考慮すべきいく぀かのニュアンスがありたす



Llvmモヌドは氞続モヌドもサポヌトしたす 。このモヌドでは、耇数のテストケヌスが同じ発芜プロセス内で連続しお実行されたす。 おそらく、これによりオヌバヌヘッドコストはさらに削枛されたすが、次のテスト䟋でのプログラム実行の結果は、珟圚の䟋だけでなく起動履歎にも䟝存する危険性がありたす。 ずころで、蚘事の執筆を開始した埌、 WindowsのAFLポヌトもむンストルメンテヌションにDynamoRIOを䜿甚し、氞続モヌドのみを䜿甚するずいう情報に出䌚いたした。 さお、 forkサポヌトなしで他に䜕が残っおいたすか


そのため、フォヌクサヌバヌをDynamoRIOで䜜成する必芁がありたすが、たず、ファザヌプロセスずの盞互䜜甚に必芁なプロトコルを理解する必芁がありたす。 䞊蚘のAFL䜜者のブログぞのリンクから䜕かを収集できたすが、 llvm_mode/afl-llvm-rt.ocでllvm_mode/afl-llvm-rt.ocファむルを芋぀ける方が簡単です。 たくさんの興味深いものがありたすが、最初に__afl_start_forkserver関数を芋おみたしょう-コメントも__afl_start_forkserverお、すべおがそこで詳现に説明されおいたす。 氞続モヌドには興味がありたせん。そうでなければ、すべおが明確です。既知の番号を持぀2぀のファむル蚘述子が䞎えられたす。䞀方から読み取り、他方に曞き蟌みたす。 必芁なもの


  1. ゜ヌスからの匕甚 電話をかけ、芪に倧䞈倫だず䌝えたす 。 任意の4バむトを曞き蟌みたす。
  2. 4バむトを読み取りたす。 私たちの堎合氞続モヌドなしには䞍倉なchild_stopped == 0があるように芋えるため、正確に読み取ったものは重芁ではありたせん。
  3. ファザヌずの通信のために、子プロセスを䜜成し、その䞭のファむル蚘述子を閉じたす。
  4. 子プロセスのPIDで4バむトをファむル蚘述子に曞き蟌み、それプロセスが完了するのを埅ちたす。
  5. 埅機した埌、戻りコヌドでさらに4バむトを曞き蟌み、ステップ2に進みたす。

そしお、実際にクラむアントを曞くこずになりたした。 ここでは、ドキュメンテヌションの䟋は数癟行しか取らないが、ドキュメンテヌションを読む䟡倀があるずいう䜙談をする必芁がありたす。 たずえば、 ここから始めるこずができたす。特にここでは、すべきでないこずに぀いお曞かれおいたす。 たずえば、よく知られおいる文孊的なキャラクタヌを蚀い換えるず、「蚈装クラむアントは非垞に奇劙なものですそこにあるように芋えたすが、なくなっおいるようです」、これはドキュメントではクラむアントの透明性ず呌ばれたす 特に、システムラむブラリのコピヌを䜿甚する必芁がありたすプラむベヌトロヌダヌが圹立぀もの、たたはDynamoRIO APIを䜿甚したすメモリの割り圓お、コマンドラむンオプションの解析など。 API関数のドキュメントにも重芁な情報がありたす。たずえば、 dr_register_bb_event関数の説明には、11項目の短いリストが瀺されおおり、むンストルメンテヌション埌に結果ずしお生じる呜什のシヌケンスを満たす必芁がありたす。


DynamoRIOのクラむアントのビルドを管理するには、CMakeを䜿甚するこずをお勧めしたす-CMakeを䜿甚したす。 ドキュメントでこれを行う方法に぀いお読むこずができたすが、さらに興味深い質問に進みたす。 たずえば、遅延フォヌクサヌバヌを䜜成するには、調査䞭のプログラムでその起動堎所を䜕らかの方法でマヌクし、DynamoRIOでこのマヌクを芋぀ける必芁がありたすただし、調査䞭のプログラム内の通垞の関数の呌び出しずしおフォヌクサヌバヌを䜜成するこずを劚げるものはないようですしかし、それは面癜くありたせんか、幞いなこずに、そのような機胜はDynamoRIOに組み蟌たれおおり、 泚釈ず呌ばれたす。 クラむアントの開発者は、調査䞭のプログラムにリンクする必芁のある特別な静的ラむブラリを収集し、必芁な堎所でこのラむブラリから関数を呌び出す必芁がありたす。通垞の実行䞭は䜕も実行したせんが、DynamoRIOで実行されるず、特定の定数に眮き換えられるか、関数呌び出し。


私は公匏の教科曞を繰り返さず、ヘッダヌファむルをDynamoRIO配信からコピヌし、泚釈の名前ず眲名の䞋に2぀のマクロの「呌び出し」を䜜り盎しこのファむルはテスト䞭のプログラムに接続する必芁がありたす、たた゜ヌスコヌドを䜜成するこずを提案したすマクロぞの1回の呌び出し。これにより、泚釈甚のスタブの実装が生成されたすテスト察象のプログラムにリンクする必芁がある静的ラむブラリがそこから構築されたす。


forkserverの実装に぀いおは、 libcコピヌからforkを呌び出すのがおそらくあたり正しくないこずを陀いお、すべおが非垞に簡単ですたずえば、forkserver実装の機胜に関する䞊蚘の蚘事のAFLの著者は、 libc PIDをキャッシュするず述べおいたす。 調査䞭のアプリケヌションのlibcコピヌからfork呌び出すず、発生したfork呌び出しに぀いお知るこずができ、DynamoRIOは私もそれに気づくでしょう。 だから私は次のようなものを曞かなければならなかった


 module_data_t *module = dr_lookup_module_by_name("libc.so.6"); EXIT_IF_FAILED(module != NULL, "Cannot lookup libc.\n", 1) fork_fun_t fork_ptr = (fork_fun_t)dr_get_proc_address(module->handle, "fork"); EXIT_IF_FAILED(fork_ptr != NULL, "Cannot get fork function from libc.\n", 1) dr_free_module_data(module); 

したがっお、プログラムの起動時に埓来のforkserver起動モヌドをサポヌトするには、この時点でlibcがすでに利甚可胜であるこずを確認する必芁がありたす。


これをテストしようずするず、奇劙な動䜜に遭遇したした。テスト䞭のプログラムは起動したしたが、forkserverは起動したせんでした。 dr_client_main印刷を远加しdr_client_main -衚瀺されたせんでした。 ドラフトコヌドのアセンブリディレクトリに切り替えたす。 うヌん...それは動䜜したす。 nm -Dの出力を、動䜜䞭および動䜜しないむンストルメンテヌションラむブラリず比范したしたdr_client_main関数がありたす。 もう䞀床比范したす-本圓にありたす... drrunオプション-verbose -debug指定されおい-verbose -debug 。 , , , . QtCreator .../build-afl-dr-Desktop_3fb6e5- , "" — .../build-afl-dr-Desktop- . , . , , , , , , , 
 (, - .)


:


 $ ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug Running forkserver... Cannot connect to fuzzer. 1 $ ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug 198<&0 199>/dev/null Running forkserver... xxxx 1 Incorrect spawn command from fuzzer. 

, - . , AFL. dumb mode ( ), - forkserver:


 $ AFL_DUMB_FORKSRV=1 $AFL_PATH/afl-fuzz -i input -o output -n -- ./example-bug ... -   Fork server handshake failed -- ,   ... $ AFL_DUMB_FORKSRV=1 $AFL_PATH/afl-fuzz -i input -o output -n -- ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug ... -   Timeout while initializing fork server (adjusting -t may help) $ #   strace- ... $ AFL_DUMB_FORKSRV=1 $AFL_PATH/afl-fuzz -i input -o output -n -m 2048 -- ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug ...  ,     ( -m) 


, . .


: forkserver , SIGSEGV. , , , , fork libc , , . , extension droption . , dr_option_t<T> , , , dr_client_main droption_parser_t::parse_argv(...) (, C++). , 7.0 RC1, 6.2.0-2 , CMake droption - . . , , , , drutil .


:


afl-annotations.h
 // Based on dr_annotations.h from DynamoRIO sources #ifndef _AFL_DR_ANNOTATIONS_H_ #define _AFL_DR_ANNOTATIONS_H_ 1 #include "annotations/dr_annotations_asm.h" /* To simplify project configuration, this pragma excludes the file from GCC warnings. */ #ifdef __GNUC__ # pragma GCC system_header #endif #define RUN_FORKSERVER() \ DR_ANNOTATION(run_forkserver) #ifdef __cplusplus extern "C" { #endif DR_DECLARE_ANNOTATION(void, run_forkserver, ()); #ifdef __cplusplus } #endif #endif 

afl-annotations.c
 #include "afl-annotations.h" DR_DEFINE_ANNOTATION(void, run_forkserver, (), ); 

afl-dr.c
 #include <dr_api.h> #include <droption.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #include "afl-annotations.h" static const int FROM_FUZZER_FD = 198; static const int TO_FUZZER_FD = 199; typedef int (*fork_fun_t)(); #define EXIT_IF_FAILED(isOk, msg, code) \ if (!(isOk)) { \ dr_fprintf(STDERR, (msg)); \ dr_exit_process((code)); \ } static droption_t<bool> opt_private_fork(DROPTION_SCOPE_CLIENT, "private-fork", false, "Use fork function from the private libc", "Use fork function from the private libc"); static void parse_options(int argc, const char *argv[]) { std::string parse_err; if (!droption_parser_t::parse_argv(DROPTION_SCOPE_CLIENT, argc, argv, &parse_err, NULL)) { dr_fprintf(STDERR, "Incorrect client options: %s\n", parse_err.c_str()); dr_exit_process(1); } } static void start_forkserver() { // For references, see https://lcamtuf.blogspot.ru/2014/10/fuzzing-binaries-without-execve.html // and __afl_start_forkserver in llvm_mode/afl-llvm-rt.oc from AFL sources static bool forkserver_is_running = false; uint32_t unused_four_bytes = 0; uint32_t was_killed; if (!forkserver_is_running) { dr_printf("Running forkserver...\n"); forkserver_is_running = true; } else { dr_printf("Warning: Attempt to re-run forkserver ignored.\n"); return; } if (write(TO_FUZZER_FD, &unused_four_bytes, 4) != 4) { dr_printf("Cannot connect to fuzzer.\n"); return; } fork_fun_t fork_ptr; // Lookup the fork function from target application, so both DynamoRIO // and application's copy of libc know about fork // Currently causes crashes sometimes, in that case use the private libc's fork. if (!opt_private_fork.get_value()) { module_data_t *module = dr_lookup_module_by_name("libc.so.6"); EXIT_IF_FAILED(module != NULL, "Cannot lookup libc.\n", 1) fork_ptr = (fork_fun_t)dr_get_proc_address(module->handle, "fork"); EXIT_IF_FAILED(fork_ptr != NULL, "Cannot get fork function from libc.\n", 1) dr_free_module_data(module); } else { fork_ptr = fork; } while (true) { EXIT_IF_FAILED(read(FROM_FUZZER_FD, &was_killed, 4) == 4, "Incorrect spawn command from fuzzer.\n", 1) int child_pid = fork_ptr(); EXIT_IF_FAILED(child_pid >= 0, "Cannot fork.\n", 1) if (child_pid == 0) { close(TO_FUZZER_FD); close(FROM_FUZZER_FD); return; } else { int status; EXIT_IF_FAILED(write(TO_FUZZER_FD, &child_pid, 4) == 4, "Cannot write child PID.\n", 1) EXIT_IF_FAILED(waitpid(child_pid, &status, 0) >= 0, "Wait for child failed.\n", 1) EXIT_IF_FAILED(write(TO_FUZZER_FD, &status, 4) == 4, "Cannot write child exit status.\n", 1) } } } DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { parse_options(argc, argv); EXIT_IF_FAILED( dr_annotation_register_call("run_forkserver", (void *)start_forkserver, false, 0, DR_ANNOTATION_CALL_TYPE_FASTCALL), "Cannot register forkserver annotation.\n", 1); } 


, , . , , AFL, . , afl-gcc afl-as.c , afl-as.h . AFL , , , technical details . ,


 cur_location = <COMPILE_TIME_RANDOM>; shared_mem[cur_location ^ prev_location]++; prev_location = cur_location >> 1; 

( AFL). , , , 1 — . 64- shared memory, llvm_mode/afl-llvm-rt.oc .


, DynamoRIO, . , ( , , / ) DynamoRIO . , dr_register_bb_event . , , thread-local , , . , -, , :


 //  dr_client_main: lock = dr_mutex_create(); dr_register_thread_init_event(event_thread_init); dr_register_thread_exit_event(event_thread_exit); dr_register_bb_event(event_basic_block); dr_register_exit_event(event_exit); 

:


 typedef struct { uint64_t scratch; uint8_t map[MAP_SIZE]; } thread_data; static void event_thread_init(void *drcontext) { void *data = dr_thread_alloc(drcontext, sizeof(thread_data)); memset(data, 0, sizeof(thread_data)); dr_set_tls_field(drcontext, data); } static void event_thread_exit(void *drcontext) { thread_data *data = (thread_data *) dr_get_tls_field(drcontext); dr_mutex_lock(lock); for (int i = 0; i < MAP_SIZE; ++i) { shmem[i] += data->map[i]; } dr_mutex_unlock(lock); dr_thread_free(drcontext, data, sizeof(thread_data)); } 


 . , -, , DynamoRIO , , thread-specific memory pool. -, --- thread_data , tls field.


, : event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) , . , — instrlist_t *bb . ( ) , , , amd64 aka x86_64. DynamoRIO , dr_register_bb_event . , basic block:


DR constructs dynamic basic blocks, which are distinct from a compiler's classic basic blocks. DR does not know all entry points ahead of time, and will end up duplicating the tail of a basic block if a later entry point is discovered that targets the middle of a block created earlier, or if a later entry point targets straight-line code that falls through into code already present in a block.

, , — !


. , , :


 static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *where = instrlist_first(bb); reg_id_t tls_reg = DR_REG_XDI, offset_reg = DR_REG_XDX; dr_save_arith_flags(drcontext, bb, where, SPILL_SLOT_1); dr_save_reg(drcontext, bb, where, tls_reg, SPILL_SLOT_2); dr_save_reg(drcontext, bb, where, offset_reg, SPILL_SLOT_3); dr_insert_read_tls_field(drcontext, bb, where, tls_reg); //     dr_restore_reg(drcontext, bb, where, offset_reg, SPILL_SLOT_3); dr_restore_reg(drcontext, bb, where, tls_reg, SPILL_SLOT_2); dr_restore_arith_flags(drcontext, bb, where, SPILL_SLOT_1); return DR_EMIT_DEFAULT; } 

tls_reg . COMPILE_TIME_RANDOM. , event_basic_block : for_trace translating . , , , DynamoRIO . , , , for_trace = true , . , , , translating = true — , , , translating = false . , DR_EMIT_STORE_TRANSLATIONS DR_EMIT_DEFAULT , , . , by design, , . , basic block.


 void *app_pc = dr_fragment_app_pc(tag); uint32_t cur_location = ((uint32_t)(uintptr_t)app_pc * (uint32_t)33533) & 0xFFFF; 

, ASLR , . , " ", sysctl -w kernel.randomize_va_space=0 .


, . . API , . .:


  instrlist_meta_preinsert(bb, where, XINST_CREATE_load(drcontext, opnd_create_reg(offset_reg), OPND_CREATE_MEM64(tls_reg, offsetof(thread_data, scratch)))); instrlist_meta_preinsert(bb, where, INSTR_CREATE_xor(drcontext, opnd_create_reg(offset_reg), OPND_CREATE_INT32(cur_location))); instrlist_meta_preinsert(bb, where, XINST_CREATE_store(drcontext, OPND_CREATE_MEM32(tls_reg, offsetof(thread_data, scratch)), OPND_CREATE_INT32(cur_location >> 1))); instrlist_meta_preinsert(bb, where, INSTR_CREATE_inc(drcontext, opnd_create_base_disp(tls_reg, offset_reg, 1, offsetof(thread_data, map), OPSZ_1))); 

, , , -- , Segmentation fault. , XINST_CREATE_load XINST_CREATE_store :


 $ ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so --private-fork -- ./example-bug Cannot get SHM id from environment. Creating dummy map. Running forkserver... Cannot connect to fuzzer. ^C $ #  load  store $ ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so --private-fork -- ./example-bug Cannot get SHM id from environment. Creating dummy map. <Application /path/to/example-bug (5058). Tool internal crash at PC 0x00005605e72bbeaa. Please report this at your tool's issue tracker. Program aborted. Received SIGSEGV at pc 0x00005605e72bbeaa in thread 5058 Base: 0x00005605e71c5000 Registers:eax=0x0000000000000001 ebx=0x00007ff6dfa12038 ecx=0x0000000000000048 edx=0x0000000000000000 esi=0x0000000000000049 edi=0x0000000000000005 esp=0x00007ff6dfa0ebb0 ebp=0x00007ff6dfa0ebc0 r8 =0x0000000000000003 r9 =0x0000000000000005 r10=0x0000000000000000 r11=0x00005605e72b70ef r12=0x0000000000000000 r13=0x0000000000000000 r14=0x000000000000000c r15=0x00005605e7368c50 eflags=0x0000000000010202 version 6.2.0, build 2 -no_dynamic_options -client_lib '/path/to/libafl-dr.so;0;"--private-fork"' -code_api -stack_size 56K -max_elide_jmp 0 -max_elide_call 0 -early_inject -emulate_brk -no_inline_ignored_syscalls -native_exec_default_list '' -no_native_exec_managed_code -no_indcall2direct 0x00007ff6dfa0ebc0 0x0000020803000000> 

どうする , . -debug -loglevel 1 -logdir /tmp/dynamorio/ , - :


 ERROR: Could not find encoding for: mov (%rdi)[8byte] -> %rdx SYSLOG_ERROR: Application /path/to/example-bug (5192) DynamoRIO usage error : instr_encode error: no encoding found (see log) SYSLOG_ERROR: Usage error: instr_encode error: no encoding found (see log) (/dynamorio_package/core/arch/x86/encode.c, line 2417) 

, : , — .


, , :


 $ $AFL_PATH/afl-fuzz -i input -o output -m 2048 -- ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug afl-fuzz 2.42b by <lcamtuf@google.com> [+] You have 4 CPU cores and 1 runnable tasks (utilization: 25%). [+] Try parallel jobs - see docs/parallel_fuzzing.txt. [*] Checking CPU core loadout... [+] Found a free CPU core, binding to #0. [*] Checking core_pattern... [*] Checking CPU scaling governor... [*] Setting up output directories... [+] Output directory exists but deemed OK to reuse. [*] Deleting old session data... [+] Output dir cleanup successful. [*] Scanning 'input'... [+] No auto-generated dictionary tokens to reuse. [*] Creating hard links for all input files... [*] Validating target binary... [-] Looks like the target binary is not instrumented! The fuzzer depends on compile-time instrumentation to isolate interesting test cases while mutating the input data. For more information, and for tips on how to instrument binaries, please see docs/README. When source code is not available, you may be able to leverage QEMU mode support. Consult the README for tips on how to enable this. (It is also possible to use afl-fuzz as a traditional, "dumb" fuzzer. For that, you can use the -n option - but expect much worse results.) [-] PROGRAM ABORT : No instrumentation detected Location : check_binary(), afl-fuzz.c:6894 


 
 ? : strace , , drrun . , afl-fuzz , ? , , AFL — ? , , , , afl-fuzz.c:6894 , :


 f_data = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0); // ... if (!qemu_mode && !dumb_mode && !memmem(f_data, f_len, SHM_ENV_VAR, strlen(SHM_ENV_VAR) + 1)) { // ... FATAL("No instrumentation detected"); } 

-, : AFL __AFL_SHM_ID — , . - , echo -ne "__AFL_SHM_ID\0" >> /path/to/drrun , , : , , AFL_SKIP_BIN_CHECK :


 $ #  -d      $ AFL_SKIP_BIN_CHECK=1 $AFL_PATH/afl-fuzz -i input -o output -m 2048 -d -- ~/soft/DynamoRIO-Linux-6.2.0-2/bin64/drrun -c libafl-dr.so -- ./example-bug 


, AFL , , , total paths: 12 , 4-5. , , libc. example-bug , , (, DynamoRIO, ). 



最適化



 , . , , " ". module_data_t * , dr_client_main , event_basic_block , " ":


 module_data_t *main_module; //  dr_client_main: main_module = dr_get_main_module(); //  event_basic_block: if (!opt_instrument_everything.get_value() && !dr_module_contains_addr(main_module, pc)) { return DR_EMIT_DEFAULT; } 

80 ( 2 ), output/queue test NU — , .


, DynamoRIO -thread_private , . tls field , , , immediate-:


 if (dr_using_all_private_caches()) { instrlist_meta_preinsert(bb, where, INSTR_CREATE_mov_imm(drcontext, opnd_create_reg(tls_reg), OPND_CREATE_INTPTR(dr_get_tls_field(drcontext)))); } else { dr_insert_read_tls_field(drcontext, bb, where, tls_reg); } 

-thread_private ( -c libafl-dr.so , DynamoRIO, ), 5 . , if (0 && dr_using_all_private_caches()) , — , DynamoRIO . :)


, : -disable_traces — , , , , , , . 
 10-15 . , , , , , test case-.


, , "" forkserver-: example-bug.c


 ungetc('1', stdin); char ch; fscanf(stdin, "%c", &ch); RUN_FORKSERVER(); 


 15 . , , : , - libc forkserver, , - .


. :




 . , : " API / ?" , ", , !" — , . , , , , — , .


参照



UPD: , ( qemu_mode ). API , 5. , , .



Source: https://habr.com/ru/post/J332076/


All Articles