徒然ネル

LinuxカーネルとかeBPFとか

HACKING: 美しき策謀勉強メモ #1

0x200のプログラミングの基礎は手を動かさず読むだけにした。

0x300のプログラムの脆弱性攻撃から実際に動かしていく。

 

overflow_example.cについて見ていく。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv) {
    int value = 5;
    char buffer_one[8], buffer_two[8];

    strcpy(buffer_one, "one"); /* "one"をbuffer_oneに設定 */
    strcpy(buffer_two, "two"); /* "two"をbuffer_twoに設定 */

    printf("[前] buffer_two は %p にあり、その値は \'%s\' です\n", buffer_two, buffer_two);
    printf("[前] buffer_one は %p にあり、その値は \'%s\' です\n", buffer_one, buffer_one);
    printf("[前] value%p にあり、その値は %d (0x%08x) です\n", &value, value, value);

    printf("\n[STRCPY] %ld バイトを buffer_two にコピーします\n\n", strlen(argv[1]));
    strcpy(buffer_two, argv[1]); /* 最初の引数をbuffer_twoにコピーする */

    printf("[後] buffer_two は %p にあり、その値は \'%s\' です\n", buffer_two, buffer_two);
    printf("[後] buffer_one は %p にあり、その値は \'%s\' です\n", buffer_one, buffer_one);
    printf("[後] value%p にあり、その値は %d (0x%08x) です\n", &value, value, value);    
}
 
-gオプションをつけてコンパイルgdb起動、20行目にブレイク貼って、run
$ gcc -g -o overflow_example overflow_example.c 
$ gdb -q ./overflow_example a
(gdb) b 20
(gdb) run
 
ASLRの有効、無効でアドレス配置が変わるのか気になったので試す。
 
何もせず(ASLR無効)アドレス配置を表示
 

ASLRを有効にしてからアドレス配置を表示
(gdb) set disable-randomization off

[heap], [vvar], [vdso], [stack]の配置のされ方が違うな
vvar, vdsoが何か知らんので調べた

dump[vvar]セグメントは、Linuxユーザー空間プログラムにおいて、関数呼び出しにまたがって保存する必要のあるデータを格納するために使用されるメモリ領域である。このセグメントは通常、読み取り専用メモリ(ROM)領域、またはフラッシュ・メモリやEEPROMなどの不揮発性メモリ(NVM)デバイスマッピングされる。

Understanding Dump[vvar]Segment in Linux User Space Programs (devcodef1.com)より

 vDSO (virtual dynamic shared object) は、注意深く選択された kernel space ルーチン群を、user space 上のアプリケーションにエクスポートするための、カーネルカニズムである。これにより、アプリケーションは、それらカーネル空間ルーチンを、システムコール インタフェースを使用して、それらの同じカーネル空間ルーチンを呼び出す時に固有の、user mode から カーネルモード への コンテキストスイッチ によるパフォーマンス低下なしに、プロセス内で呼び出せる。[1][2]
vvarが関数呼び出しに使うデータの保存領域で、vdsoがシステムコール呼び出しのための何か
 
heapやstackの中身の配置のされ方は同じなんかな?
と思って、実行結果を見たんだけど、

配置のされ方は同じみたい
ただ、書籍で書かれてる配置のされ方と違った。
書籍ではbuffer_two, buffer_one, valueの順にアドレスが大きくなっていたんだけど、手元の環境は逆になってる。
考えられる理由としては、
・スタックのアドレスの進む方向(番地が大きい方に進む or 小さい方に進む)はCPUによってことなる
・異なる関数の変数のプッシュ順はOSによって異なる 
・同じ関数内の変数のプッシュ順はコンパイラによって異なる 
 
以降、ASLR有効、無効はスタックオーバーフローに関係しなさそうなのでASLR無効の方を見ていく(スタックオーバーフローを利用して任意のコードを実行しようとする際はASLR関係しそうだけど)
 
8バイト突っ込んだときのアドレスが以下で、

9バイト突っ込んだときが以下、

スタックのアドレスが全部-16されてる
9バイト突っ込んだ方は、オーバーフローを検知されて異常終了してるけど、領域的にはスタック領域内だし他のスタックを書き換えたりはしてなさそう
 
