OhNativeEnc
根据提示直接分析so文件
发现是XXTEA加密
密钥:
密文:
解密如下
python
from struct import pack, unpack
# --- 配置区: 若需要请按实际内存填入(32 字节 hex, 空格分隔或逗号分隔均可) ---
CIPHERTEXT_HEX = "B6 53 6E 4D 77 5D 08 D2 FB 2C 63 1E BB 7B 01 9B F5 04 6A F4 0E 84 27 47 64 A1 E4 D9 EF 12 44 37"
KEY_BYTES = b"ThisIsAXXteaKey\x00" # 确认为 16 字节(你已经确认末尾为 0)
# ------------------ 下面为实现(无需修改) ------------------
mask32 = 0xFFFFFFFF
def hextobytes(s):
s = s.replace("0x"," ").replace(",", " ").strip()
parts = [p for p in s.split() if p]
return bytes(int(p,16) for p in parts)
# parse ciphertext
ct = hextobytes(CIPHERTEXT_HEX)
if len(ct) != 32:
raise SystemExit("CIPHERTEXT must be exactly 32 bytes (32 hex bytes).")
# interpret as 8 uint32 little-endian words (mapping matches binary: dest[0..3] -> W0 etc.)
W = list(unpack("<8I", ct))
# parse key into 4 uint32 little-endian words
if len(KEY_BYTES) != 16:
raise SystemExit("KEY_BYTES must be exactly 16 bytes.")
KEY = list(unpack("<4I", KEY_BYTES))
# helper F function (matches forward update used in binary)
def F(a, b, i_val, kx):
# (((a >> 5) ^ (4 * b)) + ((b >> 3) ^ (16 * a))) ^ ((i ^ b) + (kx ^ a))
part1 = ((a >> 5) ^ ((4 * b) & mask32)) & mask32
part2 = ((b >> 3) ^ ((16 * a) & mask32)) & mask32
sum12 = (part1 + part2) & mask32
part3 = ((i_val ^ b) + ((kx ^ a) & mask32)) & mask32
return (sum12 ^ part3) & mask32
# Build reverse deltas: forward loop used i = 114514 * k for k=1..12.
deltas = [114514 * k for k in range(12, 0, -1)] # 12..1
# Make a local copy to operate on
v = W[:] # v[0]..v[7]
for i in deltas:
v13 = (i >> 2) & 3
v14 = KEY[v13]
# reverse updates in exact reverse order of forward encryption
# forward order (from asm snippet): v5(v0) += ..., v6(v1) += ..., v11(v2) += ..., v7(v3) += ...,
# v8(v4) += ..., v9(v5) += ..., v10(v6) += ..., v4(v7) += ...
# reverse subtract in opposite order:
# reverse v7:
tmp = F(v[6], v[0], i, KEY[v13 ^ 3])
v[7] = (v[7] - tmp) & mask32
# reverse v6:
tmp = F(v[5], v[7], i, KEY[v13 ^ 2])
v[6] = (v[6] - tmp) & mask32
# reverse v5:
tmp = F(v[4], v[6], i, KEY[v13 ^ 1])
v[5] = (v[5] - tmp) & mask32
# reverse v4:
tmp = F(v[3], v[5], i, v14)
v[4] = (v[4] - tmp) & mask32
# reverse v3:
tmp = F(v[2], v[4], i, KEY[v13 ^ 3])
v[3] = (v[3] - tmp) & mask32
# reverse v2:
tmp = F(v[1], v[3], i, KEY[v13 ^ 2])
v[2] = (v[2] - tmp) & mask32
# reverse v1:
tmp = F(v[0], v[2], i, KEY[v13 ^ 1])
v[1] = (v[1] - tmp) & mask32
# reverse v0:
tmp = F(v[7], v[1], i, v14)
v[0] = (v[0] - tmp) & mask32
# pack back to bytes
plain = pack("<8I", *v)
# print results
print("Recovered plaintext (hex):", plain.hex())
# printable ASCII or escaped
def printable_ascii(b):
try:
s = b.decode('ascii')
except:
return None
# check for printable range 0x20..0x7e or newline/tab allowed
if all((0x20 <= ord(ch) <= 0x7e) or ch in "\r\n\t" for ch in s):
return s
return None
s = printable_ascii(plain)
if s is not None:
print("Recovered plaintext (ASCII, printable):")
print(s)
else:
# show escaped bytes and printable-chars-with-escapes
print("Recovered plaintext contains non-printable bytes. Escaped form:")
print(''.join('\\x{:02x}'.format(b) for b in plain))
# also show a mixed printable representation
mixed = ''.join((chr(b) if 0x20 <= b <= 0x7e else '\\x{:02x}'.format(b)) for b in plain)
print("Mixed view:", mixed)
尤皮·埃克斯历险记(1)
查壳发现有upx壳,无魔改,用UPX工具直接脱壳即可,对脱壳后文件分析
主要逻辑如下,其中v19是我们的输入,v21是密文,长度为34
分析可知,先对我们的输入经过一个encrypt函数加密,然后进行调试检测,如果没有检到debug,就将encrypt的处理结果异或上0x3c
跟进分析encrypt,发现是对输入进行一系列变换
对大小写字符(-69-v24)
对数字(105-v24)
不对符号进行变换,写脚本解密即可
python
flag=""
enc=[0x69, 0x73, 0x66, 0x68, 0x47, 0x4A, 0x09, 0x74, 0x7E, 0x63, 0x55, 0x0A, 0x79, 0x0A, 0x75, 0x54,0x6A, 0x63, 0x6A, 0x09, 0x54, 0x7E, 0x63, 0x6A, 0x51, 0x64, 0x75, 0x7E, 0x77, 0x7B, 0x04, 0x05,0x71, 0x41]
print(len(enc))
for x in enc:
flag+=chr(x^0x3c)
for c in flag:
if(ord(c)>0x39):
if((c>="a" and c<="z")or(c>="A" and c<="Z")):
print(chr((-ord(c)-69)&0xff),end='')
else:
print(c,end='')
else:
print(chr((105-ord(c))&0xff),end='')
flag{E4sy_R3v3rSe_e4Sy_eNcrypt10n}
Look at me carefully
可以发现是对输入经过函数sub_4016E0变换,然后和v7字符串比较
打断点查看加密后的结果,结果多次调试,可以发现本质上是进行索引重排
输入
加密结果
找到序列即可
patch修改我们的输入如下
![[Pasted image 20251008172040.png]]
运行到加密结束
提取到下标列表,重排解密即可
python
flag=""
index1=[ 0x1C, 0x06, 0x07, 0x0A, 0x1D, 0x13, 0x21, 0x1E, 0x05, 0x0C,
0x10, 0x12, 0x17, 0x09, 0x23, 0x11, 0x14, 0x08, 0x1B, 0x24,
0x03, 0x0F, 0x16, 0x01, 0x02, 0x1A, 0x0E, 0x18, 0x15, 0x1F,
0x22, 0x0B, 0x04, 0x0D, 0x19, 0x20]
enc="cH4_1elo{ookte?0dv_}alafle___5yygume"
for i in range(36):
c=index1.index(i+1)
flag+=enc[c]
print(flag)
flag{H4ve_you_lo0ked_at_me_c1o5ely?}
采一朵花,送给艾达(1)
手动去除花指令

