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とうつ