SUCTF2018 HelloPython
密文 f1f5d29b6e4414ec 加密程序已给出,请计算明文, 明文不包含flag格式SUCTF{},提交时请手动补全encrypt
pyc逆向,这种古早的技术使用现成工具 uncompyle6
https://github.com/rocky/python-uncompyle6#installation
开个venv直接pip install uncompyle6
(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 ctypesdef 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逆向?
逻辑很简单就是有写混淆
loc_1209
得到校验,就是一个初始化然后换表
unsigned __int64 __fastcall sub_1209 (char *g_input, unsigned int len) { int i; unsigned int j; char v5[256 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); 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(0x28 u); }
编写解题flag
from hashlib import md5check = [ 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} '''
NewStarCTF BabyRe
linux逆向,题目描述说
程序真的都是从main开始的吗?
代码逻辑很简单,输入后进行异或然后比较
根据提示看看 start,从ELF文件格式的入口点开始,这个在Dectect It Easy那张图可以看到
这里有个问题就是函数的执行顺序了
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; __int64 i; 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是一个函数数组列表,然后挨个执行
就是这个修改了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位有壳,而且还有反调试(壳自带的)
直接用已经有的脱壳器
https://github.com/ergrelet/unlicense/releases
静态分析上来就是一个花指令
之后几个loc的call依旧存在花指令导致ida无法识别出函数
参考之前的手动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手动转为汇编重新识别,重新识别过后才能创建为函数
这时候的main函数就很好看了
sub_A21290(input, a2) -> sub_A210C0(),rc4的密钥拓展算法中的sbox初始化和密钥调度算法
然后就是稍微变化过的RC4加密了,不过RC4是完全对称加密,加密/解密函数都是同一个,直接抄就行了,要改的就是+改为-
接着的sub_A213C0根据魔数一眼xtea魔改(循环次数),密钥也给出了。
#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; unsigned int i; char * v4; unsigned __int8 v6; unsigned __int8 v7; 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 ); } return 0 ; } char * __cdecl sub_1213C0 (uint32_t * a1) { char * result; int v2[4 ]; int v3; int i; uint32_t v5; uint32_t v6; unsigned int v7; v2[0 ] = 1855465527 ; v2[1 ] = 1144201745 ; v2[2 ] = 287454020 ; v2[3 ] = 925407342 ; v6 = a1[0 ]; v5 = a1[1 ]; 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); 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 }; for (int i = 36 ; i >= 0 ; i--) { sub_1213C0((uint32_t *)&encenc[i]); } char encrypted_data[44 ]; sub_121290((char *)encenc, encrypted_data); printf ("%s\n" , encrypted_data); return 0 ; }
SWPU2019 easyRE
sub_40EF90
BOOL sub_40EF90 () { HANDLE CurrentProcess; NTSTATUS (__stdcall *NtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); HMODULE hModule; int ProcessInformation_; 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
回到主函数
sub_401FE0和memset_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; };
sub_402DE0说明somePtr指向另外一个class,sub_40F360也可以说明。
发现和NewClass很类似,索性暂时为这个类型,再小修一下
struct NewClass { void *vtable; DWORD *dwPtr; void *ptr; char unknown[40 ]; char str[40 ]; char unknown2[12 ]; NewClass *OtherClass; };
sub_40F150 ,真正的main函数
sub_4024B0是校验函数,调用了虚函数进行校验
直接把反调试patch了然后看最后的vtable长啥样,计算完偏移过后得到0x4124E4
根据调用顺序就是(按照加减法的话应该是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; unsigned int v3; int i; _BYTE v6[56 ]; _BYTE v7[20 ]; char Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm[48 ]; int v9; 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, 0x38 u); sub_402B00(); v9 = 0 ; for ( i = 0 ; i < 45 ; ++i ) Ncg_esdvLkLgk$mL_Lgk$mL_Lgk$mL_Lgk$mL_Lgk$mLm[i] ^= 0x10 u; newClassObject(v7, 0x14 u); 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} '''
通过了检查,果然是三分逆向七分猜,不跑跑脚本可能都想不到。
接着看func7
int __thiscall func7 (NewClass *this) { int i; 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; for ( i = 0 ; i < 5 ; ++i ) func8(this, i); return func10(this); }
关于func8,没啥思路,但是func7不影响循环,看看跑完是啥
感觉就是拆开,但是也看不出来啥,那看最后的 func11
int __thiscall func11 (unsigned __int8 *this) { int i; for ( i = 0 ; i < 40 ; ++i ) { if ( this[i + 52 ] != this[i + 12 ] ) return 0 ; } return 1 ; }
前40个和后40个相等,突然想起最开始逆向class的时候有一段固定40字节的长度
整理得到
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
难点是有段汇编
带 CF 的循环左移,形成一个8 bit的循环左移
不怕汇编,动态调试看看
从头开始取
得到4个
开始移位
.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,不跳转
再 循环左移两次就变成了11000100b = 0x88
然后再循环右移,rcr也是带CF标志位的9bit循环右移
也就是说
[CF,1bit][char值, 8bit] 000110001 循环右移过后 100011000 第一组 ebp-0x14 = 8bit二进制左边有多少个0再+1 ebp-0x10 = 8bit二进制右边有多少个0,没有再加了 一个字节完成后,ebp+=1
将输入的subflag[i]的char左移刚才统计的n位,低位补0
结果的低1字节放在栈上
然后再循环n结果回去
最后的结果
res1 = 右边0的个数 + 左移进位次数; res2 = (flag << 左移进位次数) & 0xff >> res1; res3 = (flag << (8 - 0的个数)) | ((flag >> (8 - 左移次数)) << 左移次数);
最后的脚本
import stringfunc4 = 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 ): 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
当前进程标记自己为“被追踪”状态,声明由它的父进程来跟踪它。
那就是反调试
sub_92A里面有反校验算法,将其重命名为input_then_check
很简单就可以写出还原算法
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}
但是输入的时候发现不正确
[FlareOn4]IgniteMe
无壳32位程序
对于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 flag = "" counter = len (enc_data) for i in enc_data[::-1 ]: flag += chr (key ^ i) key = key ^ i print (flag[::-1 ])
vn2020-CSRe
使用了.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去混淆
去混淆后的逻辑
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 + "}" ); } }
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是一样的
得到flag:flag{1415turn}