CTF Writeup

zer0ptsCTF 2023 - fvm Writeup

LittleDev0617 2023. 7. 18. 23:43

 

vm 문제로 보인다.

 

여러 vm 문제를 풀어보면서 터득한 루틴이 있다.

  1. instruction 분석
  2. disassembler 간단 제작 후 전체 코드 뽑아보기
  3. 동적 실행으로 조건문/반복문 판별하며 흐름 분석
  4. 각 instruction 들이 모인 코드 덩이들의 목적이나 역할 분석 - 함수화
  5. 정연산 짜기
  6. 복호화 코드 짜기

 

1. instruction 분석

모든 연산을 fpu 위에서 한다.

fpu 에 대해 아예 몰랐었는데, 이번에 많이 알게 되었다.

 

먼저 fpu 는 8개의 st register를 stack 구조로 관리한다.

그리고 top 을 이동시키며 push/pop 을 구현하기 때문에 8번 push/pop 하면 제자리로 돌아오게 된다.

예를 들어보자면

위 그림과 같다. 

top 을 기준으로 st0 부터 st1 st2 ... st7 까지 정한다.

 

fld__ instruction : push ___ 

fxch st_ : swap(st[0], st[_])

fdecstp : top++

fincstp  : top--

fcom  : st[0] st[1] 비교 후 flag 설정

이정도를 사용하며, 뒤에 p 가 붙은 instruction들은 연산 후 pop 한다는 소리이다.

2. disassembler 제작

- vm.py

더보기
# vm.py
from collections import deque
import math
DYNAMIC_DEBUG_MODE = True
vm = bytes.fromhex('23 38 41 24 43 54 24 43 54 73 24 38 38 41 43 27 41 24 43 54 73 24 38 38 23 51 43 43 43 54 73 23 24 41 24 38 43 43 54 73 23 38 38 51 43 43 24 43 54 73 23 38 38 43 43 54 22 41 73 21 62 94 01 62 B2 01 62 84 01 39 05 3F 11 F4 FF 29 57 81 0E 40 65 03 00 32 53 32 3A 39 5E 57 F3 B4 A3 8C 7F BA 07 40 65 03 00 31 53 31 3A 62 67 01 62 85 01 62 57 01 39 B7 73 80 E1 B0 72 87 F2 03 40 65 03 00 32 53 32 3A 39 3F 68 A3 E4 04 9A 3B 8F 07 40 65 03 00 31 53 31 3A 62 3A 01 62 58 01 62 2A 01 39 54 27 B5 B6 95 52 5D CD 0B 40 65 03 00 32 53 32 3A 39 28 91 9A 4A A2 71 37 91 07 40 65 03 00 31 53 31 3A 62 0D 01 62 2B 01 62 FD 00 39 A3 86 14 05 41 8C 74 E5 0B 40 65 03 00 32 53 32 3A 39 BC 61 ED F2 E9 6E C1 AB 06 40 65 03 00 31 53 31 3A 62 E0 00 62 FE 00 62 D0 00 39 88 92 3A E0 69 3F 54 92 06 40 65 03 00 32 53 32 3A 39 0F D1 BE E3 39 A1 96 C5 05 40 65 03 00 31 53 31 3A 62 B3 00 62 D1 00 62 A3 00 39 83 EA 7B 97 E5 4C D3 EA 06 40 65 03 00 32 53 32 3A 39 87 EF B0 D1 5C FE 86 AD 04 40 65 03 00 31 53 31 3A 62 86 00 62 A4 00 62 76 00 39 19 42 6C D4 CA F6 F2 D9 09 40 65 03 00 32 53 32 3A 39 EF FF DA 15 13 EA AE DB 06 40 65 03 00 31 53 31 3A 62 59 00 62 77 00 62 49 00 39 FE 13 97 DF CD 12 7E F0 0A 40 65 03 00 32 53 32 3A 39 1C AF CF 1F 96 45 41 A5 06 40 65 03 00 31 53 31 3A 72 23 38 43 25 41 24 38 43 43 54 64 05 00 3A 3A 63 7D 00 3A 39 5A 88 34 14 A0 C3 05 BD FE 3F 64 8E 00 63 6B 00 32 38 32 38 32 43 32 41 32 61 72 62 42 00 72 62 3E 00 22 22 41 23 43 43 24 38 41 54 24 38 43 43 54 24 43 54 44 38 52 42 43 31 61 72 62 21 00 72 62 1D 00 22 22 41 23 43 43 24 38 41 54 24 38 43 43 54 24 43 54 44 38 53 22 41 31 52 43 43 31 61 31 23 38 43 23 43 54 22 41 67 0F 00 24 38 43 24 43 25 41 24 43 54 68 02 00 31 61 23 25 43 24 41 23 38 43 43 54 73 23 24 41 24 38 43 43 54 73 22 23 41 24 38 43 43 54 73 63 21 00 23 22 45 41 24 38 38 43 43 43 54 73 24 38 26 23 41 24 41 43 43 54 73 23 38 43 24 43 54 73 63 00 00 23 38 43 54 73 71 00')
INPUT = b'zer0pts{TSET_1234_abcd}\n'
inp_p = 0
p = 0
v = 0
isExit = False
reg = [0 for _ in range(8)]
top = 0

