picoCTF 2019: asm3

問題

問題文

What does asm3(0xd46c9935,0xdfe28722,0xb335450f) return? Submit the flag as a hexadecimal value (starting with '0x'). NOTE: Your submission for this question will NOT be in the normal flag format. Source located in the directory at /problems/asm3_2_376e88472c6a9317470a12cc31d506a4.

解答例

指針

  • 読むだけ

解説

x86アセンブリコードが与えられる.

asm3(0xd46c9935,0xdfe28722,0xb335450f) が返す値を16進表記したものを聞かれている.

去年の picoCTF2018 の assembly シリーズと全く同じ問題.

$ wget https://2019shell1.picoctf.com/static/7fa30288613be44a6a39c1191ccf1971/test.S
$ cat test.S
asm3:
        <+0>:   push   ebp
        <+1>:   mov    ebp,esp
        <+3>:   xor    eax,eax
        <+5>:   mov    ah,BYTE PTR [ebp+0xa]
        <+8>:   shl    ax,0x10
        <+12>:  sub    al,BYTE PTR [ebp+0xf]
        <+15>:  add    ah,BYTE PTR [ebp+0xe]
        <+18>:  xor    ax,WORD PTR [ebp+0x10]
        <+22>:  nop
        <+23>:  pop    ebp
        <+24>:  ret

ax は eax レジスタの下位16bit, ah は ax の上位8bit, al は ax の下位8bit. これを図示したのが以下の画像. (Public Domain の https://ja.wikibooks.org/wiki/ファイル:Diagram_of_register_EAX.svg を使わせてもらった.)

f:id:kira000:20191228192112p:plain

x86 はリトルエンディアン形式なので, asm3(0xd46c9935,0xdfe28722,0xb335450f) が呼ばれたとき, 引数は以下のように Call Stack に置かれる.

(lower address)

ebp + 0x00 | saved ebp   |
ebp + 0x04 | return addr |
ebp + 0x08 |  0x35 |
ebp + 0x09 |  0x99 |
ebp + 0x0a |  0x6c |
ebp + 0x0b |  0xd4 |
ebp + 0x0c |  0x22 |
ebp + 0x0d |  0x87 |
ebp + 0x0e |  0xe2 |
ebp + 0x0f |  0xdf |
ebp + 0x10 |  0x0f |
ebp + 0x11 |  0x45 |
ebp + 0x12 |  0x35 |
ebp + 0x13 |  0xb3 |

(higher address)

return されるのは eax レジスタの値なので, eax の値を追っていく.

以下の命令で eax の値は 0 となる.

<+3>:   xor    eax,eax

以下の命令で ah に 0x6c が代入される. このとき, eax の値は eax = 0x6c00 となる.

<+5>:   mov    ah,BYTE PTR [ebp+0xa]

以下の命令で, ax が 16bit 左シフトされる. したがって, ax = (ax << 16) & ((1<<16)-1) = 0 となる.

axを16bit 左シフトし, 下位16bit を取り出すと値は 0 となる. eax の上位16bit は 0 であったから, eax = 0 となる.

<+8>:   shl    ax,0x10

以下の命令により, al の値は -0xdf となる. 負数は2の補数で表現されるので, al は 0xdf の bit を反転して1を足した値となる. al = 0x21

$ python
>>> hex((0xdf^0xff)+1)
'0x21'
<+12>:  sub    al,BYTE PTR [ebp+0xf]

以下の命令で ah に 0xe2 を足して, ax = 0x21+0xe200 = 0xe221 となる.

<+15>:  add    ah,BYTE PTR [ebp+0xe]

最後に, ax と 0x450f の xor をとっているので答えは, 0xa72e となる.

>>> hex(0xe221 ^ 0x450f)
'0xa72e'
<+18>:  xor    ax,WORD PTR [ebp+0x10]

flag: 0xa72e

Python3 のコードで書くと, 以下のようになる.

eax = 0
eax += 0x6c00
eax = (0x6c00 << 16) & ((1<<16) - 1)
eax = ((0xdf^0xff)+1)
eax += 0xe200
eax ^= 0x450f

print(hex(eax))
  • 別解 (C言語から呼び出す)

以下のように, C言語からアセンブリ言語で定義された関数を呼ぶことができる.

$ sudo apt install libc6-dev-i386
$ cat test.S
.intel_syntax noprefix

.global asm3

asm3:
        push   ebp
        mov    ebp,esp
        xor    eax,eax
        mov    ah,BYTE PTR [ebp+0xa]
        shl    ax,0x10
        sub    al,BYTE PTR [ebp+0xf]
        add    ah,BYTE PTR [ebp+0xe]
        xor    ax,WORD PTR [ebp+0x10]
        nop
        pop    ebp
        ret

$ cat main.c
#include <stdio.h>

extern int asm3(int a, int b, int c);

int main() {
  int a = asm3(0xd46c9935,0xdfe28722,0xb335450f);
  printf("%x\n", a);
}
$ gcc -m32 main.c test.S
$ ./a.out
a72e

参考文献