CTF班・第14回の活動をしました

2年部員の井上 (@sei0o)です。昨日Macに入れているディストリビューションをopenSUSEからArchに変えました。ついさっき日本語入力ができるように(一応)なったので、ブログも日本語で書けます。

久しぶりだったので適当に近況を共有したあと、「プログラミングの先生がEmacsをやたら推してくる」という理由でEmacsのソースリーディングっぽいことをしました。2年生2人と1年生2人。

とりあえず起動してすぐ実行されるmain関数の冒頭を読み解いていきました。具体的にいうとコマンドライン引数のパース部分です。1年生はまだプログラミングの授業が始まったばかりなので、ポインタや文字列操作系の関数は随時説明しました。メモリ上の配置は前回前々回と見ていたのでサクッと説明したわりにはわかってもらえたんじゃないかなあ。

扱ったEmacsのバージョンは2.61です。GNUからダウンロードできます。main関数が入っているのはsrc/emacs.cです。

int main(int argc, char **argv) {
  ...

  sort_args(argc, argv);
  argc = 0;
  while (argv[argc])
    argc++;

  // コマンドライン引数に -version, --version が含まれているかチェック
  if (argmatch(argv, argc, "-version", "--version", 3, NULL, &skip_args)) {
    const char *version, *copyright;
    ...
    printf("%s %s\n", PACKAGE_NAME, version);
  
  ...

もうちょっと上のほうにさかのぼってみるとargmatch関数の実装があります。skipptrの次の引数をsstr,lstrと比較して, 値を指定するオプション(–chdir)であれば値となる文字列のアドレスをvalptrに入れています。下のほうにある三項演算子がif-else文と同じ意味を持つと解説したところ、すごく読みづらそうにしていました。その気持ちわかるなあ。

static bool argmatch(char **argv, int argc, const char *sstr, const char *lstr,
                     int minlen, char **valptr, int *skipptr) {
  char *p = NULL;
  ptrdiff_t arglen;
  char *arg;

  // argv[argc]のようなアクセスが起こらないようにする
  if (argc <= *skipptr + 1)
    return 0;

  arg = argv[*skipptr + 1];
  if (arg == NULL)
    return 0;
  if (strcmp(arg, sstr) == 0) {
    if (valptr != NULL) {
      *valptr = argv[*skipptr + 2];
      *skipptr += 2;
    } else
      *skipptr += 1;
    return 1;
  }
  arglen = (valptr != NULL && (p = strchr(arg, '=')) != NULL ? p - arg
                                                             : strlen(arg));

ポインタをふんだんに使ったソースコードを眺めながら「このわからなさが楽しい」というようなことを@pittyi2ndが言っていて、「あーそうだよな、そういう感覚忘れてたよなあ」と思わされました。かといって難しすぎるとやる気失くすんだけど。

Slackでも随時クイズ形式でソースコードを投げてみようかなあ。次回はちゃんとCTFしたいですね。CTF班じゃなくて低レイヤ班でもすればよかったか。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください