最近あまりネタがなくて放置気味でした。毎日少しずつ勉強しているのですが、いろいろなジャンルをつまみ食いしているような関係で、1つのネタで1つの記事を書くのが厳しく、更新できていませんでした。
なので、学んだことをまとめる小ネタ集みたいな記事を今後更新していこうかなと思います!
一応今大きなテーマで勉強していることが1つあるのですが、記事にするまで結構時間がかかりそう。
CTFをずっと練習していました
CTF(Capture The Flag)とは、セキュリティ系のコンテストです。さまざまなクイズが出題され、フラグと呼ばれる特定の文字列を探し出し、それを多くクリアできたチームの優勝になります。
競プロのような感じですが、競プロはプログラムを書くのに対し、CTFは与えられたコードや情報の中から、フラグを探し出すという点が違います。
最近、このCTFにハマっており、練習サイトを使ってさまざまな問題を解いていました。
苦手だったビットあたりの理解が少し捗った気がするので、クイズが好きでかつプログラミングも勉強したい人にはうってつけなのではないかと思います。
おすすめCTFサイト
- picoCTF – https://picoctf.org/
アメリカにあるカーネギーメロン大学が運営しているサイトなので、学生向け&英語になるのですが、大人が参加しても大丈夫なようです。
登録すると、300問くらい問題を解くことができて、解答もググればいろんな人の解答が見れます。
年1回、個人で参加可能なCTF大会も開催しているようなので、来年個人で参加してみたいなーと思ったり。
ここからは、CTFを解いていておさらいした点や新たに学び直した点などの小ネタ集になります。
小ネタ集
Linuxのディレクトリ構造
ルートディレクトリ以下にいろいろありますが、正直何がどこにあるのかわかっていない状況でした。重要なのは以下かな・・
ディレクトリ | 説明 |
/bin | コマンドが収納 |
/sbin | ルート権限用のコマンドが収納 |
/etc | 設定ファイル |
/home | 各ユーザのhomeディレクトリがある。/home/jiji/…といった感じ |
/lib | bin・sbinに入っているコマンドを実行するために必要なライブラリ群 |
/tmp | 一時ファイルなどの置き場所。再起動すると消えるので注意 |
/usr | ユーザ向けディレクトリで、ユーザがインストールしたプログラムなどはここに入ってくる |
/usr/local | ローカルで必要なアプリなどは全てここに入れることができる。自由に触ってもOK |
とりあえず、jijiユーザでいろんなコマンドをhomebrewでどんちゃか入れているけどそれらはほとんど/usr/local配下にインストールされているのかなーという理解です。
他のディレクトリはあまりいじらない方が良さそう?
XORの驚くべき規則
論理演算子の中にXORというのがありますが、こちらは以下のようなふるまいをします。
左項 | 右項 | 結果 |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
両方が等しい場合は0, 違う場合は1となるパターンです。こちらはある驚くべき特徴があります。
なんと左項XOR結果=右項となり右項XOR結果=左項となるのです・・!CTFの問題でこの特徴を使った問題が出たりするので、覚えておくと良いかも。
python3 コマンドを叩くと、コードが逐次実行される・・
今までJavaやC, C++を使ってきた身からすると、プログラムを作ったら、コンパイルしてビルドする必要がある認識でした。
ですが、pythonなんかは、コンパイルしてビルドする処理がないことになんでだろうともやっとしていました。
しかもおまけにCTFを勉強し始めてからpythonを使うようになったのですが、python3とコマンドをターミナルで叩くだけで、プログラムが1行ずつ実行されるので、とても驚きました。
実はpythonというのはinterpreter型の言語になるので、逐次コンパイルすることができるみたいです。逆にC, C++などはコンパイル型と呼ばれます。
長く複雑なコードには時間がかかるので、コンパイル型で処理したほうがよいみたいですが、簡単なコードだったらpythonで書いた方が断然よいですね。
個人的に、この数値を16進したらいくつになるんだろう・・というのがぱっと計算できるのがよいです。
base 64とは
64進数のことをいいます。メールなどにおいて、画像や音声のデータ送信時にbase64方式で変換することで、相手にこれらのデータを送ることができます。
base 64でエンコードされてる文字列が提供され、そこからフラグを発掘せよといった問題があるので、そういった場合はオンラインデコードツールなどを使うと良いです。
ビットで表現できる情報の話
ビット | 表現できる情報 |
1 | 2^1 = 2 |
2 | 2^2 = 4 |
3 | 2^3 = 8 |
4 | 2^4 = 16 |
5 | 2^5 = 32 |
10 | 2^10 = 1024 |
例えば、4つの情報(喜怒哀楽)を表現したければ、2bitあれば充分です。
喜 | 00 |
怒 | 01 |
哀 | 10 |
楽 | 11 |
というような感じですね。
他にも例えば、アルファベットは26文字あるので、5ビットで表現可能です。
他にも、16進数の1文字は、0~Fまでの16文字が使われるので、16進数の1文字を表現するには4ビットが必要になります。(0xF = 1111(2) = 15(10))
ASCII コード
ASCIIコードは英語や記号などを8bit = 1byteで表現する方式になります。つまり、ASCIIで表現できる文字は256個未満であるということが考えられます。(数えてみると127個だったので、残りは念の為のreservedとなっているのかな。)
ちなみにASCIIコード表を眺めていると、16進数で表現されていることが多いかと思いますが、この理由については、1byteを2進数表示すると、8bit(0000 0000)となり、とても長いですよね。
16進数は1文字あたり4bitで表現可能なので、こちらがとても短く表現できます。
例えば、小文字のjをASCIIコード表で表現すると、6A(16) = 0110 1010(2)となり16進数のほうが直感的にわかりやすいのかなと思います。
C言語コンパイル用環境変数
C言語のプログラムをコンパイルする必要があり、環境変数が必要になったのでその備忘録。
LD_LIBRARY_PATH | OSが使用する変数。 リンク時または実行時にリンクする共有ライブラリを探すために使用。 |
CPATH | コンパイル用のインクルード・ディレクトリーのパスを指定 |
LDFLAGS | リンク用のディレクトリを指定 |
CPPFLAGS | Cプリプロセッサのオプション |
PKG_CONFIG_PATH | pkg-config(システムにインストールされているライブラリに関する情報を取得するために使用)が.pcファイルを検索する追加のパスを指定する環境変数 |
ハッシュ値
ファイルから生成される値で、gitではコミットハッシュというものがあります。これはコミットから生成される一意の値で、16進数で表現されています。
暗号化において、パスワードからハッシュ値を生成して、それを管理するみたいなことがあるみたいなのですが、
攻撃者にハッシュを置き換えられたら完全に終わりなので、ハッシュの置き場所には注意しなくてはいけないようです。
gccとは、GNUとは、Unixとは・・・・
まず・・gccとは、コンパイラーです。C, C++などさまざまな言語に対応しているようです。こちらはGNU系コンパイラーといわれています。
次にGNUとは、Unix系のOSと関連するソフトウェア群を開発するプロジェクト名らしいです。
そしてUnixとは、OSです。いろいろと歴史があるようですが、LinuxはこのUnixをアレンジしたものになるようです。
ポインタ変数
C, C++を勉強しているとぶち当たるのがこれですね。これだけで1つ記事を書いてもいいような気がしますが、ちょっとまだ自信がないので、小ネタとして残しておきます。
ポインタ変数とは、メモリアドレスを管理する変数になります。
// *をつけることで、ポインタ変数として定義することができます。
int *a;
int x = 12345;
// &をつけることで、xのいる場所のメモリアドレスが参照され、代入されます。
a = &x;
// *をつけることで、そのメモリアドレスに格納されている値を表示します。
printf(*a) // 12345と表示される
// そのままだと格納されている値のメモリアドレスが表示されます。
printf(a) // 12345が格納されているメモリアドレス(例えば0x7ff7b617e984とか)
// &をつけることで、ポインタ変数のメモリアドレスが表示されます。
printf(&a) // ポインタ変数aのいる場所のメモリアドレス
// このような宣言方法もできる。初期値は、アドレスしか格納しないので、アドレスを入れている。
int *a = &x;
1[arr] = *(1+arr) ってなんでこれになるの・・?
arrayとポインタ変数の関係性も奥が深いですが、題にある式がなんで成り立つのかがわからないでいました。
以下はpicoCTFprimerというページに載っているC言語のポインタ説明からお借りしました。
//You might be wondering about the relation between arrays and pointers. Some people say in c, the use of [] is just syntactic sugar.
//But there are not actual arrays on C.
//In this expression it is created a pointer to the first element of the array. In fact, arr is pointer to the first element:
char arr[5]="hello";
//these expressions are the same:
printf("\n This is arr[0]: %c ", arr[0]);
printf("\n This is *arr: %c ", *(arr+0));
//as well as:
printf("\n This is arr[0]: %c ", arr[1]);
printf("\n This is *(arr+0): %c ", *(arr+1));
printf("\n This is arr[1]: %c ", arr[2]);
printf("\n This is *(arr+1): %c ", *(arr+2));
printf("\n This is arr[2]: %c ", arr[3]);
printf("\n This is *(arr+2): %c ", *(arr+3));
printf("\n This is arr[3]: %c ", arr[4]);
printf("\n This is *(arr+3): %c ", *(arr+4));
//understanding that, you can see now why in C, a thing that looks very weird as the following, makes sense:
printf("\n This is 1[arr]: %c ", 1[arr]);
//As you see, it printed 'e', because that expression is just *(1+a), which is the same as *(a+1)
//People says that proves that in C there are not actual arrays. What is our opinion? As long as you clearly
//understand how it works in the languages you are using
printf("\n SEE YOU! keep on the good work! \n ");
ここでは、文字列”hello”が入っているarray[5]を宣言して、それぞれをprintfで出力しています。arrayの形でも出力できるし、ポインタ変数を使っても出力できます。
21行目で1[arr]としても出力できるとありました。なぜこれが成り立つのかわからなかったです。
調べてみると、arr[1] = *(arr + 1)と表現できますよね。このを左項を1[arr]で置き換えてみると、1[arr] = *(1 + arr)になりますよね。
つまり、1[arr]でも配列の文字列にアクセスできるらしいです。ふーむ・・そうなんだ・・となりました。汗
GDBとは、GUIのないデバッガー
C言語やC++で動くCUIのデバッガーになります。こちらもGNU系で、一部アセンブラで表示されるので、ちょっと知識がないと使えなさそうだな。。。といった感じでした。
ただ、デバッガーは CUIのものも存在することに驚きました。
プロセッサー、レジスタ、IP
GDBを使っていると、このあたりの用語が出てきましたので残しておきます。
プロセッサー | CPU |
レジスタ | プロセッサーの中にあるRAMとは別のメモリ。演算などが行われる |
IP | 現在実行している場所を保存するレジスタ |
リトルエンディアンとビッグエンディアン
バイト列のオーダーに関する用語です。
リトルエンディアン | バイト列が逆順になっていること 例えば、0x4883c480 が 0x80c48348となっている コンピュータ内やバイナリファイルではリトルエンディアン形式になっていルことが多い |
ビッグエンディアン | 我々がよく目にするバイト列 |
ピンと来ないかもしれませんが、パソコン内のバイナリファイルを上書きするみたいなことがある場合は、リトルエンディアン方式に変換するのを忘れないようにしないといけないみたいですね。
アセンブラでよく見るレジスタの種類
こちらもpicoCTFprimeで解説されているものを引用した形になります。
General Registers
RAX,EAX,AX (Accumulator register) | 関数のreturn valueを保持するのに使用 |
REX,EBX,BX (Base register) | メモリアクセスする際のベース |
RDX,EDX,DX (Data register) | テンポラリデータを保存 |
index/poninter registers
今いるスタックの始点や終点をマークするのに使われる。関数から戻ってくる時にどこに戻るかなどを記録するため。
RSP,ESP,SP (Stack pointer register) | スタックの始点 |
RIP,EIP,IP (Instruction Pointer) | 今実行しているプログラムの位置 |
RBP,EBP,BP (Base pointer register) | 関数のスタックフレームの始点 |
RDI,EDI,DI (Destination index register) | メモリチャンクをコピーするためのレジストリ |
RSI,ESI,SI (Source index register) | Destination index registerと同じようなもの |
Fernetとは
pytho用の暗号化ライブラリ。
ローカルプロキシツールとは
burpsuiteなどが挙げられます。サーバとクライアント間の通信を視覚的に見ることができるツール。
HTTPメソッドを偽装して、フラグを表示するみたいな問題がありました。
HTTPメソッドとは
GET, POST, HEADなどがあり、GETは通常データをサーバへ取りに行く、POSTはデータをポストする役割を持つのが推奨されています。
HEADはGETとほぼ一緒ですが、データ丸々ではなく、一部を取ってくるので、ネットワークの帯域節約などによく使われるようです。
最大公約数と最小公倍数
これはもはや数学ですが、忘れちゃったので。
公開鍵暗号化方式(RSA暗号化方式)の仕組みを調べていたことがあって、これらが使われています。
最大公約数 | aとbを割り切れる数の最大のもの。2と3ならば1です。 |
最小公倍数 | aとbが互いに割り切れる数字の最小のもの。2と3ならば、6です。 |
ユークリッド互除法とは
最大公約数を求める式の一つ。
a>bであるとき、a=bq+rとある場合、aとbの最大公約数はqとrの最大公約数であるといえる。aをbでわり、そのときでた余りでbを割る・・というのを続けて、最後余りが0になったときのbの値が最大公約数といえる
まとめ
先月あたりから蓄えてきた小ネタ集です。どなたかの役に立てれば幸いです。
にほんブログ村に参加しております!よろしければクリックお願いします 🙂
にほんブログ村
コメント