Bakhönnun Python-verkefna
Python-bakhönnunarverkefni henta oft byrjendum því frumkóðinn er yfirleitt læsilegur. Kóðanum kann þó að hafa verið breytt með torlæsingu eða hann getur notað umbreytingar sem við þurfum að snúa við.
Algengar tegundir
| Tegund | Lýsing |
|---|---|
| Samanburður | Forritið ber inntak saman við rétt svar og við könnum hvernig samanburðurinn fer fram |
| XOR-dulritun | Fáninn er dulritaður með XOR-aðgerð og við beitum sömu aðgerð aftur til að endurheimta hann |
| Sérsniðin kóðun | Stöfum eða bætum er umraðað eða skipt út eftir fastri reglu, til dæmis með umskiptum |
| Torlæsing | Kóðinn er vísvitandi gerður torlæs, til dæmis með óskiljanlegum breytunöfnum eða exec og Base64-kóðuðum texta |
Dæmi 1: Beinn samanburður
# challenge.py
import sys
def check(flag):
secret = [103, 103, 123, 115, 51, 99, 114, 51, 116, 125]
if len(flag) != len(secret):
return False
for i in range(len(secret)):
if ord(flag[i]) != secret[i]:
return False
return True
flag = input("Flag: ")
if check(flag):
print("Correct!")
else:
print("Wrong!")
Lausn: Breyta tölunum í stafi:
secret = [103, 103, 123, 115, 51, 99, 114, 51, 116, 125]
flag = ''.join(chr(c) for c in secret)
print(flag) # gg{s3cr3t}
Dæmi 2: XOR-dulritun
# challenge.py
encrypted = bytes([0x25, 0x25, 0x39, 0x3a, 0x72, 0x30, 0x3f])
key = 0x42
flag = input("Flag: ").encode()
if len(flag) != len(encrypted):
exit("Wrong length")
result = bytes(b ^ key for b in flag)
if result == encrypted:
print("Correct!")
Lausn: XOR-aðgerðin er andhverfanleg. Við beitum henni aftur með sama lykli til að fá fánann:
encrypted = bytes([0x25, 0x25, 0x39, 0x3a, 0x72, 0x30, 0x3f])
key = 0x42
flag = bytes(b ^ key for b in encrypted)
print(flag.decode()) # gg{x0r}
Dæmi 3: Umskipti
# challenge.py
table = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(s):
return ''.join(table[alpha.index(c)] if c in alpha else c for c in s)
secret = "uu{itssg_vgksr}"
flag = input("Flag: ")
if encode(flag) == secret:
print("Correct!")
Lausn: Búa til uppflettingartöflu:
table = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
secret = "uu{itssg_vgksr}"
reverse = {t: a for t, a in zip(table, alpha)}
flag = ''.join(reverse.get(c, c) for c in secret)
print(flag) # gg{hello_world}
Dæmi 4: Torlæs kóði
Stundum lítur kóðinn svona út:
exec(__import__('base64').b64decode(
b'ZmxhZyA9IGlucHV0KCk7IHByaW50KCdDb3JyZWN0IScgaWYgZmxhZyA9PSAnZ2d7dGVzdH0nIGVsc2UgJ1dyb25nIScpCg=='
))
Lausn: Við keyrum kóðann ekki strax heldur afkóðum Base64-gögnin og lesum niðurstöðuna fyrst:
import base64
code = base64.b64decode(b'ZmxhZyA9IGlucHV0KCk7IHByaW50KCdDb3JyZWN0IScgaWYgZmxhZyA9PSAnZ2d7dGVzdH0nIGVsc2UgJ1dyb25nIScpCg==')
print(code.decode())
# flag = input(); print('Correct!' if flag == 'gg{test}' else 'Wrong!')
Keyrum aldrei óþekktan kóða beint
Þegar við sjáum exec(...) eða eval(...) með kóðuðum strengjum afkóðum við og lesum kóðann áður en við keyrum hann.
Dæmi 5: Mörg lög umbreytinga
Stundum er nokkrum kóðunaraðferðum og umbreytingum beitt hverri á eftir annarri:
import base64, codecs
def encode(flag):
step1 = flag.encode()
step2 = base64.b64encode(step1)
step3 = codecs.encode(step2.decode(), 'rot_13').encode()
step4 = step3[::-1] # Snúa við
return step4
target = b'==DsmW3Z5EQo7q2M'
guess = input("Fáni: ")
if encode(guess) == target:
print("Rétt!")
else:
print("Rangt!")
target er úttakið sem rétti fáninn á að gefa. Það byrjar á == vegna þess að í síðasta skrefinu er strengnum snúið við og fyllingarstafir Base64 færast fremst.
Lausn: Snúa við í öfugri röð:
import base64, codecs
target = b'==DsmW3Z5EQo7q2M'
step3 = target[::-1] # Snúa við
step2 = codecs.decode(step3.decode(), 'rot_13') # Afkóða ROT13
step1 = base64.b64decode(step2) # Afkóða Base64
flag = step1.decode() # UTF-8
print(flag) # gg{l4y3rs}
Gagnleg Python-tól
# Prenta alla stafi í streng
for c in b'\x41\x42\x43':
print(chr(c), end='')
# Breyta á milli gagnagerða
int('ff', 16) # sextándakerfi → heiltala: 255
hex(255) # heiltala → sextándakerfi: '0xff'
bin(255) # heiltala → tvíundakerfi: '0b11111111'
bytes([65, 66, 67]) # listi → bytes-gildi: b'ABC'
list(b'ABC') # bytes-gildi → listi: [65, 66, 67]
# XOR allt á móti lykli
key = 0x42
result = bytes(b ^ key for b in encrypted)
# Finna lykil ef við þekkjum hluta ódulritaða textans
# "gg{" er oft upphaf fánans
for key in range(256):
attempt = bytes(b ^ key for b in encrypted[:3])
if attempt == b'gg{':
print(f"Lykill: {hex(key)}")
Algengar aðgerðir og hvernig við snúum þeim við
| Aðgerð | Hvernig snúa við |
|---|---|
XOR key |
Beita XOR-aðgerð aftur með sama lykli |
base64.b64encode |
base64.b64decode |
rot13 |
codecs.decode(..., 'rot_13') aftur |
s[::-1] (reverse) |
s[::-1] aftur |
chr(ord(c) + n) |
chr(ord(c) - n) |
sorted(s) |
Yfirleitt ekki andhverfanlegt nema upplýsingar um upprunalegu röðina hafi varðveist |
| Þekkt umröðun | Beita andhverfu umröðuninni |
Föll, lokanir og fönguð gildi
Föll í Python eru hlutir. Innra fall getur haldið í breytur úr ytra fallinu, jafnvel eftir að ytra fallið hefur lokið keyrslu. Slíkt innra fall kallast lokun (e. closure).
def wrap(value, inside):
def doll():
return value, inside
return doll
inner = wrap("B", None)
outer = wrap("A", inner)
Hér geymir outer bæði strenginn "A" og fallið inner. Hægt er að sjá nöfn fönguðu breytanna í outer.__code__.co_freevars og gildin í outer.__closure__:
captured = {
name: cell.cell_contents
for name, cell in zip(outer.__code__.co_freevars, outer.__closure__)
}
print(captured)
Þegar lokanir eru hreiðraðar getum við fylgt innra fallinu áfram eins og dúkku inni í annarri dúkku.
Z3 (SMT-leysir)
Fyrir flóknar skorður getur Python-bókasafnið Z3 leyst jöfnur sjálfkrafa:
from z3 import *
x = Int('x')
s = Solver()
s.add(x * 3 + 7 == 100)
s.check()
print(s.model()) # x = 31
Æfingar
Verkefni til að æfa Python-bakhönnun:
- Matryoshka: Við rekjum gögn í gegnum nokkur lög af Python-föllum.
- Pandora's Box: Við greinum torlæstan kóða á öruggan hátt án þess að keyra hann í blindni.
- Fossil: Við komumst að því hvernig forritið virkar út frá þýddri Python-skrá þegar frumkóðinn fylgir ekki.
- The Sphinx: Við leysum verkefni þar sem mörg skilyrði þurfa að standast samtímis.