去除完main函数的花指令后,查看伪代码
发现是RC4加密,先看一下rc4_init,去除花指令后发现有魔改
再看rc4_crypt,发现也魔改了
key和密文在main中已给出,修改脚本直接解密即可
c
#include<stdio.h>
#include<string.h>
typedef unsigned longULONG;
//初始化函数
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++)
{
s[i] = -i;
k[i] = key[i % Len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; // 交换s[i]和s[j]
s[j] = tmp;
}
}
//加解密
void rc4_crypt(unsigned char* s, unsigned char* Data, unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; // 交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] -= s[t];
}
}
int main()
{
unsigned char s[256] = { 0 }, s2[256] = { 0 }; // S-box
char key[256] = { "EasyJunkCodes" };
char pData[] = { 0xC7, 0x7F, 0xC1, 0x43, 0x03, 0x64, 0x75, 0x11,0x88, 0xB8, 0x8C, 0x55, 0xF6, 0xC0, 0x23, 0xDF,
0x4D, 0x0F, 0x2E, 0x9E, 0xF6, 0x82, 0xF0, 0xF2 ,0xBC, 0x51, 0x6B, 0x08, 0x29, 0x83, 0x27, 0xE1 ,0xCB, 0xBD, 0xC6, 0x88, 0xB1, 0x80, 0x4F, 0x4E };
unsigned long len = strlen(pData);
int i;
printf("pData=%s\n", pData);
printf("key=%s,length=%d\n\n", key, strlen(key));
rc4_init(s, (unsigned char*)key, strlen(key)); // 已经完成了初始化
printf("\n\n");
for (i = 0; i < 256; i++) // 用s2[i]暂时保留经过初始化的s[i],很重要的!!!
{
s2[i] = s[i];
}
//可以看到,加解密函数都是相同的
printf("已经加密,现在解密:\n\n");
rc4_crypt(s2, (unsigned char*)pData, len); // 解密
printf("pData=%s\n\n", pData);
//如果需要得到16进制数组
//printf("解密后pData(16进制): ");
//for (i = 0; i < len; i++) {
// printf("%02X ", pData[i]);
//}
return 0;
}
flag{Junk_C0d3s_4Re_345y_t0_rEc0gn1Ze!!}
Forgotten_Code
汇编,可以手撕,也可以直接丢AI
里面有个比较容易错的地方,就是密钥每次都会异或上17,所以第一三块的解密密钥和二四块的解密密钥不同
python
#!/usr/bin/env python3
# verify_chal.py
# 验证脚本:严格模拟汇编行为(ng 每次调用 fn 前 ^=17;fn 为 TEA 加密 32 轮)
# 用法:
# python verify_chal.py "flag{...}"
# 或直接运行:
# python verify_chal.py
# 会用内置 candidate 测试
import sys
import struct
# 原始 ng 数据(和汇编中完全一致)
NG_INITIAL = bytearray(b"sp\x7fvuctp|xeb|hv~") # 16 bytes, includes 0x7F
# ezgm 常量(按汇编给出)。这里转换为 32-bit 无符号整数数组
EZGM = [
1210405119, 710975774,
-90350153, -1958008304,
-745722482, 67707510,
-86515270, -1728462407
]
EZGM_U = [x & 0xFFFFFFFF for x in EZGM]
# TEA constants
DELTA = 0x9E3779B9
ROUNDS = 32
MASK32 = 0xFFFFFFFF
def ng_xor17(ng):
"""就地对 ng 的 16 字节逐字节 ^= 17(与汇编一致)"""
for i in range(16):
ng[i] ^= 17
def tea_encrypt_block(v0, v1, key_words):
"""TEA 加密(32 轮),返回 (v0', v1')。与汇编中 v0+=... v1+=... 顺序一致。"""
v0 &= MASK32
v1 &= MASK32
sum_ = 0
k0, k1, k2, k3 = key_words
for _ in range(ROUNDS):
sum_ = (sum_ + DELTA) & MASK32
# v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1)
t1 = ( (v1 << 4) + k0 ) & MASK32
t2 = (v1 + sum_) & MASK32
t3 = ( (v1 >> 5) + k1 ) & MASK32
v0 = (v0 + ((t1 ^ t2) ^ t3)) & MASK32
# v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3)
u1 = ( (v0 << 4) + k2 ) & MASK32
u2 = (v0 + sum_) & MASK32
u3 = ( (v0 >> 5) + k3 ) & MASK32
v1 = (v1 + ((u1 ^ u2) ^ u3)) & MASK32
return v0, v1
def tea_decrypt_block(v0, v1, key_words):
"""TEA 解密(逆运算),用于从 ezgm 反推原始明文块"""
v0 &= MASK32
v1 &= MASK32
sum_ = (DELTA * ROUNDS) & MASK32
k0, k1, k2, k3 = key_words
for _ in range(ROUNDS):
v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + sum_) ^ ((v0 >> 5) + k3))) & MASK32
v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + sum_) ^ ((v1 >> 5) + k1))) & MASK32
sum_ = (sum_ - DELTA) & MASK32
return v0, v1
def get_key_words_from_ng(ng):
"""从 ng 当前状态按小端读出 4 个 uint32 作为 TEA key"""
return [int.from_bytes(bytes(ng[i:i+4]), 'little') for i in range(0, 16, 4)]
def fn_simulate_encrypt(ng, block_bytes):
"""
模拟 _Z2fnPj 的行为(加密流程):
- 函数一开始对 ng 做 ^=17(就地)
- 读取 key(4 * uint32 小端)
- 将 block_bytes(8字节)拆为 v0,v1(小端 uint32)
- 用 TEA 加密得到 v0',v1'
- 返回 (v0', v1')
"""
# toggle ng first (汇编每次进入 fn 都执行该操作)
ng_xor17(ng)
key_words = get_key_words_from_ng(ng)
v0 = int.from_bytes(block_bytes[0:4], 'little')
v1 = int.from_bytes(block_bytes[4:8], 'little')
return tea_encrypt_block(v0, v1, key_words)
def verify_flag(candidate):
"""验证 candidate 是否为正确 flag(按照汇编逻辑)"""
if not candidate.startswith("flag{") or not candidate.endswith("}"):
print("Invalid flag format!")
return False
inner = candidate[5:-1]
if len(inner) != 32:
print("Wrong length! inner length =", len(inner))
return False
# working copy of ng (mutable)
ng = bytearray(NG_INITIAL)
# for each 8-byte block, simulate fn (encrypt) and compare to ezgm
for i in range(4):
offset = i * 8
block = inner.encode('latin1')[offset:offset+8] # latin1 preserves raw bytes 0-255
if len(block) != 8:
print("Block", i, "too short")
return False
out_v0, out_v1 = fn_simulate_encrypt(ng, block)
expected_v0 = EZGM_U[i*2]
expected_v1 = EZGM_U[i*2+1]
if out_v0 != expected_v0 or out_v1 != expected_v1:
print(f"Block {i} mismatch:")
print(f" simulated -> v0={hex(out_v0)}, v1={hex(out_v1)}")
print(f" expected -> v0={hex(expected_v0)}, v1={hex(expected_v1)}")
return False
print("Right! flag accepted")
return True
def reconstruct_inner_from_ezgm():
"""
从 ezgm 反推出 inner 32 字节(按汇编交替 key)
注意:汇编中每次调用 fn 时都会先对 ng ^=17,因此在加密时 key 是交替的。
当我们解密 ezgm (密文)时,需要按相同的顺序交替使用 ng 状态来解密。
"""
ng = bytearray(NG_INITIAL)
recovered = bytearray()
for i in range(4):
# 在程序中,fn 在加密时会在每次进入时调用 ng ^=17,得到用于该次加密的 key。
# 要解密 ezgm[i],我们需要用对应时刻的 key(即调用前执行了 ng ^=17 的 ng)。
ng_xor17(ng) # toggle to get the key used when encryption ran for this block
key_words = get_key_words_from_ng(ng)
ev0 = EZGM_U[i*2]
ev1 = EZGM_U[i*2+1]
dv0, dv1 = tea_decrypt_block(ev0, ev1, key_words)
recovered += dv0.to_bytes(4, 'little') + dv1.to_bytes(4, 'little')
return bytes(recovered)
if __name__ == "__main__":
if len(sys.argv) > 1:
candidate = sys.argv[1]
else:
# 默认 candidate(可替换为你想测试的字符串)
candidate = "flag{4553m81y_5_s0o0o0_345y_jD5yQ5mD9}"
print("No candidate provided; testing default:", candidate)
ok = verify_flag(candidate)
if not ok:
# If verification fails, print expected inner 32 bytes (hex / latin1)
expected_inner = reconstruct_inner_from_ezgm()
print("\n-- Expected inner 32 bytes (hex):", expected_inner.hex())
print("-- Expected inner 32 bytes (latin1 view):", expected_inner.decode('latin1'))