SUCTF2018 HelloPython

密文 f1f5d29b6e4414ec 加密程序已给出,请计算明文, 明文不包含flag格式SUCTF{},提交时请手动补全encrypt

pyc逆向,这种古早的技术使用现成工具 uncompyle6

https://github.com/rocky/python-uncompyle6#installation

开个venv直接pip install uncompyle6

# uncompyle6 version 3.9.3
# Python bytecode version base 2.7 (62211)
# Decompiled from: Python 3.14.2 (tags/v3.14.2:df79316, Dec 5 2025, 17:18:21) [MSC v.1944 64 bit (AMD64)]
# Embedded file name: encrypt_ol.py
# Compiled at: 2018-11-01 21:19:40
(lambda __operator, __print, __g, __contextlib, __y: [(lambda __mod: [[[(lambda __items, __after, __sentinel: __y((lambda __this: (lambda : (lambda __i: [(lambda __out: (lambda __ctx: [__ctx.__enter__(), __ctx.__exit__(None, None, None), __out[0]((lambda : __this()))][2])(__contextlib.nested(type('except', (), {'__enter__': (lambda self: None), '__exit__': (lambda __self, __exctype, __value, __traceback: __exctype is not None and [True for __out[0] in [(sys.exit(0), (lambda after: after()))[1]]][0])})(), type('try', (), {'__enter__': (lambda self: None), '__exit__': (lambda __self, __exctype, __value, __traceback: [False for __out[0] in [(v.append(int(word, 16)), (lambda __after: __after()))[1]]][0])})())))([None]) for __g['word'] in [__i]][0] if __i is not __sentinel else __after())(next(__items, __sentinel)))))())(iter(p_text.split('_')), (lambda : [[[[[[[(lambda __after: __y((lambda __this: (lambda : (lambda __target: [(lambda __target: [(lambda __target: [[__this() for __g['n'] in [__operator.isub(__g['n'], 1)]][0] for __target.value in [__operator.iadd(__target.value, (y.value << 4) + k[2] ^ y.value + x.value ^ (y.value >> 5) + k[3])]][0])(z) for __target.value in [__operator.iadd(__target.value, (z.value << 4) + k[0] ^ z.value + x.value ^ (z.value >> 5) + k[1])]][0])(y) for __target.value in [__operator.iadd(__target.value, u)]][0])(x) if n > 0 else __after())))())((lambda : [[(__print(('').join(map(hex, w)).replace('0x', '').replace('L', '')), None)[1] for w[1] in [z.value]][0] for w[0] in [y.value]][0])) for __g['w'] in [[0, 0]]][0] for __g['n'] in [32]][0] for __g['u'] in [2654435769]][0] for __g['x'] in [c_uint32(0)]][0] for __g['z'] in [c_uint32(v[1])]][0] for __g['y'] in [c_uint32(v[0])]][0] for __g['k'] in [[3735928559, 590558003, 19088743, 4275878552]]][0]), []) for __g['v'] in [[]]][0] for __g['p_text'] in [raw_input('plain text:\n> ')]][0] for __g['c_uint32'] in [__mod.c_uint32]][0])(__import__('ctypes', __g, __g, ('c_uint32', ), 0)) for __g['sys'] in [__import__('sys', __g, __g)]][0])(__import__('operator', level=0), __import__('__builtin__', level=0).__dict__['print'], globals(), __import__('contextlib', level=0), (lambda f: (lambda x: x(x))((lambda y: f((lambda : y(y)()))))))
return

直接猜吧,这里类似tea的加密

(y.value << 4) + k[2] ^ y.value + x.value ^ (y.value >> 5) + k[3])]][0])(z) for __target.value in [__operator.iadd(__target.value, (z.value << 4) + k[0] ^ z.value + x.value ^ (z.value >> 5) + k[1])]][0])(y) for __target.value in 

tea的密钥

for __g['k'] in [[3735928559, 590558003, 19088743, 4275878552]]][0])

重要特征的magic number

for __g['u'] in [2654435769]]
>>> hex(2654435769)
'0x9e3779b9'

这里是输入

[raw_input('plain text:\n> ')]][0] for __g['c_uint32'] in [__mod.c_uint32]][0])

解题脚本

import ctypes


def encrypt(v, k):
v0 = ctypes.c_uint32(v[0]).value
v1 = ctypes.c_uint32(v[1]).value

delta = 0x9E3779B9
sum_value = 0

k0, k1, k2, k3 = k

for _ in range(32):
sum_value = ctypes.c_uint32(sum_value + delta).value

