picoCTF unpackmeを解いた

# 

どうも、CTF初心者です。
picoCTFのReversEngineering分野の難易度mediumのunpackmeを解いたので、備忘録として残します。

## 必要なもの
Linux(私はkali linuxを使いました)
Linuxの最低限のコマンド操作の知識


## 問題を解いてみよう
問題ファイルをダウンロードしたらfileコマンドでファイルフォーマットを確認する

```
$ ls
unpackme-upx

$ file unpackme-upx 
unpackme-upx: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, no section header
```

次にファイルを実行してどのような動きをするか確認する
```
$ chmod +x unpackme-upx

$ ./unpackme-upx 
What's my favorite number? 1
Sorry, that's not it!
```
私のお気に入りの数字は何でしょうとのこと。
適当に1を入力してみたが正解ではなかったみたい。

次はobjdumpコマンドで逆アセンブルしてコードを確認してみる。
と実行してみたはいいものの、次のようなメッセージが出てきて逆アセンブルされない。
```
$ objdump -D unpackme-upx                      

unpackme-upx:     file format elf64-x86-64
```

特にエラーメッセージなどが出てるわけではないので、objdumpの結果が表示されない理由がわからない。
問題のヒントが一つだけあるので確認すると、

What is UPX?

というヒントをもらった。

UPXについてgoogleで調べてみる。
wikipediaに次のように書いてあった。

UPX (ultimate packer for executables)は様々なOSのファイル形式に対応したFLOSSの実行ファイル圧縮ソフトウェアである。

なんか圧縮されているようなので解凍すればいいのかもしれない。

解凍方法を調べるとupxコマンドなるものがあり、そのコマンドで解凍できるみたい。

```
$ upx --help                
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.4       Markus Oberhumer, Laszlo Molnar & John Reiser    May 9th 2024

Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file..

Commands:
  -1     compress faster                   -9    compress better
  --best compress best (can be slow for big files)
  -d     decompress                        -l    list compressed file
  -t     test compressed file              -V    display version number
  -h     give this help                    -L    display software license
```
-dコマンドで解凍できるそうなので、解凍してみる。
ちなみに -o <FILE> で解凍したファイルを別ファイルとして保存できるみたいなので、
適当に「new」というファイル名で出力した。

```
$ upx -d unpackme-upx -o new
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.4       Markus Oberhumer, Laszlo Molnar & John Reiser    May 9th 2024

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   1006445 <-    379188   37.68%   linux/amd64   new

Unpacked 1 file.

$ ls
new  unpackme-upx
```

objdumpで逆アセンブルできるか試してみる。

```
objdump -D new

new:     file format elf64-x86-64


Disassembly of section .note.gnu.property:

0000000000400270 <__ehdr_start+0x270>:
  400270:	04 00                	add    $0x0,%al
  400272:	00 00                	add    %al,(%rax)
  400274:	10 00                	adc    %al,(%rax)
  400276:	00 00                	add    %al,(%rax)
  400278:	05 00 00 00 47       	add    $0x47000000,%eax
  40027d:	4e 55                	rex.WRX push %rbp
  40027f:	00 02                	add    %al,(%rdx)
  400281:	00 00                	add    %al,(%rax)
  400283:	c0 04 00 00          	rolb   $0x0,(%rax,%rax,1)
  400287:	00 03                	add    %al,(%rbx)
  400289:	00 00                	add    %al,(%rax)
  40028b:	00 00                	add    %al,(%rax)
  40028d:	00 00                	add    %al,(%rax)
	...

```
逆アセンブルできた。
しかし表示が多くて読むのが大変だと思った。
main関数を見てみたかったので、次のコマンドを使用した。

```
$ objdump -d new|grep "<main>:" -A 70
```

