拾い物のコンパス

まともに書いたメモ

radare2のエイリアス機能をソースコードレベルでいじって使いやすくしてみる

 この記事はプログラムのソースコードを書き換えるものです.行う際は自己責任でお願いします.

 radare2エイリアスやマクロを使っていたが,どうにもコマンドを打ちにくいのでソースコードをいじって試行錯誤してみた.マクロ機能をいじるのは大規模な改変になりそうだったため,今回はエイリアスのみをいじることにした.結果エイリアスの識別文字を$からnにすると,比較的使いやすくなったのでそのやり方をまとめておく.
ついでに,改変後のエイリアス機能を利用した動的解析用エイリアスも紹介する.作成したエイリアスPedaを使ったGDBを意識した.

エイリアス機能

radare2エイリアス機能は以下のように定義・使用することができる.

* 定義
  $dis=`af;pdf`
* 使用
  $dis

詳しくは$?で見ることができる.

問題点

 まず基礎知識として,radare2は最初に機能を大別する文字(今回ならば$)の後に個別のコマンドが続くようになっている.
エイリアス機能は多用するのにタイプしにくい.これは識別文字に$を使っていることが原因であると考えた.
$を入力するShift+4って何回も打つのは苦痛だ,できればアルファベットのどれか,かつShiftを押さないで済ましたい.
ソースコードを読んでみると,$を定義している部分のコメントにはprefix aliases with a dashとあった.英語は苦手だが,とりあえず「dashを参考にしてエイリアスのプレフィックスを$にしました」と理解した(間違っていたらぜひ指摘してください).
恐らく,dashを起動した時に左に出てくる$のことだと思う.dashに固執する特別な意味は感じなかったので変えても問題はないだろうと判断した.
そこで,エイリアス機能の識別子である$を現在コマンドがマッピングされていないnに変更することにした.

準備

 Githubからソースコードをダウンロードする.この記事には関係はないが,Ubuntuaptなどでインストールされるradare2はバージョンが古いので,Githubからのインストールをおすすめしたい.
$ git clone https://github.com/radare/radare2 && cd radare2

編集

 次に,libr/core/cmd.cを以下のように編集する.やってることは,$nに置き換えているだけ.現在マッピングされているコマンドとかぶらなければ良いので,nでなくとも問題はない.他に現在コマンドがマッピングされていない文字はh j vがある(大文字であればもっと存在するが,Shiftを押したくないので今回は考えない).行番号はバージョンによって異なるので,適宜修正すること.