さらに、バイト数を大きくすると、スタックのアドレスが小さい方にシフトして、結局オーバーフローを検知して異常終了

OSかCPUで、オーバーフローで任意のコードを実行できないように、スタックのアドレスを動的に格納するみたいやな
 
書籍にあったこっちも動かしてみたけど、書籍通りパスワード間違っててもオーバーフローでauth_flag書き換えはできんやった
password_bufferのほうが大きいアドレスに配置されてオーバーフローしてもauth_flagを書き換えられん
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
   int auth_flag = 0;
   char password_buffer[16];

   strcpy(password_buffer, password);

   if(strcmp(password_buffer, "brillig") == 0)
      auth_flag = 1;
   if(strcmp(password_buffer, "outgrabe") == 0)
      auth_flag = 1;

   return auth_flag;
}

int main(int argc, char *argv) {
   if(argc < 2) {
      printf("使用方法: %s <パスワード>\n", argv[0]);
      exit(0);
   }
   if(check_authentication(argv[1])) {
      printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
      printf("   アクセスを許可します。\n");
      printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
   } else {
      printf("\nアクセスを拒否しました。\n");
   }
}
 
 
うーん、書籍に書いてあることと現実が違うと読み進める気失せるな…
この本の続きやるかどうかは、また考えよう。

面白そうな記事を見つけたので機械学習環境を整備

気になる記事を見つけたので、winデスクトップに機械学習環境を用意してみた

zenn.dev

 

一点、記事ではUbuntu24.04を入れていたけどaptのレポジトリとかでエラー踏みそうなので、Ubuntu-22.04にした

 

Dockerを常時起動状態にするとこまでやった

メモリサイズやディスクサイズの増加はやってない

itmediaの「main()関数の前には何があるのか」を読んでみる(第四回)

itmediaの「main()関数の前には何があるのか」の連載を読んでわからなかったことや、調べたことをメモする

 

第四回は" OSのシステムコールの呼び出しとは&バイナリエディタの使い方"

 

いきなり前回の記事の途中から始まってて、どうやったら逆アセンブルのこの行にたどりつくのか忘れてた。

以下、思い出しながら実行したら、第四回の頭のところに行けた

  • ```bash
  • gdb hello
  • layout asm
  • break write
  • ```

 

先頭からアセンブリ追ってみたいな

図2.30: write()の内部

https://image.itmedia.co.jp/l/im/ait/articles/1703/01/l_r20_Hello04-01.PNG

 

>__write_nocancel()が__kernel_vsyscall()を呼んでるみたいだけど、それぞれが何なのか?

__write_nocancel()はwriteシステムコールを呼ぶAPI内の関数っぽい

nocancel()はスレッドキャンセルポイントでない関数ということらしい

noncancelの前のwriteはキャンセルポイントということかな

pthread_cancelって関数でキャンセルできるみたい

pthreadがPOSIXスレッドっていうPOSIX標準のスレッドとのこと(?)

ここらへんは、マルチスレッドプログラミングを勉強する必要がありそう

(gdb) where
#0  0x00110416 in __kernel_vsyscall ()
#1  0x08053d92 in __write_nocancel ()
#2  0x08067671 in _IO_new_file_write ()
#3  0x0806819b in _IO_new_do_write ()
#4  0x080683ea in _IO_new_file_overflow ()
#5  0x080673f4 in _IO_new_file_xsputn ()
#6  0x08059738 in vfprintf ()
#7  0x08049381 in printf ()
#8  0x080482e2 in main (argc=1, argv=0xbffffc14) at hello.c:5
(gdb)

 

hexeditの/使った検索は、全文検索じゃなくてカーソル以降の検索なんやね

検索したい箇所通り過ぎた状態で検索してもnot foundって言われて謎だった

 

0x80d6750はどこだ?

objdump -d hello | less で検索してもこのアドレスは入ってなかった。

ちなみに、0x110414<__kernel_vsyscall>もどこなのか気になる

helloが確保するメモリの範囲外な気がしてる

 

 

itmediaの「main()関数の前には何があるのか」を読んでみる(第三回)