```
オプションの意味
objdump -d・・・ファイルを逆アセンブル
grep "<main>:" -A 70・・・<main>:が表示された行から70行下を表示する。
```
main関数の中身が表示された。
```
0000000000401e43 <main>:
  401e43:	f3 0f 1e fa          	endbr64
  401e47:	55                   	push   %rbp
  401e48:	48 89 e5             	mov    %rsp,%rbp
  401e4b:	48 83 ec 50          	sub    $0x50,%rsp
  401e4f:	89 7d bc             	mov    %edi,-0x44(%rbp)
  401e52:	48 89 75 b0          	mov    %rsi,-0x50(%rbp)
  401e56:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  401e5d:	00 00 
  401e5f:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  401e63:	31 c0                	xor    %eax,%eax
  401e65:	48 b8 41 3a 34 40 72 	movabs $0x4c75257240343a41,%rax
  401e6c:	25 75 4c 
  401e6f:	48 ba 46 41 6d 6b 30 	movabs $0x30623e306b6d4146,%rdx
  401e76:	3e 62 30 
  401e79:	48 89 45 d0          	mov    %rax,-0x30(%rbp)
  401e7d:	48 89 55 d8          	mov    %rdx,-0x28(%rbp)
  401e81:	48 b8 37 66 48 30 36 	movabs $0x5f60643630486637,%rax
  401e88:	64 60 5f 
  401e8b:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
  401e8f:	c7 45 e8 32 61 66 37 	movl   $0x37666132,-0x18(%rbp)
  401e96:	66 c7 45 ec 4e 00    	movw   $0x4e,-0x14(%rbp)
  401e9c:	48 8d 3d 61 11 0b 00 	lea    0xb1161(%rip),%rdi        # 4b3004 <_IO_stdin_used+0x4>
  401ea3:	b8 00 00 00 00       	mov    $0x0,%eax
  401ea8:	e8 f3 ec 00 00       	call   410ba0 <_IO_printf>
  401ead:	48 8d 45 c4          	lea    -0x3c(%rbp),%rax
  401eb1:	48 89 c6             	mov    %rax,%rsi
  401eb4:	48 8d 3d 65 11 0b 00 	lea    0xb1165(%rip),%rdi        # 4b3020 <_IO_stdin_used+0x20>
  401ebb:	b8 00 00 00 00       	mov    $0x0,%eax
  401ec0:	e8 6b ee 00 00       	call   410d30 <__isoc99_scanf>
  401ec5:	8b 45 c4             	mov    -0x3c(%rbp),%eax
  401ec8:	3d cb 83 0b 00       	cmp    $0xb83cb,%eax
  401ecd:	75 43                	jne    401f12 <main+0xcf>
  401ecf:	48 8d 45 d0          	lea    -0x30(%rbp),%rax
  401ed3:	48 89 c6             	mov    %rax,%rsi
  401ed6:	bf 00 00 00 00       	mov    $0x0,%edi
  401edb:	e8 a5 fe ff ff       	call   401d85 <rotate_encrypt>
  401ee0:	48 89 45 c8          	mov    %rax,-0x38(%rbp)
  401ee4:	48 8b 15 e5 d7 0d 00 	mov    0xdd7e5(%rip),%rdx        # 4df6d0 <stdout>
  401eeb:	48 8b 45 c8          	mov    -0x38(%rbp),%rax
  401eef:	48 89 d6             	mov    %rdx,%rsi
  401ef2:	48 89 c7             	mov    %rax,%rdi
  401ef5:	e8 86 ea 01 00       	call   420980 <_IO_fputs>
  401efa:	bf 0a 00 00 00       	mov    $0xa,%edi
  401eff:	e8 1c ef 01 00       	call   420e20 <putchar>
  401f04:	48 8b 45 c8          	mov    -0x38(%rbp),%rax
  401f08:	48 89 c7             	mov    %rax,%rdi
  401f0b:	e8 60 cd 02 00       	call   42ec70 <__free>
  401f10:	eb 0c                	jmp    401f1e <main+0xdb>
  401f12:	48 8d 3d 0a 11 0b 00 	lea    0xb110a(%rip),%rdi        # 4b3023 <_IO_stdin_used+0x23>
  401f19:	e8 22 ed 01 00       	call   420c40 <_IO_puts>
  401f1e:	b8 00 00 00 00       	mov    $0x0,%eax
  401f23:	48 8b 4d f8          	mov    -0x8(%rbp),%rcx
  401f27:	64 48 33 0c 25 28 00 	xor    %fs:0x28,%rcx
  401f2e:	00 00 
  401f30:	74 05                	je     401f37 <main+0xf4>
  401f32:	e8 69 ac 05 00       	call   45cba0 <__stack_chk_fail>
  401f37:	c9                   	leave
  401f38:	c3                   	ret
  401f39:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
```

上から読んでいくと気になる一行があった。

```
401ec8:	3d cb 83 0b 00       	cmp    $0xb83cb,%eax
```
これはeaxレジスタと0xb83cbを比較している処理である。

ファイルを実行した際の入力に0xb83cbを入力すればフラグが出力される可能性がある。
ためしに入力してみる。

```
$ ./new         
What's my favorite number? 0xb83cb
Sorry, that's not it!
```
16進数をそのまま入力してもダメだったので、10進数に直してから入力してみる。

```
$ python3
Python 3.13.2 (main, Feb  5 2025, 01:23:35) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import decimal
>>> decimal.Decimal(0xb83cb)
Decimal('754635')
```
```
$ ./new
What's my favorite number? 754635
picoCTF{up><_m3_f7w_e510a27f}
```

フラグが表示された。

