0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ShellCraftでアセンブリを読む

Last updated at Posted at 2024-04-25

ShellCraftとは?

原義の「シェルコード」を指します。
悪いコードというイミでなく、シェルを呼び出す関数です。

ShellCraftが実行するコード

shellcraft.asm
   /* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    mov ebx, esp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00\x00' */
    push 0x1010101
    xor dword ptr [esp], 0x1016972
    xor ecx, ecx
    push ecx /* null terminate */
    push 4
    pop ecx
    add ecx, esp
    push ecx /* 'sh\x00' */
    mov ecx, esp
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

要約すると、

/* execve(path='/bin///sh', argv=['sh'], envp=0) */
execveは、システムコールです。OSが用意してる関数みたいなもんです。
これを召喚すればシェルやスクリプトを実行してくれるわけです。
【引数】
第一引数 : 実行したいファイルのパス(実行バイナリやスクリプト)
第二引数 : プログラムに渡す配列(NULLポインタが終端)。ls -lとか
第三引数 : プログラムに渡す環境変数(NULLポインタが終端)

目標として、

eax(ebx, ecx, edx) の形式になることを確認できれば👌
↑ は、
execve("/bin///sh", "sh", 0) と同じイミ

それぞれのレジスタ(e[a-d]x)に文字とかを格納できれば👌

スタックを追う

スタックつうのはprint("HelloWorld!!")するならこんな感じになります。
引数→リターンアドレスの順に、先端へ積んでいくわけです。ジェンガと同じで、先端から取り出します。底の方から取り出すことはできません。

image.png

アセンブリで簡易に表すと ↓

HelloWorld!!.asm
push 00!!
push dlroW
push olleH
call print

push : スタックに積め、という命令
pop : スタックから取り出せ、という命令。pushの逆の操作
00 : 終端文字。コンピュータが文字列命令文を区別するために文字列の終端に供えます。16進数「\x00」はASCIIで「NULL」を指します。


では、セクションごとに見ていきましょう。

①/* push '/bin///sh\x00' */

第一引数として、「/bin///sh」という文字列をスタックに追加すると言っています。

/* push '/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp

スタックは4バイト度()にアドレスが割り振られており、0x68(1バイト)は残りの3バイト分を000000で埋めます。これが終端文字NULLに該当します。
0x00000068 : nullnullnullh
0x732f2f2f : s///
0x6e69622f : nib/

1バイト区切りで逆さから読めば「/bin///sh」という文字列の完成です。ASCII表で16進数アルファベットの対応を確認できます。例えば「2f」は「/」に対応しています。

このパスのシェルを呼び出すようですね。

現在のスタックはこのようになっています。👌
image.png

mov ebx, espは、ebxというレジスタにespのアドレスを格納する命令です。「esp」というのは常にスタックの先端アドレスを指すポインタです。
今、このアドレスには「nib/」が格納されており、ここから逆さに文字を辿っていくと、「/bin///sh」と第一引数が完成します🎉

②/* push argument array ['sh\x00'] */

第二引数として、「sh」という文字列をスタックに追加すると言っています。

/* push argument array ['sh\x00'] */
/* push 'sh\x00\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016972
xor ecx, ecx
push ecx /* null terminate */
push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx

ただ先ほどと違って命令が多いです。順を追って見ましょう。
push 0x1010101
xor dword ptr [esp], 0x1016972

0x1010101をpushしてみました。
image.png

次の命令は、esp0x1016972xorをとり、その計算結果をespに代入するようです。
espには0x1010101が格納されているので

esp = 0x1010101 xor 0x1016972

という命令になります。
0x6873という値が得られるはずです。ASCIIでいう「sh」に対応します。

espに「sh」を代入すると、
image.png
となります。👌

dword ptrは、espを4バイトとして扱います。esp = 0x1010101は7バイト(7桁)なので0x01010101という風に8バイトで表現します。計算結果は変わりません。

ちょっとした疑問と予想

そもそもespはポインタであり、「データのアドレス」を指しています。しかし、今回のxor演算では「データそのもの(0x1010101)」を表していました。これは、dwordを宣言することでespを「アドレスとして」でなく、「ポインタが指すデータそのものとして」扱わせているのかも🤔


😎

😎

😎

xor ecx, ecx
push ecx /* null terminate */

同じ値でxorをとると値は必ず0になります。ecxには0が代入され、
それをpushすると
image.png
となります。👌
この0が第二引数「sh」の終端文字NULLだそうですね。


😎

😎

😎

push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx

push 4
image.png
しました👌

pop ecxpopした値をecxに格納)
image.png
しました👌

add ecx, esp
今、以下の値を足してecxに格納しようとしています。
ecx = 4
esp = 0を格納しているアドレス
すると、、
ecxには「espに4足したアドレス」が格納されます。
image.png

※アドレスを「足す」とに近づきます。先端に行くほどアドレスは小さくなります。4ずつ。正規分布みたく。
ecxには「sh」を指すアドレスが格納されました。

push ecx
image.png

mov ecx, esp
image.png

ecxには「shを表すアドレスのアドレス」。これが第二引数です。
今、esp + 4の位置にある0はおそらく、shのアドレスの終端文字だと考えられます。

最後に、xor edx, edxedx = 0になります。これが第三引数です。

引数はすべて完了しました。👌

🍵

🍵

🍵

/* call execve() */

ようやくシステムコールの処理です。
このシステムコールがさっき求めた引数を取ります。

/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80

push SYS_executepop eax
image.png
スタックはこうなります。👌

int 0x80は、システムコールの呼び出し(重言)命令です。

整理

eax = SYS_execute
ebx = /bin///sh
ecx = sh
edx = 0
eax(ebx, ecx, edx)

execve("bin///sh", "sh", 0) の完成です。

最後に

間違った情報があれば、教えていただきたいです。

今のところ、
"ecxshのアドレスが格納されてもなおecxespを代入して間接的なポインタを作るのはエレガント性に欠けるな~"
くらいです。
これ以外の一連は納得しているつもりです。

「解析」にしては手ごろなサイズだったので、楽しいまま解析し終えました。次はシステムコールがどのように引数を取り、どのような一連を経て実行されるのかが知りたいです。

なにか、疑問点・ここってこうじゃね?的な意見があれば是非コメントお願いします。理解の矯正につながると思います。

51c9670f00071a021114094cc8afa5733d4b6c9f5cee2f71f987c60bd573ffb0

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?