itmediaの「main()関数の前には何があるのか」の連載を読んでわからなかったことや、調べたことをメモする

(作者サポートページ)

 

第三回は"試行錯誤のデバッグで探る、printf()内のポインタ経由での関数呼び出しが行き着く先とは"

 

break mainでrunしたあと、breakしてるのが↓の行なのよくわからんな

breakが貼られる箇所ってどういう規則になってるんだろう?

図2.8: layout asmの状態でブレークする

https://image.itmedia.co.jp/ait/articles/1703/01/r20_Hello03-01.PNG

 

gdbについて調べると使い方ばかり出てきて、原理がわからんな

 

break function だとfunctionのエントリーポイントでブレイクするらしい

ということは、スタックポインタやベースポインタ更新後が関数のエントリーポイントってこと?kprobeやuprobeも同じタイミングなのかな?

break function
function のエントリにブレークポイントを設定します。

https://flex.phys.tohoku.ac.jp/texi/gdb-j/gdb-j_18.html#:~:text=break%20function,%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

vfprintfにブレイク貼ったら、今度もsub命令でスタック確保したあとで止まった
スタック確保後がエントリーポイントなのかな
図2.15: vfprintf()でブレークする

https://image.itmedia.co.jp/l/im/ait/articles/1703/01/l_r20_Hello03-08.PNG

 

アセンブラで深いとこ潜っていくのは、break貼って勧めて関数呼び出し見つけてstepiで呼び出し先の関数に飛んではbreak貼ってを繰り返していくみたい。

 

itmediaの「main()関数の前には何があるのか」を読んでみる(第二回)

itmediaの「main()関数の前には何があるのか」の連載を読んでわからなかったことや、調べたことをメモする

(作者サポートページ)

 

第二回は"「Hello World!」の主役printf()の内部動作をデバッガGDBで追う"

 

gdbserverを使う話が出てきた

Remote Debugging using GDB - CodeProject

https://www.codeproject.com/KB/mcpp/remote_debugging/3.jpg

 

 

printfの逆アセンブル結果のニーモニックleaがわからなかった

movはラベルを指定すると、ラベルの先にある中身をコピーする。

leaは、アドレスの先の中身ではなく、アドレスそのものをコピーする。

https://3iz.jp/try-assembly.html#:~:text=mov%E3%81%AF%E3%83%A9%E3%83%99%E3%83%AB%E3%82%92,%E3%81%9D%E3%81%AE%E3%82%82%E3%81%AE%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%99%E3%82%8B%E3%80%82

movは中身の値、leaはアドレスをコピーするみたい

 

ニーモニックleaveも知らないな

調べてみると、

leave命令は、以下の2つの命令を組み合わせたのと同等の処理をします。

mov esp, ebp
pop ebp

https://vanya.jp.net/os/x86call/#enterleave:~:text=leave%E5%91%BD%E4%BB%A4%E3%81%AF,ebp%0Apop%20ebp

とのこと。

retの前に必要な処理みたい

retはスタックに積まれてるアドレスから呼び出しもとに戻る命令

上でも参照した以下のサイトが関数呼び出しについてわかりやすかった

x86アセンブリ言語での関数コール (vanya.jp.net)

 

 

itmediaの「main()関数の前には何があるのか」を読んでみる(第一回)

itmediaの「main()関数の前には何があるのか」の連載を読んでわからなかったことや、調べたことをメモする

 

第一回は"「Hello World!」の中身を探る意義と環境構築、main(C言語)のアセンブラコードの読み方"

 

4ページ目の

「*(int *)((char *)EBP+0xc)」

が、なんでcharでキャストしてるのかわからん

→1 byteずつ明示的にアドレスを加算するためだと思われる(追記)

 

4ページ目のスタックの扱いから難しい

and命令ではスタックポインタに0xFFFFFFF0を論理積でかけあわせることで、スタックポインタを16バイト境界にそろえているようだ。

ようわからんなーと思ったけど、具体的なアドレスで考えるとわかった。

16進数で下一桁を0にすることで、16バイト境界に合わせてスタックのアドレスを下の方に移動(ゼロアドレスに向かって移動)させてるのね。

 