## GDBを使用して解く
GDBでupxコマンドで解凍したファイルをデバッグする。
```
$ gdb new
GNU gdb (Debian 16.2-8) 16.2
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from new...
(No debugging symbols found in new)
(gdb)
```
まずはmain関数を逆アセンブルしてみる。
```
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000401e43 <+0>:	endbr64
   0x0000000000401e47 <+4>:	push   %rbp
   0x0000000000401e48 <+5>:	mov    %rsp,%rbp
   0x0000000000401e4b <+8>:	sub    $0x50,%rsp
   0x0000000000401e4f <+12>:	mov    %edi,-0x44(%rbp)
   0x0000000000401e52 <+15>:	mov    %rsi,-0x50(%rbp)
   0x0000000000401e56 <+19>:	mov    %fs:0x28,%rax
   0x0000000000401e5f <+28>:	mov    %rax,-0x8(%rbp)
   0x0000000000401e63 <+32>:	xor    %eax,%eax
   0x0000000000401e65 <+34>:	movabs $0x4c75257240343a41,%rax
   0x0000000000401e6f <+44>:	movabs $0x30623e306b6d4146,%rdx
   0x0000000000401e79 <+54>:	mov    %rax,-0x30(%rbp)
   0x0000000000401e7d <+58>:	mov    %rdx,-0x28(%rbp)
   0x0000000000401e81 <+62>:	movabs $0x5f60643630486637,%rax
   0x0000000000401e8b <+72>:	mov    %rax,-0x20(%rbp)
   0x0000000000401e8f <+76>:	movl   $0x37666132,-0x18(%rbp)
   0x0000000000401e96 <+83>:	movw   $0x4e,-0x14(%rbp)
   0x0000000000401e9c <+89>:	lea    0xb1161(%rip),%rdi        # 0x4b3004
   0x0000000000401ea3 <+96>:	mov    $0x0,%eax
   0x0000000000401ea8 <+101>:	call   0x410ba0 <printf>
   0x0000000000401ead <+106>:	lea    -0x3c(%rbp),%rax
   0x0000000000401eb1 <+110>:	mov    %rax,%rsi
   0x0000000000401eb4 <+113>:	lea    0xb1165(%rip),%rdi        # 0x4b3020
   0x0000000000401ebb <+120>:	mov    $0x0,%eax
   0x0000000000401ec0 <+125>:	call   0x410d30 <__isoc99_scanf>
   0x0000000000401ec5 <+130>:	mov    -0x3c(%rbp),%eax
   0x0000000000401ec8 <+133>:	cmp    $0xb83cb,%eax
   0x0000000000401ecd <+138>:	jne    0x401f12 <main+207>
   0x0000000000401ecf <+140>:	lea    -0x30(%rbp),%rax
   0x0000000000401ed3 <+144>:	mov    %rax,%rsi
   0x0000000000401ed6 <+147>:	mov    $0x0,%edi
   0x0000000000401edb <+152>:	call   0x401d85 <rotate_encrypt>
   0x0000000000401ee0 <+157>:	mov    %rax,-0x38(%rbp)
   0x0000000000401ee4 <+161>:	mov    0xdd7e5(%rip),%rdx        # 0x4df6d0 <stdout>
   0x0000000000401eeb <+168>:	mov    -0x38(%rbp),%rax
   0x0000000000401eef <+172>:	mov    %rdx,%rsi
   0x0000000000401ef2 <+175>:	mov    %rax,%rdi
   0x0000000000401ef5 <+178>:	call   0x420980 <fputs>
   0x0000000000401efa <+183>:	mov    $0xa,%edi
   0x0000000000401eff <+188>:	call   0x420e20 <putchar>
   0x0000000000401f04 <+193>:	mov    -0x38(%rbp),%rax
   0x0000000000401f08 <+197>:	mov    %rax,%rdi
   0x0000000000401f0b <+200>:	call   0x42ec70 <free>
   0x0000000000401f10 <+205>:	jmp    0x401f1e <main+219>
   0x0000000000401f12 <+207>:	lea    0xb110a(%rip),%rdi        # 0x4b3023
   0x0000000000401f19 <+214>:	call   0x420c40 <puts>
--Type <RET> for more, q to quit, c to continue without paging--
   0x0000000000401f1e <+219>:	mov    $0x0,%eax
   0x0000000000401f23 <+224>:	mov    -0x8(%rbp),%rcx
   0x0000000000401f27 <+228>:	xor    %fs:0x28,%rcx
   0x0000000000401f30 <+237>:	je     0x401f37 <main+244>
   0x0000000000401f32 <+239>:	call   0x45cba0 <__stack_chk_fail_local>
   0x0000000000401f37 <+244>:	leave
   0x0000000000401f38 <+245>:	ret
End of assembler dump.
```
次のコマンドで0x0000000000401ec8 <+133>:	cmp    $0xb83cb,%eaxにブレークポイントを設定する。
```
(gdb) b * 0x0000000000401ec8
Breakpoint 1 at 0x401ec8
```
ブレークポイントを設定したら実行してみる。
```
(gdb) r
Starting program: /home/kali/CTF/new 
What's my favorite number?
```
実行したら数字の入力を求められたので適当に数字を入力した後に、
次のコマンドでeaxに数字をセットする。
```
(gdb) set $eax=0xb83cb
```
eaxに16進数をセットして次のコマンドでプログラムを進めるとフラグが表示される。
```
(gdb) c
Continuing.
picoCTF{up><_m3_f7w_e510a27f}
[Inferior 1 (process 39782) exited normally]
```

## 終わりに
今回は簡単なプログラムだったので、main関数だけを読むことで済みましたが、
難しくなると、ほかの関数まで調べる必要も出てきそうです。

upxという存在を知ることができただけでもこの問題を解いた意味はあったと思います。

他の問題も解けたら記事にしようと思います。

ここまで読んでいただきありがとうございました。