loopCnt = 0
indexList = []

pp = 0
while not isExit:    
    c = vm[p if DYNAMIC_DEBUG_MODE else pp]
    # if DYNAMIC_DEBUG_MODE:
    #     if p not in indexList:
    #         indexList.append(p)
    #     else:
    #         loopCnt += 1
    #         print('*'*10 + f'loop {loopCnt}')
    #         print(indexList)
    #         indexList = [p]
    #         input()

    offset = 0
    if DYNAMIC_DEBUG_MODE:
        print(f'[{p:4d}] : {chr(c)}\t{round(reg[top % 8])}\t{round(reg[(top + 1) % 8])}', end='\t')
    else:
        print(f'[{pp:4d}] : {chr(c)}\t', end='\t')

    if p == 80:
        for i in range(8):
            print(reg[(top + i) % 8], end=' ')
        input()
    if DYNAMIC_DEBUG_MODE:
        p += 1
    else:
        pp += 1

    # bcdefghi
    if 0 <= c - 0x62 <= 0xD:
        if DYNAMIC_DEBUG_MODE:
            offset = int.from_bytes(vm[p:p+2], 'little', signed=True)
            p += 2
        else:
            offset = int.from_bytes(vm[pp:pp+2], 'little', signed=True)
            pp += 2

    c = chr(c)
    match c:
        case '!':
            print('PUSH 0', end='')
            top -= 1
            reg[top % 8] = 0
        case '"':
            print('PUSH 1', end='')
            top -= 1
            reg[top % 8] = 1
        case '#':
            print('PUSH PI', end='')
            top -= 1
            reg[top % 8] = math.pi
        case '$':
            print('PUSH log2_10' + str(round(math.log2(10))), end='')
            top -= 1
            reg[top % 8] = math.log2(10)
        case '%':
            print('PUSH log2_e' + str(round(math.log2(math.e))), end='')
            top -= 1
            reg[top % 8] = math.log2(math.e)
        case '&':
            print('PUSH log10_2' + str(round(math.log10(2))), end='')
            top -= 1
            reg[top % 8] = math.log10(2)
        case '\\':
            print('PUSH ln2' + str(round(math.log(math.e, 2))), end='')
            top -= 1
            reg[top % 8] = math.log(math.e, 2)
        case c if c in '1234567':
            print(f'swap {0} {int(c)}', end='')
            reg[top % 8], reg[(top + int(c)) % 8] = reg[(top + int(c)) % 8], reg[top % 8]
        case '8':
            print('DUP', end='')
            reg[(top - 1) % 8] = reg[top % 8]
            top -= 1
        case '9':
            top -= 1
            reg[top % 8] = int.from_bytes(vm[p:p+10], 'little')
            if DYNAMIC_DEBUG_MODE:
                p += 10
            else:
                pp += 10
        case ':':         
            print('POP', end='')
            top += 1
        case 'A':
            if DYNAMIC_DEBUG_MODE:
                print(f'ADD {reg[top % 8]} {reg[(top+1)%8]}', end='')
            else:
                print('ADD', end='')
            tmp = reg[top % 8]
            top += 1
            reg[top % 8] += tmp
        case 'B':
            if DYNAMIC_DEBUG_MODE:
                print(f'SUB {reg[top % 8]} {reg[(top+1)%8]}', end='')
            else:
                print('SUB', end='')
            tmp = reg[top % 8]
            top += 1
            reg[top % 8] -= tmp
        case 'C':
            if DYNAMIC_DEBUG_MODE:
                print(f'MUL {reg[top % 8]} {reg[(top+1)%8]}', end='')
            else:
                print('MUL', end='')
            tmp = reg[top % 8]
            top += 1
            reg[top % 8] *= tmp
        case 'D':
            if DYNAMIC_DEBUG_MODE:
                print(f'DIV {reg[top % 8]} {reg[(top+1)%8]}', end='')
            else:
                print('DIV', end='')
            tmp = reg[top % 8]
            top += 1
            reg[top % 8] /= tmp
        case 'E':
            print('NEG', end='')
            reg[top % 8] = -reg[top % 8]
        case 'Q':
            reg[top % 8] = math.sqrt(reg[top % 8])
        case 'R':
            reg[top % 8] = math.sin(reg[top % 8])            
        case 'S':
            reg[top % 8] = math.cos(reg[top % 8])
        case 'T':
            reg[top % 8] = round(reg[top % 8])
        case 'a':
            v = round(reg[top % 8])
            
            if DYNAMIC_DEBUG_MODE:
                print(f'RET\t{v}', end='\t')
            else:
                print('RET', end='')

            top += 1
            p = v
        case 'b':            
            if DYNAMIC_DEBUG_MODE:
                print(f'CALL\t{p+offset}', end='\t')
            else:
                print(f'CALL\t{pp+offset}', end='\t')

            v = p
            top -= 1
            reg[top % 8] = v
            p += offset
        case 'c':
            print(f'JMP\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            p += offset
        case 'd':
            print(f'JE\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] == reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'e':
            print(f'JNE\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] != reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'f':
            print(f'JGE\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] >= reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'g':
            print(f'JG\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] > reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'h':
            print(f'JLE\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] <= reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'i':
            print(f'JL\t{p if DYNAMIC_DEBUG_MODE else pp +offset}', end='\t')
            if reg[top % 8] < reg[(top + 1) % 8]:
                p += offset
            top += 1
        case 'q':
            isExit = True
        case 'r':
            if inp_p < len(INPUT):
                print(chr(INPUT[inp_p]),end='')
                v = INPUT[inp_p]
                inp_p += 1
                top -= 1
                reg[top % 8] = v
        case 's':
            v = round(reg[top % 8])
            top += 1            
            print(chr(v), end='')

    print()

dynamic 모드를 설정할 수 있는데, disable 하면 코드를 실행하지 않고 disassemble 만 수행한다.

dynamic 모드에서는 실제로 실행시켜 값들을 확인하고 흐름을 분석할 수 있다.

 

CALL / RET 은 각각 b / a instruction 인데, 현재 EIP(vm pointer) 를 push 하고 pop / jmp 하는 것을 보고 알아내었다.

 

- code

더보기
[   0] : #		PUSH PI
[   1] : 8		DUP		
[   2] : A		ADD
[   3] : $		PUSH log2_103
[   4] : C		MUL
[   5] : T		
[   6] : $		PUSH log2_103
[   7] : C		MUL
[   8] : T		
[   9] : s		F
[  10] : $		PUSH log2_103
[  11] : 8		DUP		
[  12] : 8		DUP		
[  13] : A		ADD
[  14] : C		MUL
[  15] : '		
[  16] : A		ADD
[  17] : $		PUSH log2_103
[  18] : C		MUL
[  19] : T		
[  20] : s		I
[  21] : $		PUSH log2_103
[  22] : 8		DUP		
[  23] : 8		DUP		
[  24] : #		PUSH PI
[  25] : Q		
[  26] : C		MUL
[  27] : C		MUL
[  28] : C		MUL
[  29] : T		
[  30] : s		A
[  31] : #		PUSH PI
[  32] : $		PUSH log2_103
[  33] : A		ADD
[  34] : $		PUSH log2_103
[  35] : 8		DUP		
[  36] : C		MUL
[  37] : C		MUL
[  38] : T		
[  39] : s		G
[  40] : #		PUSH PI
[  41] : 8		DUP		
[  42] : 8		DUP		
[  43] : Q		
[  44] : C		MUL
[  45] : C		MUL
[  46] : $		PUSH log2_103
[  47] : C		MUL
[  48] : T		
[  49] : s		:
[  50] : #		PUSH PI
[  51] : 8		DUP		
[  52] : 8		DUP		
[  53] : C		MUL
[  54] : C		MUL
[  55] : T		
[  56] : "		PUSH 1
[  57] : A		ADD
[  58] : s		 
[  59] : !		PUSH 0
[  60] : b		CALL	467	
[  63] : b		CALL	500	
[  66] : b		CALL	457	
[  69] : 9		
[  80] : e		JNE	86	
[  83] : 2		swap 0 2
[  84] : S		COS		
[  85] : 2		swap 0 2
[  86] : :		POP
[  87] : 9		
[  98] : e		JNE	104	
[ 101] : 1		swap 0 1
[ 102] : S		COS		
[ 103] : 1		swap 0 1
[ 104] : :		POP
[ 105] : b		CALL	467	
[ 108] : b		CALL	500	
[ 111] : b		CALL	457	
[ 114] : 9		
[ 125] : e		JNE	131	
[ 128] : 2		swap 0 2
[ 129] : S		COS		
[ 130] : 2		swap 0 2
[ 131] : :		POP
[ 132] : 9		
[ 143] : e		JNE	149	
[ 146] : 1		swap 0 1
[ 147] : S		COS		
[ 148] : 1		swap 0 1
[ 149] : :		POP
[ 150] : b		CALL	467	
[ 153] : b		CALL	500	
[ 156] : b		CALL	457	
[ 159] : 9		
[ 170] : e		JNE	176	
[ 173] : 2		swap 0 2
[ 174] : S		COS		
[ 175] : 2		swap 0 2
[ 176] : :		POP
[ 177] : 9		
[ 188] : e		JNE	194	
[ 191] : 1		swap 0 1
[ 192] : S		COS		
[ 193] : 1		swap 0 1
[ 194] : :		POP
[ 195] : b		CALL	467	
[ 198] : b		CALL	500	
[ 201] : b		CALL	457	
[ 204] : 9		
[ 215] : e		JNE	221	
[ 218] : 2		swap 0 2
[ 219] : S		COS		
[ 220] : 2		swap 0 2
[ 221] : :		POP
[ 222] : 9		
[ 233] : e		JNE	239	
[ 236] : 1		swap 0 1
[ 237] : S		COS		
[ 238] : 1		swap 0 1
[ 239] : :		POP
[ 240] : b		CALL	467	
[ 243] : b		CALL	500	
[ 246] : b		CALL	457	
[ 249] : 9		
[ 260] : e		JNE	266	
[ 263] : 2		swap 0 2
[ 264] : S		COS		
[ 265] : 2		swap 0 2
[ 266] : :		POP
[ 267] : 9		
[ 278] : e		JNE	284	
[ 281] : 1		swap 0 1
[ 282] : S		COS		
[ 283] : 1		swap 0 1
[ 284] : :		POP
[ 285] : b		CALL	467	
[ 288] : b		CALL	500	
[ 291] : b		CALL	457	
[ 294] : 9		
[ 305] : e		JNE	311	
[ 308] : 2		swap 0 2
[ 309] : S		COS		
[ 310] : 2		swap 0 2
[ 311] : :		POP
[ 312] : 9		
[ 323] : e		JNE	329	
[ 326] : 1		swap 0 1
[ 327] : S		COS		
[ 328] : 1		swap 0 1
[ 329] : :		POP
[ 330] : b		CALL	467	
[ 333] : b		CALL	500	
[ 336] : b		CALL	457	
[ 339] : 9		
[ 350] : e		JNE	356	
[ 353] : 2		swap 0 2
[ 354] : S		COS		
[ 355] : 2		swap 0 2
[ 356] : :		POP
[ 357] : 9		
[ 368] : e		JNE	374	
[ 371] : 1		swap 0 1
[ 372] : S		COS		
[ 373] : 1		swap 0 1
[ 374] : :		POP
[ 375] : b		CALL	467	
[ 378] : b		CALL	500	
[ 381] : b		CALL	457	
[ 384] : 9		
[ 395] : e		JNE	401	
[ 398] : 2		swap 0 2
[ 399] : S		COS		
[ 400] : 2		swap 0 2
[ 401] : :		POP
[ 402] : 9		
[ 413] : e		JNE	419	
[ 416] : 1		swap 0 1
[ 417] : S		COS		
[ 418] : 1		swap 0 1
[ 419] : :		POP

; last flag
[ 420] : r		z

; chr(round((pi * pi + log2(e)) * log2(10) * log2(10))) == '}'
[ 421] : #		PUSH PI
[ 422] : 8		DUP		
[ 423] : C		MUL
[ 424] : %		PUSH log2_e1
[ 425] : A		ADD
[ 426] : $		PUSH log2_103
[ 427] : 8		DUP		
[ 428] : C		MUL
[ 429] : C		MUL
[ 430] : T		
[ 431] : d		JE	439	
[ 434] : :		POP
[ 435] : :		POP
[ 436] : c		JMP	564     ; Wrong
[ 439] : :		POP
[ 440] : 9		
[ 451] : d		JE	596	    ; Correct
[ 454] : c		JMP	564	

    def calc(a, b):
        return (ab, a+b)
    [ 457] : 2		swap 0 2
    [ 458] : 8		DUP		
    [ 459] : 2		swap 0 2
    [ 460] : 8		DUP		
    [ 461] : 2		swap 0 2
    [ 462] : C		MUL
    [ 463] : 2		swap 0 2
    [ 464] : A		ADD
    [ 465] : 2		swap 0 2
    [ 466] : a		RET

    [ 467] : r		e
    [ 468] : b		CALL	537	
    [ 471] : r		r
    [ 472] : b		CALL	537	
    [ 475] : "		PUSH 1
    [ 476] : "		PUSH 1
    [ 477] : A		ADD
    [ 478] : #		PUSH PI
    [ 479] : C		MUL
    [ 480] : C		MUL
    [ 481] : $		PUSH log2_103
    [ 482] : 8		DUP		
    [ 483] : A		ADD
    [ 484] : T		
    [ 485] : $		PUSH log2_103
    [ 486] : 8		DUP		
    [ 487] : C		MUL
    [ 488] : C		MUL
    [ 489] : T		
    [ 490] : $		PUSH log2_103
    [ 491] : C		MUL
    [ 492] : T		
    [ 493] : D		DIV
    [ 494] : 8		DUP		
    [ 495] : R		SIN		
    [ 496] : B		SUB
    [ 497] : C		MUL
    [ 498] : 1		swap 0 1
    [ 499] : a		RET

    [ 500] : r		0
    [ 501] : b		CALL	537	
    [ 504] : r		p
    [ 505] : b		CALL	537	
    [ 508] : "		PUSH 1
    [ 509] : "		PUSH 1
    [ 510] : A		ADD
    [ 511] : #		PUSH PI
    [ 512] : C		MUL
    [ 513] : C		MUL
    [ 514] : $		PUSH log2_103
    [ 515] : 8		DUP		
    [ 516] : A		ADD
    [ 517] : T		
    [ 518] : $		PUSH log2_103
    [ 519] : 8		DUP		
    [ 520] : C		MUL
    [ 521] : C		MUL
    [ 522] : T		
    [ 523] : $		PUSH log2_103
    [ 524] : C		MUL
    [ 525] : T		
    [ 526] : D		DIV
    [ 527] : 8		DUP		
    [ 528] : S		COS		
    [ 529] : "		PUSH 1
    [ 530] : A		ADD
    [ 531] : 1		swap 0 1
    [ 532] : R		SIN		
    [ 533] : C		MUL
    [ 534] : C		MUL
    [ 535] : 1		swap 0 1
    [ 536] : a		RET

    func is_ascii(char c)
    [ 537] : 1		swap 0 1
    [ 538] : #		PUSH PI
    [ 539] : 8		DUP		
    [ 540] : C		MUL
    [ 541] : #		PUSH PI
    [ 542] : C		MUL
    [ 543] : T		
    [ 544] : "		PUSH 1          ; 31
    [ 545] : A		ADD             ; 32
    [ 546] : g		JG	564	
    [ 549] : $		PUSH log2_103
    [ 550] : 8		DUP		
    [ 551] : C		MUL
    [ 552] : $		PUSH log2_103
    [ 553] : C		MUL
    [ 554] : %		PUSH log2_e1
    [ 555] : A		ADD
    [ 556] : $		PUSH log2_103
    [ 557] : C		MUL
    [ 558] : T		
    [ 559] : h		JLE	564	        ; 127
    [ 562] : 1		swap 0 1
    [ 563] : a		RET

[ 564] : #		PUSH PI
[ 565] : %		PUSH log2_e1
[ 566] : C		MUL
[ 567] : $		PUSH log2_103
[ 568] : A		ADD
[ 569] : #		PUSH PI
[ 570] : 8		DUP		
[ 571] : C		MUL
[ 572] : C		MUL
[ 573] : T		
[ 574] : s		N
[ 575] : #		PUSH PI
[ 576] : $		PUSH log2_103
[ 577] : A		ADD
[ 578] : $		PUSH log2_103
[ 579] : 8		DUP		
[ 580] : C		MUL
[ 581] : C		MUL
[ 582] : T		
[ 583] : s		G
[ 584] : "		PUSH 1
[ 585] : #		PUSH PI
[ 586] : A		ADD
[ 587] : $		PUSH log2_103
[ 588] : 8		DUP		
[ 589] : C		MUL
[ 590] : C		MUL
[ 591] : T		
[ 592] : s		.
[ 593] : c		JMP	629	

[ 596] : #		PUSH PI
[ 597] : "		PUSH 1
[ 598] : E		NEG
[ 599] : A		ADD
[ 600] : $		PUSH log2_103
[ 601] : 8		DUP		
[ 602] : 8		DUP		
[ 603] : C		MUL
[ 604] : C		MUL
[ 605] : C		MUL
[ 606] : T		
[ 607] : s		O
[ 608] : $		PUSH log2_103
[ 609] : 8		DUP		
[ 610] : &		PUSH log10_20
[ 611] : #		PUSH PI
[ 612] : A		ADD
[ 613] : $		PUSH log2_103
[ 614] : A		ADD
[ 615] : C		MUL
[ 616] : C		MUL
[ 617] : T		
[ 618] : s		K
[ 619] : #		PUSH PI
[ 620] : 8		DUP		
[ 621] : C		MUL
[ 622] : $		PUSH log2_103
[ 623] : C		MUL
[ 624] : T		
[ 625] : s		!
[ 626] : c		JMP	629	
[ 629] : #		PUSH PI
[ 630] : 8		DUP		
[ 631] : C		MUL
[ 632] : T		
[ 633] : s		
[ 634] : q

3. 동작 분석

...

위처럼 467 500 457 CALL 후 비교 2번 하는 구조가 총 8번 반복된다.

 

- 467 function

입력(r) 을 2번 받고 537을 CALL 하는 것을 볼 수 있다.

 

537 은 32 <= 127 <= 인지 검증한다. 대충 is_ascii 로 이름지어줬다.

 

다시 467로 돌아오면 특정 연산을 한다.

l2t = log2(10)
print(round(round(round(2 * l2t) * l2t * l2t) * l2t))

# 256

두 번째 log2_10 을 사용하는 상수값은 위 코드처럼 256 이고 앞은 2 * pi * input[0] 임을 알 수 있다.

flag[i] *   (((2 * pi * flag[i+1]) / 256) - sin(2 * pi * flag[i+1] / 256))

처럼 정리할 수 있다.

 

- 500 function

마찬가지로 입력 2번 537 2번 호출한다.

이후 연산도 아래처럼 정리한다.

flag[i+2] * (1 + cos(2 * pi * flag[i+3] / 256)) * sin(2 * pi * flag[i+3] / 256)

- 457 function

return 으로 표현했지만 st 0 / 1 에 b / a 를 설정하면 ab / a + b 로 st 0 / 1을 바꿔준다.

 

- 비교

467 500 457을 CALL 한 후 2번의 비교를 한다.

"9" instruction 은 10 byte constant float 를 push 한다.

따라서 a*b 와 a + b 가 각각 특정 상수와 같으면 cos 연산을 한다.

st2는 처음에 0으로 세팅되어있고, 위의 비교가 참이면 cos 연산을 한다.

즉 정답을 입력했을 때 총 16번 cos 연산을 할 것이고, 이 값은 0.7383692041223232 가 된다.

맨 마지막에 이 값이 0.7383692041223232 인지 체크한다.

그리고 맨 마지막 글자가 } 인지 체크한다.

 

아까 467 500 457 구조가 총 8 번 있었고, 각 검사마다 4글자씩 검사하기 때문에 8 * 4 , 32글자에 마지막 } 까지 flag가 33글자임을 알 수 있따.

 

비교하는 10byte float 상수값들을 구하기 위해 다음 코드를 사용한다.

$ gdb -x log.py ./fvm > /dev/null

4. 정연산

5. 역연산

a, b, c, d 를 flag 의 4 글자씩이라 할 때, (b, d / 256 생략)

이고, alpha * beta 와 alpha + beta를 알기 때문에 alpha와 beta 각각의 값을 알 수 있다.

이후 256 * 256 브포는 돌릴만하므로 a,b / c, d 따로 오차가 적은 경우를 선택한다.

이때 어떤 값이 alpha고 beta인지 모르므로 4경우를 다 체크한다.

FLAG : zer0pts{fun_0f_FPU_st4ck_m4ch1n3}

'CTF Writeup' 카테고리의 다른 글

zer0ptsCTF 2023 - decompile_me Writeup  (0) 2023.07.19
zer0ptsCTF 2023 - mimikyu Writeup  (0) 2023.07.19
2023 Codegate UNIV division writeup  (0) 2023.06.18
Defcon 2023 Qualifier - kkkkklik writeup  (0) 2023.05.31
2022 Incognito CTF Writeup  (0) 2023.03.26