FreeBSDのUptimeが偽装できないかを検討する

初めに(やりたいこと)

uptimeが3000daysを超えるように見せたい

  kanai@:/home/kanai % uptime
  10:35PM  up 9 mins, 3 users, load averages: 0.40, 0.84, 0.54
           ^^^^^^^^^ココ!

調査

uptimeコマンドはwコマンドのhard-linkとわかります。

  kanai@:/usr/src/usr.bin % ls -1i /usr/bin/w /usr/bin/uptime
  1134480 /usr/bin/uptime
  1134480 /usr/bin/w

そのため、追うべきコードは、

 /usr/src/usr.bin/w

になります。

あたりをつけたいので、kdumpします。

 kanai@:/home/kanai % ktrace w
 kanai@:/home/kanai % kdump | grep time | grep CALL
 89994 w        CALL  gettimeofday(0xbfbfdbb4,0)
 89994 w        CALL  clock_gettime(0xd,0xbfbfdbd8)
 89994 w        CALL  clock_gettime(0x4,0xbfbfdc90)
 89994 w        CALL  clock_gettime(0,0xbfbfbeec)
 89994 w        CALL  clock_gettime(0,0xbfbfbeec)
 89994 w        CALL  clock_gettime(0,0xbfbfbeec)

clock_gettimeがあやしいです。第一引数に違和感があります。manをひきます。

  int clock_getres(clockid_t clock_id, struct timespec *tp);

ということで/usr/include/time.hを見ます。

  #define CLOCK_MONOTONIC 4
  CLOCK_MONOTONIC which increments in SI seconds
  #define CLOCK_SECOND    13
  CLOCK_SECOND which returns the current second without performing a full time counter query

CLOCK_MONOTONICというのがよくわかりません。あきらめて、/usr/src/usr.bin/w/w.cを見ます。

        /*
         * Print how long system has been up.
         */
        if (clock_gettime(CLOCK_MONOTONIC, &tp) != -1) {
                uptime = tp.tv_sec;

やはり、合っていたみたいです。では、これを書き換えればいいよね、ということで、

.. literalinclude:: Src/clock_settime.c :language: c :linenos:

すると、

::

kanai@:/home/kanai % ./a.out 2258 2258

CLOCK_MONOTONICは書き込み不可属性に見えます。/usr/src/sys/kern/kern_time.cを追います。

::

kern_clock_settime()より if (clock_id != CLOCK_REALTIME) return (EINVAL);

なるほど。clock_idがCLOCK_REALTIME == 0 の場合はsettimeできないようです。 では、CLOCK_MONOTONICは何を追っているのでしょうか?

::

kern_clock_gettime()より case CLOCK_REALTIME: /* Default to precise. / case CLOCK_REALTIME_PRECISE: nanotime(ats); break; case CLOCK_MONOTONIC: / Default to precise. */ case CLOCK_MONOTONIC_PRECISE: case CLOCK_UPTIME: case CLOCK_UPTIME_PRECISE: nanouptime(ats); break;

nanouptime()を見ます。/usr/src/sys/kern/kern_tc.cにありました。

::

/*

  • Functions for reading the time. We have to loop until we are sure that

  • the timehands that we operated on was not updated under our feet. See

  • the comment in <sys/time.h> for a description of these 12 functions. */ binuptime(struct bintime *bt) { struct timehands *th; u_int gen;

      do {
              th = timehands;
              gen = th->th_generation;
              *bt = th->th_offset;
              bintime_addx(bt, th->th_scale * tc_delta(th));
      } while (gen == 0 || gen != th->th_generation);
    

} void nanouptime(struct timespec *tsp) { struct bintime bt;

     binuptime(&bt);
     bintime2timespec(&bt, tsp);

}

bintime_addxはsys/time.hで

::

static __inline void bintime_addx(struct bintime *bt, uint64_t x) { uint64_t u;

    u = bt->frac;
    bt->frac += x;
    if (u > bt->frac)
            bt->sec++;

}

とのことです。さて、よく見ると、th = timehandsが気になるので、上の定義をみると、

::

static struct timehands *volatile timehands = &th0; static struct timehands th0; static struct timehands th0 = { &dummy_timecounter, 0, (uint64_t)-1 / 1000000, 0, {1, 0}, {0, 0}, {0, 0}, 1, &th1 };

となっているので、どうも、th0がtimehandsでuptimecounterっぽいことがわかります。 もうちょっと追います。

::

static uint64_t tc_cpu_ticks(void) { static uint64_t base; static unsigned last; unsigned u; struct timecounter *tc;

    tc = timehands->th_counter;
    u = tc->tc_get_timecount(tc) & tc->tc_counter_mask;
    if (u < last)
            base += (uint64_t)tc->tc_counter_mask + 1;
    last = u;
    return (u + base);

}

これでカウントして、tc_windup()で処理しているっぽいのですが、かなりまじめに追わないと行けなそうです。 他の手がないか確認します。tc_init()を見ていると、SYSCTL_STATIC_CHILDRENがあります。なるほど。 その手があったかsysctl。それっぽいのがあるか見ます。

::

kanai@:/ % sysctl -a | grep time | head -1 kern.boottime: { sec = 1366723591, usec = 385546 } Tue Apr 23 22:26:31 2013

これに書き込めれば勝ち!なのですが、

::

static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, CTLTYPE_STRUCT|CTLFLAG_RD, NULL, 0, sysctl_kern_boottime, “S,timeval”, “System boottime”);

で、CTLFLAG_RWは立っていませんでした。

中間まとめ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

uptimeはkern_tc.cのtimehands構造体がキーポイントとなるが、 それに対する直接アクセスのsyscallはない また、cpuが直接インクリメントしている構造体なので、かなりまじめに読まないと読めない。 ここまで来るとつらくなってくるので、kernelをdebugモードでコンパイルし直して、 gdbでattachしながら試行錯誤することにします。

kgdb ^^^^^^

対象のホストでkernelを再構築します。このとき、KDBをオプションで指定します。

::

cd /usr/src/sys/i386/ sudo cp GENERIC HOGETAN sudo vi HOGETAN kanai@:/usr/src/sys/i386/conf % diff GENERIC HOGETAN 24c24 < ident GENERIC

ident HOGETAN 67a68,69 options GDB options DDB sudo config -g HOGETAN cd ../compile/HOGETAN make cleandepend && make depend sudo make sudo make install reboot

boot -d します。

で、ここで、VirtualBoxを以下のように仕込みます。

ターゲット対象のFreeBSD(Kernelを再コンパイルしたやつ)

::

設定 > システム > マザーボード > IO APICを有効化をチェック 設定 > シリアルポート ポート番号:Com1 ポートモード:ホストにパイプ パイプ作成:OFF ポート/ファイルパス:\.\pipe\com_1

適当なFreeBSD

::

設定 > システム > マザーボード > IO APICを有効化をチェック 設定 > シリアルポート ポート番号:Com1 ポートモード:ホストにパイプ パイプ作成:ON ポート/ファイルパス:\.\pipe\com_1

0x80にする gdbとうつ