v0 = ctypes.c_uint32(
v0 + (
((v1 << 4) + k0)
^ (v1 + sum_value)
^ ((v1 >> 5) + k1)
)
).value

v1 = ctypes.c_uint32(
v1 + (
((v0 << 4) + k2)
^ (v0 + sum_value)
^ ((v0 >> 5) + k3)
)
).value

v[0] = v0
v[1] = v1


def decrypt(v, k):
v0 = ctypes.c_uint32(v[0]).value
v1 = ctypes.c_uint32(v[1]).value

delta = 0x9E3779B9
sum_value = 0xC6EF3720

k0, k1, k2, k3 = k

for _ in range(32):
v1 = ctypes.c_uint32(
v1 - (
((v0 << 4) + k2)
^ (v0 + sum_value)
^ ((v0 >> 5) + k3)
)
).value

v0 = ctypes.c_uint32(
v0 - (
((v1 << 4) + k0)
^ (v1 + sum_value)
^ ((v1 >> 5) + k1)
)
).value

sum_value = ctypes.c_uint32(sum_value - delta).value

v[0] = v0
v[1] = v1


def main():
v = [0xf1f5d29b, 0x6e4414ec]

k = [
3735928559,
590558003,
19088743,
4275878552
]

decrypt(v, k)

print(f"{v[0]:08x}_{v[1]:08x}")


if __name__ == "__main__":
main()
'''
6ba18492_34f9b252
'''

NewStarCTF 2023 Petals

或许是常规ELF逆向?

image-20260519132442714

逻辑很简单就是有写混淆

image-20260519132600247

loc_1209

image-20260519132701903

image-20260519132857665

得到校验,就是一个初始化然后换表

unsigned __int64 __fastcall sub_1209(char *g_input, unsigned int len)
{
int i; // [rsp+18h] [rbp-118h]
unsigned int j; // [rsp+1Ch] [rbp-114h]
char v5[256]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
memset(v5, 0, sizeof(v5));
for ( i = 0; i <= 255; ++i )
v5[i] = ~(i ^ len);
for ( j = 0; len > j; ++j )
g_input[j] = v5[(unsigned __int8)g_input[j]];
return v6 - __readfsqword(0x28u);
}

编写解题flag

from hashlib import md5
check = [
0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89,
0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1,
0xD2, 0x82, 0xD3, 0xDE, 0x87
]

v5 = []
for i in range(256):
v5.append(0)
for i in range(256):
v5[i] = (~(i ^ 25)) & 0xFF

flag = []

for i in range(25):
flag.append(v5[check[i]])

flagstr = ''.join(chr(i) for i in flag)
print(flagstr)
print("flag{{{}}}".format(md5(flagstr.encode()).hexdigest()))

'''
66ccff#luotianyi#b074d58a
flag{d780c9b2d2aa9d40010a753bc15770de}
'''

image-20260519133947311

NewStarCTF BabyRe

linux逆向,题目描述说

程序真的都是从main开始的吗?

image-20260517123337908

代码逻辑很简单,输入后进行异或然后比较

image-20260517123856596

image-20260517123841954

根据提示看看 start,从ELF文件格式的入口点开始,这个在Dectect It Easy那张图可以看到

image-20260517124032586

这里有个问题就是函数的执行顺序了

https://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-generic/LSB-generic/baselib---libc-start-main-.html

The __libc_start_main() function shall perform any necessary initialization of the execution environment, call the *main* function with appropriate arguments, and handle the return from main(). If the main() function returns, the return value shall be passed to the exit() function.

Note: While this specification is intended to be implementation independent, process and library initialization may include:

  • performing any necessary security checks if the effective user ID is not the same as the real user ID.
  • initialize the threading subsystem.
  • registering the *rtld_fini* to release resources when this dynamic shared object exits (or is unloaded).
  • registering the *fini* handler to run at program exit.
  • calling the initializer function (**init*)().
  • calling main() with appropriate arguments.
  • calling exit() with the return value from main().

This list is an example only.

所以先执行的是 _libc_csu_init

void __fastcall _libc_csu_init(unsigned int a1, __int64 a2, __int64 a3)
{
signed __int64 i_1; // rbp
__int64 i; // rbx

init_proc();
i_1 = &_do_global_dtors_aux_fini_array_entry - _frame_dummy_init_array_entry;
if ( i_1 )
{
for ( i = 0; i != i_1; ++i )
(_frame_dummy_init_array_entry[i])(a1, a2, a3);
}
}

_frame_dummy_init_array_entry是一个函数数组列表,然后挨个执行

