20

Linux で C/C++ コードを使用し、gdb を使用して、特定の文字列で中断するために受信文字列をスキャンする gdb ブレークポイントを追加するにはどうすればよいですか?

特定のライブラリのコードにアクセスすることはできませんが、そのライブラリが特定の文字列を標準出力に送信したらすぐに中断して、スタックに戻ってライブラリを呼び出しているコードの部分を調査できるようにしたいと考えています。もちろん、バッファ フラッシュが発生するまで待ちたくありません。これはできますか?おそらくのルーチンlibstdc++ですか?

4

4 に答える 4

26

この質問は良い出発点かもしれません.gdbの「何かが端末に出力されます」にブレークポイントを設定するにはどうすればよいですか?

したがって、何かが stdout に書き込まれるたびに、少なくとも壊れる可能性があります。この方法では、基本的に、最初の引数が(つまり STDOUT)writeであるという条件で、syscall にブレークポイントを設定します。コメントには、呼び出し1の文字列パラメーターを検査する方法についてのヒントもあります。write

x86 32 ビット モード

私は次のことを思いつき、gdb 7.0.1-debian でテストしました。それはかなりうまくいくようです。$esp + 8には、渡された文字列のメモリ位置へのポインタが含まれているwriteため、最初に整数にキャストし、次に へのポインタにキャストしますchar$esp + 4書き込み先のファイル記述子を含みます (STDOUT の場合は 1)。

$ gdb break write if 1 == *(int*)($esp + 4) && strcmp((char*)*(int*)($esp + 8), "your string") == 0

x86 64 ビット モード

プロセスが x86-64 モードで実行されている場合、パラメータはスクラッチ レジスタを介して渡され%rdi%rsi

$ gdb break write if 1 == $rdi && strcmp((char*)($rsi), "your string") == 0

スタック上の変数ではなくスクラッチ レジスタを使用しているため、1 レベルの間接性が削除されていることに注意してください。

バリアント

strcmp上記のスニペットで使用できる以外の関数:

  • strncmpn書き込まれている文字列の最初の文字数に一致させたい場合に便利です
  • strstr探している文字列が、関数によって書き込まれる文字列の先頭に あると常に確信できるとは限らないため、文字列内の一致を見つけるために使用できますwrite

編集:私はこの質問を楽しんで、その後の答えを見つけました。私はそれについてブログ投稿をすることにしました。

于 2011-11-22T23:59:48.153 に答える
5

catch+strstr状態

このメソッドの優れた点は、使用されている glibc に依存しないことwriteです。実際のシステム コールをトレースします。

さらに、printf()複数のprintf()呼び出しで出力される文字列をキャッチすることさえできるため、バッファリングに対する回復力が高くなります。

x86_64 バージョン:

define stdout
    catch syscall write
    commands
        printf "rsi = %s\n", $rsi
        bt
    end
    condition $bpnum $rdi == 1 && strstr((char *)$rsi, "$arg0") != NULL
end
stdout qwer

テストプログラム:

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    write(STDOUT_FILENO, "asdf1", 5);
    write(STDOUT_FILENO, "qwer1", 5);
    write(STDOUT_FILENO, "zxcv1", 5);
    write(STDOUT_FILENO, "qwer2", 5);
    printf("as");
    printf("df");
    printf("qw");
    printf("er");
    printf("zx");
    printf("cv");
    fflush(stdout);
    return EXIT_SUCCESS;
}

結果: 休憩:

  • qwer1
  • qwer2
  • fflush. 前のprintfものは実際には何も出力しませんでした。バッファリングされていました! syacallwriteは でのみ発生しましたfflush

ノート:

  • $bpnumTromey に感謝: https://sourceware.org/bugzilla/show_bug.cgi?id=18727
  • rdi: x86_64 での Linux システム コールの番号を含むレジスタ1は、write
  • rsi: syscall の最初の引数。writeバッファを指しているからです。
  • strstr: 標準 C 関数呼び出し、サブマッチを検索し、見つからない場合は NULL を返します

Ubuntu 17.10、gdb 8.0.1 でテスト済み。

トレース

インタラクティブに感じている場合の別のオプション:

setarch "$(uname -m)" -R strace -i ./stdout.out |& grep '\] write'

出力例:

[00007ffff7b00870] write(1, "a\nb\n", 4a

そのアドレスをコピーして、次の場所に貼り付けます。

setarch "$(uname -m)" -R strace -i ./stdout.out |& grep -E '\] write\(1, "a'

この方法の利点は、通常の UNIX ツールを使用してstrace出力を操作できることと、深い GDB-fu を必要としないことです。

説明:

于 2015-07-27T20:59:31.097 に答える