Lucky Slot

image.png

누가 봐도 유니티로 만들어져있다.

그렇게 되면 Unity == C#이라는 공식을 만들 수 있고, C# 분석에는 dotPeek, dnSpy등의 도구를 이용할 수 있다.(.Net 분석 도구 혹은 C# 분석 도구라고 검색하면 나옴)

되게 많은 파일들이 있는데, Unity라는 웰노운한 프로그램으로 작성된 실행파일이므로 폴더 tree 결과(파일 구조)를 GPT에게 주면 중요한 파일이 ./slot_Data/Managed/Assembly-CSharp.dll이라는 것을 알 수 있다.

GPT를 이용하면 알아서 cherry_bar_cherry_500를 구해주고, Asset을 호출하므로 ./slot_Data/resource.assets에 있는 key를 찾아내어 복호화 가능하다.

key = b"cherry_bar_cherry_500"
blob = list(bytes.fromhex("3A 21 36 34 09 13 3E 01 0A 02 30 17 37 0D 1B 16 1D 3A 5B 6F 59 0D 37 17 17 01 16 2A 10 02 17 22"))
out = bytes([blob[i] ^ key[i % len(key)] for i in range(len(blob))])
print(out)

Please don’t be late

dd if=wisp.sh bs=1 skip=1003 count=13420 of=payload.gz
gunzip -t payload.gz
gunzip -c payload.gz > payload

파일 추출.

모든 코드를 GPT 복붙하였다.(main 한번, 나머지 한 번, 총 두번에 걸쳐서)

주어진 main과 서브루틴을 조합해보면, 프로그램은 입력(64-hex → 32바이트)을 여러 번의 AES-128과 XOR로 섞은 뒤, 최종 두 블록을 상수 v103[0], v103[1](로더 주소 0x406170, 0x406180)와 비교합니다.

관건은 난수(seed). 코드가 매번

srand(time(NULL));
rand(); rand();

로 문자열 "%d%d"를 만들고, 그 SHA-256 해시의 **짝수 바이트(16B)**와 **홀수 바이트(16B)**를 AES 키로 씁니다(각각 sub_404300, sub_4040A0). 그런데 이 과정을 총 4회 수행하면서 매번 다시 srand(time) 를 호출합니다. 실행이 거의 즉시 이뤄지므로 time() 값(초)이 매번 같고, 따라서

“홀수 바이트 키”는 1·2회차가 동일 (K₁ = K₂)

“짝수 바이트 키”는 3·4회차가 동일 (K₃ = K₄)
이 됩니다.

그 결과, 중간에서 등장하는

C₂ ⊕ C₁,   Z₂ ⊕ Z₁,   C₄ ⊕ C₃,   Z₄ ⊕ Z₃

같은 항들이 모두 0으로 상쇄됩니다(같은 키로 같은 평문/영(0)블록을 암호화하므로 동일한 결과). 그러면 최종 비교식은 깔끔하게 다음으로 환원됩니다.

최종 타깃 블록들을 T0 = [0x406170], T1 = [0x406180],

상수 마스크를 M150 = [0x406150], M160 = [0x406160],

고정 키들을

KA = [0x4061A0] ⊕ [0x406130]

KB = [0x4061A0] ⊕ [0x406140]

KC = [0x406190] ⊕ [0x406130]

KD = [0x406190] ⊕ [0x406140]
로 두면,

최종 단계의 역연산은

V109 = AES_dec(KA, T0 ⊕ M150)

V110 = AES_dec(KB, T1 ⊕ M160)

P0 = AES_dec(KC, V109 ⊕ M150) ← 입력 앞 16바이트

P1 = AES_dec(KD, V110 ⊕ M160) ← 입력 뒤 16바이트

이며, P0‖P1을 64-hex로 적으면 곧 정답입니다.
이때 쓴 AES는 표준 AES-128(ECB 한 블록, SubBytes/ShiftRows/MixColumns/AddRoundKey, 표준 S-box/Rcon) 입니다. sub_401F10의 디컴파일에도 GF(2⁸)에서의 2배/3배(0x1B 다항), Rcon, S-box 테이블(byte_406200), 키스케줄(byte_406300)이 그대로 보입니다.
# solve.py
import mmap, struct

def read16(mm, addr, base=0x400000):
    off = addr - base
    return mm[off:off+16]

Sbox=[0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]
Inv=[0]*256
for i,v in enumerate(Sbox): Inv[v]=i
R=[0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36]
def mul(a,b):
    r=0
    for _ in range(8):
        if b&1: r^=a
        a=((a<<1)&0xff) ^ (0x1b if a&0x80 else 0)
        b>>=1
    return r