image-20260517124443442

image-20260517124451924

image-20260517124503637

就是这个修改了flag

final = [0x66, 0x6D, 0x63, 0x64, 0x7F, 0x56, 0x69, 0x6A, 0x6D, 0x7D,
0x62, 0x62, 0x62, 0x6A, 0x51, 0x7D, 0x65, 0x7F, 0x4D, 0x71,
0x71, 0x73, 0x79, 0x65, 0x7D, 0x46, 0x77, 0x7A, 0x75, 0x73,
0x21, 0x62]

final[6] = 54
final[11] = 58
final[22] = 38
final[30] = 63
flag = []
for i in range(len(final)):
flag.append(i ^ final[i])

print(''.join(chr(i) for i in flag))

'''
flag{S0meth1ng_run_bef0re_main!}
'''

DASCTF 2024 EZRE

No.32位有壳,而且还有反调试(壳自带的)

image-20260516151056635

image-20260516152128293

直接用已经有的脱壳器

https://github.com/ergrelet/unlicense/releases

image-20260516152916291

静态分析上来就是一个花指令

image-20260516160441850

之后几个loc的call依旧存在花指令导致ida无法识别出函数

image-20260516153921397

image-20260516154226317

参考之前的手动patch直接写ida脚本吧


#include<idc.idc>
static main()
{
auto StartVa, StopVa, Size, i, j;
StartVa = 0xA21000;
StopVa = 0xA23000;
Size = StopVa - StartVa;
for (i = 0; i < Size; i++) {
if (Byte(StartVa) == 0x74
&& Byte(StartVa + 1) == 0x03
&& Byte(StartVa + 2) == 0x75
&& Byte(StartVa + 3) == 0x00)
{
PatchByte(StartVa, 0xEB);
PatchByte(StartVa + 1, 0x03);
for (j = 0; j < 3; j++) {
PatchByte(StartVa + 2 + j, 0x90);
}
MakeCode(StartVa);
StartVa++;
Message("Find Fakereturn Opcode!!\n");
continue;
}
StartVa++;
}
Message("Clear Fakereturn Opcode Ok\n");
}

去除花指令后如下内容需要ida手动转为汇编重新识别,重新识别过后才能创建为函数

image-20260516160737445

image-20260516160747621

这时候的main函数就很好看了

image-20260516160925551

sub_A21290(input, a2) -> sub_A210C0(),rc4的密钥拓展算法中的sbox初始化和密钥调度算法

image-20260516161449067

然后就是稍微变化过的RC4加密了,不过RC4是完全对称加密,加密/解密函数都是同一个,直接抄就行了,要改的就是+改为-

image-20260516162106953

接着的sub_A213C0根据魔数一眼xtea魔改(循环次数),密钥也给出了。

image-20260516162953049

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <stdint.h>

unsigned char S[256];

void swap(unsigned char* a, unsigned char* b) {
unsigned char temp = *a;
*a = *b;
*b = temp;
}

void rc4_key_setup(unsigned char* key, int key_length, unsigned char S[256]) {
unsigned char T[256];
int i, j;

for (i = 0; i < 256; i++) {
S[i] = i;
T[i] = key[i % key_length];
}

j = 0;
for (i = 0; i < 256; i++) {
j = (j + S[i] + T[i]) % 256;
swap(&S[i], &S[j]);
}
}

char __cdecl sub_121290(char* a1, char* a2)
{
char result; // al
unsigned int i; // [esp+18h] [ebp-10h]
char* v4; // [esp+1Ch] [ebp-Ch]
unsigned __int8 v6; // [esp+26h] [ebp-2h]
unsigned __int8 v7; // [esp+27h] [ebp-1h]

v7 = 0;
v6 = 0;
int k = 0;
for (i = 0; i < 44; ++i)
{
v6 += S[++v7];
swap(&S[v7], &S[v6]);
// 解密逻辑
a2[i] = a1[i] - (S[(unsigned char)(S[v6] + S[v7])] ^ 0x33);
// 加密逻辑
//a2[i] = (S[(unsigned char)(S[v6] + S[v7])] ^ 0x33) + a1[i];
}
return 0;
}