// libr/core/cmd.c
 128 static int cmd_alias(void *data, const char *input) {
 129   int i;
 130   char *def, *q, *desc, *buf;
 131   RCore *core = (RCore *)data;
       // help_msgの'$'を'n'に置き換える
 132   if (*input=='?') {
 133     const char* help_msg[] = {
 134       "Usage:", "nalias[=cmd] [args...]", "Alias commands",
 135       "n", "", "list all defined aliases",
 136       "n*", "", "same as above, but using r2 commands",
 137       "n", "dis='af;pdf'", "create command - analyze to show function",
 138       "n", "test=#!pipe node /tmp/test.js", "create command - rlangpipe script",
 139       "n", "dis=", "undefine alias",
 140       "n", "dis", "execute the previously defined alias",
 141       "n", "dis?", "show commands aliased by 'analyze'",
 142       NULL};
 143       r_core_cmd_help (core, help_msg);
 144     return 0;
 145   }
 ...
       // コマンド解析時の識別文字を変更.ここが要
 149   *buf = 'n';
 ...
       // 何も押さないでEnterした時に前回のコマンドが実行される機能を'n'で動くようにする.
2354 R_API void r_core_cmd_repeat(RCore *core, int next) {
2355   // Fix for backtickbug px`~`
2356   if (core->cmd_depth + 1 < R_CORE_CMD_DEPTH)
2357     return;
2358   if (core->lastcmd)
2359   switch (*core->lastcmd) {
2360   case 'd': // debug
2361     r_core_cmd0 (core, core->lastcmd);
2362     switch (core->lastcmd[1]) {
2363     case 's':
2364     case 'c':
2365       r_core_cmd0 (core, "sr PC;pd 1");
2366     }
2367     break;
2368   case 'p': // print
2369   case 'x':
       // ここを変更.これを忘れると,入力なしの時に前回のコマンドを実行する機能が動かない
2370   case 'n':
2371     if (next) {
2372       r_core_seek (core, core->offset + core->blocksize, 1);

インストール

セーブしたら,
$ sys/install.sh
でインストールできる.
ローカルにインストールしたいときは
$ sys/user.sh
削除は
$ make uninstall && make purge
で行うことができる.

確認

無事インストールが終わったら,反映されているかを確認する.

$ r2 /bin/ls
[0x00404840]> n?
|Usage: nalias[=cmd] [args...]Alias commands
| n                               list all defined aliases
| n*                              same as above, but using r2 commands
| ndis='af;pdf'                   create command - analyze to show function
| ntest=#!pipe node /tmp/test.js  create command - rlangpipe script
| ndis=                           undefine alias
| ndis                            execute the previously defined alias
| ndis?                           show commands aliased by 'analyze'
[0x00404840]> ndis='af;pdf'
[0x00404840]> n*
ndis=af;pdf
[0x00404840]> ndis
/ (fcn) entry0 42
|           0x00404840      31ed           xor ebp, ebp
|           0x00404842      4989d1         mov r9, rdx
|           0x00404845      5e             pop rsi
|           0x00404846      4889e2         mov rdx, rsp
|           0x00404849      4883e4f0       and rsp, 0xfffffffffffffff0
...(snip)...

これでエイリアス機能の識別文字がnになっていることが確認できた. しかし,エイリアスの定義では引数を渡す文字の@を使うことができない.
[0x00404840]> ndis='pd 10@eip
とすると,

[0x00404840]> n*
ndis='pd 10

と途切れてしまう.これはひと手間かけることで解決できる.

[0x00404840]> (test,"",pd 10@eip)
[0x00404840]> ntest=.(test)`  

マクロを定義して,そのマクロのエイリアスを設定している.これで,@問題は解決できる.

デバッグエイリアス

最後に,デバッグでよく使いそうなマクロ・エイリアスを紹介する.一部はradare2bookに載っているものを活用した.これを$HOME/.radare2rcに書いておくと起動時に毎回読み込んでくれる.

// .radare2rc for 32bit
(peda32,"",drr,pd 10@eip,pxr 40@esp)
(stepOut32,"",dso,drr,pd 10@eip,pxw 64@esp)
(stepIn32,"",ds,drr,pd 10@eip,pxw 64@esp)
(outputStack32,"",pxw 64@esp)
nn=.(stepIn32)                              ; GDBでの'si'
ni=.(stepOut32)                             ; GDBでの'ni'
nw=.(outputStack32)                         ; GDBでの'x/20xw $esp'
np=.(peda32)                                ; pedaによる出力に似た何か
ns='do;db main;dc;db- main;np'              ; GDBでの`start'.デバッグ情報が消されている場合は動作しない
nr='do;dc;np'                               ; GDBでの'run'
nc='dc;np'                                  ; GDBでの'continue'
// .radare2rc for 64bit <- この行は書かない
(peda64,"",drr,pd 10@rip,pxr 40@rsp)
(stepOut64,"",dso,drr,pd 10@rip,pxq 64@rsp)
(stepIn64,"",ds,drr,pd 10@rip,pxq 64@rsp)
(outputStack64,"",pxq 64@rsp)

nn=.(stepIn64)
ni=.(stepOut64)
nq=.(outputStack64)
np=.(peda64)
ns='do;db main;dc;db- main;np'
nr='do;dc;np'
nc='dc;np'

コマンド名は最初がnであることが条件.それ以降は自由に変えてよい.

まとめ

とりあえず,これでエイリアス機能が指をあまり動かさずに呼び出せるようになったので楽になった.しばらくはこの設定で使ってみようと思う.
書き換える箇所があれだけで済んだこととソースが読みやすかったことには感動した.機能をひとつのファイル内で完結させる部分とかは今後プログラムを書く上で意識していきたい.
しかし,もっと良い方法がデフォルトでなにかあるんじゃないかなと思いもする.Bookを読んでなにか気づいたらまた書きたい.

参考文献

Radare2Book: https://www.gitbook.com/book/radare/radare2book/details