自動変数がなにか知らんかったけど、main関数内のローカル変数のことらしい。

 

push命令周りの話は以下の記事の解説がわかりやすそう

第六話:EBPとESP、スタック領域の使われ方|トリコロールな猫 (note.com)

 

スタックの絵を書いて、アドレスがどこを指してるのか追っていかないとわからんな。

 

あと、x86アーキテクチャレジスタ名について参照しながらじゃないとわからんくなる。

x86レジスタ 備忘録 #CTF - Qiitaより

 

関数からのリターンで、

 x86アーキテクチャでは、関数からの戻り値はEAXレジスタで返される。つまりこれはmain()関数の終端にある「return 0」の戻り値を準備していることになる。

と書いてあるけど、関数からの戻り値が0以外の場合どうなるんだ?

 

関数の先頭は16バイトや32バイトなどのキャッシュラインにアラインメントされていたほうがキャッシュ効率が良くなり、高速性が高まる。

これもわかんねー

キャッシュラインにアラインメントがわからん。

 

 

次、読み返す際に、やること。

 

 

2023年度振り返って(私生活)

2023年度ももう終わり

ということで2023年度の振り返りを残しときます。

 

書いてたら長くなっちゃったから、私生活と仕事で分けます。

それでは、私生活の振り返り~

 

4月

東京に引っ越して、新生活の準備が大変でしたね。

新生活需要で洗濯機の配送に2週間以上かかるなんで知らなくて、しばらくコインランドリーに通うことになりましたね。大変でした。

週末は大学の上京組とよく遊んびましたね。

 

5月

GWに帰省して、奥さんご両親へのご挨拶、うちの家族との食事会をしました。

どうなることやらと、ビビってたけど無事に歓談できました。

あと、職場にカナダから海外インターンの学生が来て、週末に東京案内しましたね。

 

6月

新生活落ち着いたころだったかな?

週末は海外インターン生をつれて遊び回ってました。

横浜のカップヌードルミュージアムに行ったり、温泉につれてったり、プロ野球の試合につれてったり。

海外インターン生の希望で、柔道教室に通い始めたのもこの頃だっけか

 

7月

FLOWのライブに行きましたね。ロックバンドのアニソンいいですね。

2024年もFLOWのライブに行きます。(しかもアニソンロックフェスとかいう激アツイベ)

海外インターン生と一泊二日で日本で二番目に高い北岳に登りました。

きつすぎて死ぬかと思った。

 

8月

お盆前から帰省して帰省中はリモートで働いて、東京帰ってきてから休暇取りました。

帰省中に海外インターン生が福岡に遊びに来たりもしました。

お盆明けは奥さんも東京に来てて、一緒にミラコスタに泊まったりしました。

ミラコスタでプロポーズもしました。

8月末に海外インターン生がカナダに帰っちゃいました。

一緒にあっちこっち遊びに行ったから結構寂しかったな。

 

9月

会社の同期と遊びに行くことが多かったな。

 

10月

人生初の人間ドックを受けました。もうしばらくは胃カメラやりたくない。

 

11月

祝日があったので、2回ほど福岡に帰りました。

結婚指輪を買いに行きました。

 

12月

結婚後の新居探しに行きました。希望通りの物件を探すって難しいね。

そこそこ希望に沿った物件を見つけられたので良かった。

あと、入籍しました。

年末、福岡に帰省して、奥さん家族と一泊二日の旅行に行ってきました。

緊張したけど楽しかったな。

 

1月

両家顔合わせを実施。

自分たちよりも、両家の両親のほうが緊張してぎこちなかった。

東京に戻ってすぐ、新居に引っ越しした。引っ越しマジ大変。

 

2月

私の両親と弟と釜山旅行に行ってきました。

今後、奥さんと暮らし始めたら、親と旅行もなかなか行かないかなと思って行ってきました。(奥さんは仕事の都合で福岡にいたので、まだひとり暮らし中)

 

3月

月末に奥さんが福岡から東京に引っ越してきました。

もうちょっと新居の収納を準備してあげれてたら良かったな。

申し訳ない。

 

スケジュール帳を見ながらバタバタと書いたので、また後で見返して書き直したり追加したりしたいな。