char* __cdecl sub_1213C0(uint32_t* a1)
{
char* result; // eax
int v2[4]; // [esp+Ch] [ebp-24h]
int v3; // [esp+1Ch] [ebp-14h]
int i; // [esp+20h] [ebp-10h]
uint32_t v5; // [esp+24h] [ebp-Ch]
uint32_t v6; // [esp+28h] [ebp-8h]
unsigned int v7; // [esp+2Ch] [ebp-4h]

v2[0] = 1855465527;
v2[1] = 1144201745;
v2[2] = 287454020;
v2[3] = 925407342;
v6 = a1[0];
v5 = a1[1];
// 循环了33次,这里要乘以33.
v7 = 1719109785 + (0x9E3779B8 * 33);
v3 = 0x9E3779B8;
for (i = 0; i <= 32; ++i)
{
v5 -= (v2[(v7 >> 11) & 3] + v7) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
v7 -= v3;
v6 -= (v2[v7 & 3] + v7) ^ (v5 + ((v5 >> 6) ^ (32 * v5)));
}
a1[0] = v6;
a1[1] = v5;
return 0;
}


int main() {
unsigned char key[] = "th0s_i0_ke9";
int key_length = strlen((const char*)key);

// 初始化S盒
rc4_key_setup(key, key_length, S);

unsigned char encenc[] = { 0x50, 0xD4, 0xC8, 0xC4, 0x8F, 0x84, 0x40, 0xEB, 0x32, 0x81, 0x8F, 0x85, 0x6C, 0xB2, 0x2B, 0x06,
0xBF, 0x05, 0x35, 0x5D, 0x2E, 0xE3, 0x7D, 0x46, 0x8D, 0x35, 0x01, 0x70, 0x3A, 0x80, 0x81, 0xC5,
0xE6, 0x71, 0xD3, 0xD6, 0x50, 0x69, 0x6F, 0xE2, 0x6E, 0x78, 0x14, 0xD8, 0x25 };

// XTEA解密
for (int i = 36; i >= 0; i--)
{
sub_1213C0((uint32_t*)&encenc[i]);
}

// RC4解密
char encrypted_data[44];
sub_121290((char*)encenc, encrypted_data);

printf("%s\n", encrypted_data);
return 0;
}

//DASCTF{Th1l_t8e1a_rc4_l8s_s8o_int9r3es4t1ng}

SWPU2019 easyRE

image-20260514221624052

sub_40EF90