def rk_expand(key):
    Nk=4; Nb=4; Nr=10
    w=[key[i*4:(i+1)*4] for i in range(Nk)]
    for i in range(Nk,Nb*(Nr+1)):
        t=w[i-1]
        if i%Nk==0:
            t=bytes([Sbox[t[1]],Sbox[t[2]],Sbox[t[3]],Sbox[t[0]]])
            t=bytes([t[0]^R[i//Nk],t[1],t[2],t[3]])
        w.append(bytes([w[i-Nk][j]^t[j] for j in range(4)]))
    return [b''.join(w[i*4:(i+1)*4]) for i in range(Nr+1)]
def add(s,k): return bytes(x^y for x,y in zip(s,k))
def sub(s): return bytes(Sbox[b] for b in s)
def isub(s): return bytes(Inv[b] for b in s)
def sr(s):
    s=list(s); out=[0]*16
    idx=lambda r,c: c*4+r
    for r in range(4):
        row=[s[idx(r,c)] for c in range(4)]
        row=row[r:]+row[:r]
        for c in range(4): out[idx(r,c)]=row[c]
    return bytes(out)
def isr(s):
    s=list(s); out=[0]*16
    idx=lambda r,c: c*4+r
    for r in range(4):
        row=[s[idx(r,c)] for c in range(4)]
        row=row[-r:]+row[:-r]
        for c in range(4): out[idx(r,c)]=row[c]
    return bytes(out)
def mc(s):
    o=bytearray(16)
    for c in range(4):
        col=[s[c*4+r] for r in range(4)]
        o[c*4+0]=mul(2,col[0])^mul(3,col[1])^col[2]^col[3]
        o[c*4+1]=col[0]^mul(2,col[1])^mul(3,col[2])^col[3]
        o[c*4+2]=col[0]^col[1]^mul(2,col[2])^mul(3,col[3])
        o[c*4+3]=mul(3,col[0])^col[1]^col[2]^mul(2,col[3])
    return bytes(o)
def imc(s):
    o=bytearray(16)
    for c in range(4):
        col=[s[c*4+r] for r in range(4)]
        o[c*4+0]=mul(0x0e,col[0])^mul(0x0b,col[1])^mul(0x0d,col[2])^mul(0x09,col[3])
        o[c*4+1]=mul(0x09,col[0])^mul(0x0e,col[1])^mul(0x0b,col[2])^mul(0x0d,col[3])
        o[c*4+2]=mul(0x0d,col[0])^mul(0x09,col[1])^mul(0x0e,col[2])^mul(0x0b,col[3])
        o[c*4+3]=mul(0x0b,col[0])^mul(0x0d,col[1])^mul(0x09,col[2])^mul(0x0e,col[3])
    return bytes(o)
def enc(p,k):
    rk=rk_expand(k); s=add(p,rk[0])
    for r in range(1,10):
        s=mc(sr(sub(s))); s=add(s,rk[r])
    s=add(sr(sub(s)),rk[10]); return s
def dec(c,k):
    rk=rk_expand(k); s=add(c,rk[10])
    for r in range(9,0,-1):
        s=imc(add(isub(isr(s)),rk[r]))
    s=add(isub(isr(s)),rk[0]); return s

def bx(a,b): return bytes(x^y for x,y in zip(a,b))

with open("payload","rb") as f:
    mm=mmap.mmap(f.fileno(),0,access=mmap.ACCESS_READ)

C130=read16(mm,0x406130); C140=read16(mm,0x406140)
C150=read16(mm,0x406150); C160=read16(mm,0x406160)
T0  =read16(mm,0x406170); T1  =read16(mm,0x406180)
C190=read16(mm,0x406190); C1A0=read16(mm,0x4061A0)

KA=bx(C1A0,C130); KB=bx(C1A0,C140)
KC=bx(C190,C130); KD=bx(C190,C140)

V109=dec(bx(T0,C150),KA)
V110=dec(bx(T1,C160),KB)

P0=dec(bx(V109,C150),KC)
P1=dec(bx(V110,C160),KD)

print((P0+P1).hex())

엄청나게 긴 코드는 AI가 패턴 분석이나 대량의 데이터에 능하기에 보통 GPT를 적극 활용하여 분석하는 편이다.

And로이드

APK 파일이 주어진다.

APK 파일 해제하면 dex 파일(classes)이 있는데 이것에 중요한 정보가 있다. dex2jar 툴을 이용하여 dex파일을 jar 파일로 변환하여 준다.

https://200301.tistory.com/32