BOOL sub_40EF90()
{
HANDLE CurrentProcess; // eax
NTSTATUS (__stdcall *NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); // [esp+0h] [ebp-14h]
HMODULE hModule; // [esp+4h] [ebp-10h]
int ProcessInformation_; // [esp+Ch] [ebp-8h] BYREF

ProcessInformation_ = 0;
hModule = LoadLibraryA("Ntdll.dll");
NtQueryInformationProcess = (NTSTATUS (__stdcall *)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(hModule, "NtQueryInformationProcess");
CurrentProcess = GetCurrentProcess();
NtQueryInformationProcess(CurrentProcess, ProcessDebugPort, &ProcessInformation_, 4, 0);
return ProcessInformation_ != 0;
}

导出NtQueryInformationProcess然后查询,作用是反调试

https://learn.microsoft.com/zh-cn/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess

image-20260514222037968

回到主函数

sub_401FE0memset_w都有this指针,很经典的c++逆向,技巧就是把class当作struct去解析

memset_w可以知道class的大小大致为0x6C,sub_401FE0中尝试画出struct,暂时逆向为

struct NewClass
{
void *vtable;
DWORD *dwPtr;
char unknown[44];
char str[40];
char unknown2[12];
void *somePtr;
};

image-20260514223427023

sub_402DE0说明somePtr指向另外一个class,sub_40F360也可以说明。

image-20260514223745636

发现和NewClass很类似,索性暂时为这个类型,再小修一下

struct NewClass
{
void *vtable;
DWORD *dwPtr;
void *ptr;
char unknown[40];
char str[40];
char unknown2[12];
NewClass *OtherClass;
};

image-20260514224550671

sub_40F150,真正的main函数

image-20260514225139139

sub_4024B0是校验函数,调用了虚函数进行校验

image-20260514225452463

直接把反调试patch了然后看最后的vtable长啥样,计算完偏移过后得到0x4124E4

image-20260514231020874

image-20260514231449909

image-20260514233829758

image-20260514232635632

image-20260514234206958

image-20260514234016392

根据调用顺序就是(按照加减法的话应该是func0开始编号)

BOOL __thiscall checker(finalVtable *this, char *str)
{
BOOL result; // eax

this->funcPtr[2] = str;
result = 0;
if ( func4(this) )
{
func7(this);
if ( func11(this) )
return 1;
}
return result;
}

func4:

int __thiscall func4(const char **this)
{
int v2; // [esp+Ch] [ebp-B0h]
unsigned int v3; // [esp+14h] [ebp-A8h]
int i; // [esp+24h] [ebp-98h]
_BYTE v6[56]; // [esp+30h] [ebp-8Ch] BYREF
_BYTE v7[20]; // [esp+68h] [ebp-54h] BYREF
char Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm[48]; // [esp+7Ch] [ebp-40h] BYREF
int v9; // [esp+B8h] [ebp-4h]

v3 = (unsigned int)&this[2][strlen(this[2])];
strcpy(Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm, "Ncg`esdvLkLgk$mL=Lgk$mL=Lgk$mL=Lgk$mL=Lgk$mLm");
newClassObject(v6, 0x38u);
sub_402B00();
v9 = 0;
for ( i = 0; i < 45; ++i )
Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm[i] ^= 0x10u;
newClassObject(v7, 0x14u);
sub_402A70(Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm, 1);
LOBYTE(v9) = 1;
v2 = (unsigned __int8)sub_404260(this[2], v3, v6, v7, 0);
LOBYTE(v9) = 0;
sub_402A50(v7);
v9 = -1;
sub_4026A0(v6);
return v2;
}

编写该阶段flag.py,结果是一个正则匹配

func4 = b"Ncg`esdvLkLgk$mL=Lgk$mL=Lgk$mL=Lgk$mL=Lgk$mLm"
func4dec = []
for i in func4:
func4dec.append(i ^ 0x10)

print(''.join(chr(i) for i in func4dec))
'''
^swpuctf\{\w{4}\-\w{4}\-\w{4}\-\w{4}\-\w{4}\}
swpuctf{1234-1234-1234-1234-1234}
swpuctf{1234-5678-abcd-1234-5678}
'''

image-20260514235650008

通过了检查,果然是三分逆向七分猜,不跑跑脚本可能都想不到。

接着看func7

int __thiscall func7(NewClass *this)
{
int i; // [esp+4h] [ebp-4h]

for ( i = 0; i < 5; ++i )
(*((void (__thiscall **)(NewClass *, int))this->vtable + 7))(this, i);
return (*((int (__thiscall **)(NewClass *))this->vtable + 9))(this);
}

整理一下就是

int __thiscall func7(finalVtable *this)
{
int i; // [esp+4h] [ebp-4h]

for ( i = 0; i < 5; ++i )
func8(this, i);
return func10(this);
}

关于func8,没啥思路,但是func7不影响循环,看看跑完是啥

image-20260515000107338

image-20260515000728146

感觉就是拆开,但是也看不出来啥,那看最后的 func11

int __thiscall func11(unsigned __int8 *this)
{
int i; // [esp+4h] [ebp-4h]

for ( i = 0; i < 40; ++i )
{
if ( this[i + 52] != this[i + 12] )
return 0;
}
return 1;
}

前40个和后40个相等,突然想起最开始逆向class的时候有一段固定40字节的长度

image-20260515001101532

整理得到

image-20260515002220745

# started40 = b"\b\xEAX\xDE\x94\xD0;\xBE\x88\xD42\xB6\x14\x82\xB7\xAF\x14T\x7F\xCF  03\"3   0 203\"   $ "
started40 = [0x8, 0xea, 0x58, 0xde, 0x94, 0xd0, 0x3b, 0xbe, 0x88, 0xd4, 0x32, 0xb6, 0x14, 0x82, 0xb7, 0xaf, 0x14, 0x54, 0x7f,
0xcf, 0x20, 0x20, 0x30, 0x33, 0x22, 0x33, 0x20, 0x20, 0x20, 0x30, 0x20, 0x32, 0x30, 0x33, 0x22, 0x20, 0x20, 0x20, 0x24, 0x20]

没招了只能回去看func7中的func8和func10

image-20260515093049949

难点是有段汇编

image-20260515093300503

带 CF 的循环左移,形成一个8 bit的循环左移

image-20260515095556251

不怕汇编,动态调试看看

从头开始取

image-20260515124857153

得到4个

image-20260515125118976

开始移位

image-20260515125214195

image-20260515125253059

image-20260515125324066

.text:004027A9                 rcl     dl, 1
.text:004027AB inc ebx
.text:004027AC jb short loc_4027A9
.text:004027AE mov [ebp+esi+leftRotTimes], bl
.text:004027B5 xor ebx, ebx

循环左移1位,但是rcl是带标志寄存器CF的,这里的布局是

[CF,1bit][char值, 8bit]
000110001
循环左移过后
001100010
CF依旧为0,不跳转

image-20260515125944901

循环左移两次就变成了11000100b = 0x88

image-20260515130421988


然后再循环右移,rcr也是带CF标志位的9bit循环右移

image-20260515130811449

也就是说

[CF,1bit][char值, 8bit]
000110001
循环右移过后
100011000

第一组
ebp-0x14 = 8bit二进制左边有多少个0再+1
ebp-0x10 = 8bit二进制右边有多少个0,没有再加了
一个字节完成后,ebp+=1

image-20260515131317229

将输入的subflag[i]的char左移刚才统计的n位,低位补0

image-20260515131658465

结果的低1字节放在栈上

image-20260515131932939

然后再循环n结果回去

image-20260515132039010

image-20260515132211185

image-20260515132219232

最后的结果

image-20260515132704052

res1 = 右边0的个数 + 左移进位次数;
res2 = (flag << 左移进位次数) & 0xff >> res1;
res3 = (flag << (8 - 0的个数)) | ((flag >> (8 - 左移次数)) << 左移次数);

最后的脚本

import string
func4 = b"Ncg`esdvLkLgk$mL=Lgk$mL=Lgk$mL=Lgk$mL=Lgk$mLm"
func4dec = []
for i in func4:
func4dec.append(i ^ 0x10)

print(''.join(chr(i) for i in func4dec))
'''
^swpuctf\{\w{4}\-\w{4}\-\w{4}\-\w{4}\-\w{4}\}
'''


def check_1(c): # 左移进位位数
num = 0
while True:
c = c << 1
num += 1
if c & 0x100:
return num


def check_0(c): # 右边0的个数
num = 0
while True:
if c & 1:
return num
num += 1
c = c >> 1


def generate_0(c):
res1 = check_0(c) + check_1(c)
res2 = ((c << check_1(c)) & 0xff) >> res1
res3 = ((c >> (8 - check_1(c))) << check_1(c)
) | ((c << (8 - check_0(c)) & 0xff) >> res1)
return [res1, res2, res3]


def check_part(c):
tmp = list(set(' 03\"3 0 203\" $ '))
tmp2 = check_0(c) | (16 * check_1(c))
for i in tmp:
if tmp2 == ord(i):
return i

return ''


def classify():
for_each = string.ascii_lowercase + string.ascii_uppercase + string.digits
second_part_res = ' 03\"3 0 203\" $ '
d = dict.fromkeys(list(set(second_part_res)))
for i in list(set(second_part_res)):
d[i] = []

for i in for_each:
tmp = check_part(ord(i))
if tmp:
d[tmp].append(i)

return d


def test_1(c, v14):
exam = {c: generate_0(ord(c))}
v14 = v14 - (8 - exam[c][0])
tmp = exam[c][1] << v14
return tmp, v14


def test_2(c, v14):
exam = {c: generate_0(ord(c))}
v14 = v14 - exam[c][0]
tmp = exam[c][2] << v14
return tmp, v14


def calc_first_part(s):
v14 = 0x20
tmp, v14 = test_1(s[0], v14)
tmp2, v14 = test_1(s[1], v14)
tmp3, v14 = test_1(s[2], v14)
tmp4, v14 = test_1(s[3], v14)
tmp5, v14 = test_2(s[0], v14)
tmp6, v14 = test_2(s[1], v14)
tmp7, v14 = test_2(s[2], v14)
tmp8, v14 = test_2(s[3], v14)
return tmp | tmp2 | tmp3 | tmp4 | tmp5 | tmp6 | tmp7 | tmp8


def check_first_part(second_part, first_part):
for i in d[second_part[0]]:
for j in d[second_part[1]]:
for k in d[second_part[2]]:
for m in d[second_part[3]]:
tmp = i + j + k + m
if calc_first_part(tmp) == first_part:
return tmp


d = classify()
s2 = ' 03\"3 0 203\" $ '
s = ['08', 'EA', '58', 'DE', '94', 'D0', '3B', 'BE', '88', 'D4',
'32', 'B6', '14', '82', 'B7', 'AF', '14', '54', '7F', 'CF']
flag = 'swpuctf{'
for i in range(0, 5):
first_part = int(s[3 + 4 * i]+s[2 + 4 * i]+s[1 + 4 * i]+s[4 * i], 16)
second_part = s2[i*4:i*4+4]
res = check_first_part(second_part, first_part)
if i == 4:
flag += res
break
flag += res + '-'
flag += '}'

print(flag)

watevrCTF 2019 Hacking_For_Vodka

linux的ELF逆向,用了ptrace

image-20260513134456070

当前进程标记自己为“被追踪”状态,声明由它的父进程来跟踪它。

那就是反调试

sub_92A里面有反校验算法,将其重命名为input_then_check

image-20260513135038328

很简单就可以写出还原算法

enckey1 = b"abcdefghijklmnopqrstuvwxyz_!{} "
decKey1 = []
for i in range(100):
decKey1.append(0)

decKey1[7] = enckey1[26] ^ 7
decKey1[2] = enckey1[11] ^ 2
decKey1[1] = enckey1[14] ^ 1
decKey1[0] = enckey1[11]
decKey1[9] = enckey1[7] ^ 9
decKey1[4] = enckey1[24] ^ 4
decKey1[8] = enckey1[19] ^ 8
decKey1[3] = enckey1[26] ^ 3
decKey1[16] = enckey1[8] ^ 0x10
decKey1[10] = enckey1[14] ^ 0xA
decKey1[5] = enckey1[14] ^ 5
decKey1[6] = enckey1[20] ^ 6
decKey1[19] = enckey1[22] ^ 0x13
decKey1[11] = enckey1[20] ^ 0xB
decKey1[22] = enckey1[26] ^ 0x16
decKey1[15] = enckey1[26] ^ 0xF
decKey1[12] = enckey1[6] ^ 0xC
decKey1[20] = enckey1[0] ^ 0x14
decKey1[13] = enckey1[7] ^ 0xD
decKey1[18] = enckey1[26] ^ 0x12
decKey1[14] = enckey1[19] ^ 0xE
decKey1[34] = enckey1[28] ^ 0x22
decKey1[24] = enckey1[7] ^ 0x18
decKey1[29] = enckey1[0] ^ 0x1D
decKey1[35] = enckey1[29] ^ 0x23
decKey1[21] = enckey1[18] ^ 0x15
decKey1[27] = enckey1[26] ^ 0x1B
decKey1[23] = enckey1[19] ^ 0x17
decKey1[25] = enckey1[0] ^ 0x19
decKey1[30] = enckey1[18] ^ 0x1E
decKey1[17] = enckey1[19] ^ 0x11
decKey1[32] = 0
decKey1[26] = enckey1[19] ^ 0x1A
decKey1[31] = enckey1[24] ^ 0x1F
decKey1[28] = enckey1[4] ^ 0x1C

for i in range(32):
print(chr(i ^ decKey1[i]), end="")
print()

encKey2 = b"abcdefghijklmnopqrstuvwxyz_!{} \x00abcdefghijklmnopqrstuvwxyz_!{} \x00"
decKey2 = []
for i in range(100):
decKey2.append(0)

decKey2[0] = encKey2[0]
decKey2[2] = encKey2[21] ^ 2
decKey2[3] = encKey2[5] ^ 3
decKey2[6] = encKey2[28] ^ 6
decKey2[13] = encKey2[26] ^ 0xD
decKey2[8] = encKey2[9] ^ 8
decKey2[5] = encKey2[18] ^ 5
decKey2[11] = encKey2[12] ^ 0xB
decKey2[1] = encKey2[1] ^ 1
decKey2[10] = encKey2[15] ^ 0xA
decKey2[9] = encKey2[34] ^ 9
decKey2[12] = encKey2[35] ^ 0xC
decKey2[47] = encKey2[5] ^ 0x2F
decKey2[16] = encKey2[3] ^ 0x10
decKey2[15] = encKey2[34] ^ 0xF
decKey2[4] = encKey2[19] ^ 4
decKey2[20] = encKey2[7] ^ 0x14
decKey2[23] = encKey2[16] ^ 0x17
decKey2[32] = encKey2[1] ^ 0x20
decKey2[24] = encKey2[18] ^ 0x18
decKey2[14] = encKey2[9] ^ 0xE
decKey2[18] = encKey2[31] ^ 0x12
decKey2[21] = encKey2[26] ^ 0x15
decKey2[31] = encKey2[9] ^ 0x1F
decKey2[22] = encKey2[6] ^ 0x16
decKey2[7] = encKey2[21] ^ 7
decKey2[34] = encKey2[12] ^ 0x22
decKey2[17] = encKey2[12] ^ 0x11
decKey2[19] = encKey2[15] ^ 0x13
decKey2[40] = encKey2[18] ^ 0x28
decKey2[26] = encKey2[20] ^ 0x1A
decKey2[33] = encKey2[3] ^ 0x21
decKey2[25] = encKey2[26] ^ 0x19
decKey2[29] = encKey2[22] ^ 0x1D
decKey2[27] = encKey2[40] ^ 0x1B
decKey2[42] = encKey2[16] ^ 0x2A
decKey2[37] = encKey2[7] ^ 0x25
decKey2[28] = encKey2[11] ^ 0x1C
decKey2[39] = encKey2[16] ^ 0x27
decKey2[35] = encKey2[10] ^ 0x23
decKey2[36] = encKey2[15] ^ 0x24
decKey2[48] = encKey2[1] ^ 0x30
decKey2[30] = encKey2[26] ^ 0x1E
decKey2[51] = 0
decKey2[43] = encKey2[11] ^ 0x2B
decKey2[44] = encKey2[22] ^ 0x2C
decKey2[45] = encKey2[30] ^ 0x2D
decKey2[38] = encKey2[6] ^ 0x26
decKey2[50] = encKey2[29] ^ 0x32
decKey2[49] = encKey2[13] ^ 0x31
decKey2[41] = encKey2[20] ^ 0x29
decKey2[46] = encKey2[21] ^ 0x2E

for i in range(51):
print(chr(i ^ decKey2[i]), end="")
lol_you_thought_it_was_that_easy
watevr{th4nk5_h4ck1ng_for_s0ju_hackingforsoju.team}

对于buu上的格式:
flag{th4nk5_h4ck1ng_for_s0ju_hackingforsoju.team}

但是输入的时候发现不正确

image-20260513142018096

image-20260513143449621

[FlareOn4]IgniteMe

image-20260510134241058

无壳32位程序

image-20260510134409188

image-20260510134421877

对于key来说最后的key是(eax)0x700004

读取后校验,flag如下

enc_data = [
0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C,
0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E,
0x56, 0x09, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13,
0x17, 0x48, 0x42, 0x01, 0x40, 0x4D, 0x0C, 0x02, 0x69
]

key = 0x4 # 0x700004

flag = ""
counter = len(enc_data)
for i in enc_data[::-1]:
flag += chr(key ^ i)
key = key ^ i

print(flag[::-1])

#[email protected]

vn2020-CSRe

image-20260512101853197

使用了.NET框架,估计是.net的c#逆向了。并且有混淆

主逻辑

private static void Main\u0002(string[] \u0002)
{
if (!\u0003.\u0002())
{
return;
}
bool flag = true;
realMain\u0005\u2000 realMain_u0005_u = new realMain\u0005\u2000();
string str = Console.ReadLine();
if (realMain\u0005\u2000.toHash\u0002(\u0008\u2000.\u0002(-102257297) + str + \u0008\u2000.\u0002(-102257305)) != \u0008\u2000.\u0002(-102257249))
{
flag = false;
}
string text = Console.ReadLine();
string u = realMain\u0005\u2000.toHash\u0002(\u0008\u2000.\u0002(-102257234) + text);
string text2 = realMain_u0005_u.xored\u0002(u, \u0008\u2000.\u0002(-102257241));
for (int i = 0; i < text2.Length; i++)
{
if (text2[i] != '0')
{
flag = false;
}
}
if (flag)
{
Console.WriteLine(\u0008\u2000.\u0002(-102257162) + str + text + \u0008\u2000.\u0002(-102257174));
}
}

用de4dot去混淆

image-20260512111619734

去混淆后的逻辑

private static void Main(string[] args)
{
if (!Class1.smethod_1())
{
return;
}
bool flag = true;
Class3 @class = new Class3();
string str = Console.ReadLine();
if (Class3.smethod_0("3" + str + "9") != "B498BFA2498E21325D1178417BEA459EB2CD28F8")
{
flag = false;
}
string text = Console.ReadLine();
string string_ = Class3.smethod_0("re" + text);
string text2 = @class.method_0(string_, "63143B6F8007B98C53CA2149822777B3566F9241");
for (int i = 0; i < text2.Length; i++)
{
if (text2[i] != '0')
{
flag = false;
}
}
if (flag)
{
Console.WriteLine("flag{" + str + text + "}");
}
}

image-20260512112603228

public string XorEnc(string string_0, string string_1)
{
string text = string.Empty;
char[] array = string_0.ToCharArray();
char[] array2 = string_1.ToCharArray();
int num = (array.Length < array2.Length) ? array.Length : array2.Length;
for (int i = 0; i < num; i++)
{
text += (int)(array[i] ^ array2[i]);
}
return text;
}

选更短的长度,并且最后xor的结果应该全为0,所以和key是一样的

image-20260512113539430

得到flag:flag{1415turn}