目录
题目:[ACTF新生赛2020]Universe_final_answer
日期:2023.5.11
题目:[ACTF新生赛2020]usualCrypt
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,将文件放入IDA中进行分析,找到main函数。查看main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // esi
int v5[3]; // [esp+8h] [ebp-74h] BYREF
__int16 v6; // [esp+14h] [ebp-68h]
char v7; // [esp+16h] [ebp-66h]
char v8[100]; // [esp+18h] [ebp-64h] BYREF
sub_403CF8(&unk_40E140);
scanf("%s", v8);
memset(v5, 0, sizeof(v5));
v6 = 0;
v7 = 0;
sub_401080(v8, strlen(v8), v5);
v3 = 0;
while ( *(v5 + v3) == byte_40E0E4[v3] )
{
if ( ++v3 > strlen(v5) )
goto LABEL_6;
}
sub_403CF8(aError);
LABEL_6:
if ( v3 - 1 == strlen(byte_40E0E4) )
return sub_403CF8(aAreYouHappyYes);
else
return sub_403CF8(aAreYouHappyNo);
}
可以猜测sub_403CF8是输出函数,密文存放在byte_40E0E4中,查看sub_401080函数:
int __cdecl sub_401080(int a1, int a2, int a3)
{
int v3; // edi
int v4; // esi
int v5; // edx
int v6; // eax
int v7; // ecx
int v8; // esi
int v9; // esi
int v10; // esi
int v11; // esi
_BYTE *v12; // ecx
int v13; // esi
int v15; // [esp+18h] [ebp+8h]
v3 = 0;
v4 = 0;
sub_401000();
v5 = a2 % 3;
v6 = a1;
v7 = a2 - a2 % 3;
v15 = a2 % 3;
if ( v7 > 0 )
{
do
{
LOBYTE(v5) = *(a1 + v3);
v3 += 3;
v8 = v4 + 1;
*(v8 + a3 - 1) = aAbcdefghijklmn[(v5 >> 2) & 0x3F];
*(++v8 + a3 - 1) = aAbcdefghijklmn[16 * (*(a1 + v3 - 3) & 3) + ((*(a1 + v3 - 2) >> 4) & 0xF)];
*(++v8 + a3 - 1) = aAbcdefghijklmn[4 * (*(a1 + v3 - 2) & 0xF) + ((*(a1 + v3 - 1) >> 6) & 3)];
v5 = *(a1 + v3 - 1) & 0x3F;
v4 = v8 + 1;
*(v4 + a3 - 1) = aAbcdefghijklmn[v5];
}
while ( v3 < v7 );
v5 = v15;
}
if ( v5 == 1 )
{
LOBYTE(v7) = *(v3 + a1);
v9 = v4 + 1;
*(v9 + a3 - 1) = aAbcdefghijklmn[(v7 >> 2) & 0x3F];
v10 = v9 + 1;
*(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3)];
*(v10 + a3) = 61;
LABEL_8:
v13 = v10 + 1;
*(v13 + a3) = 61;
v4 = v13 + 1;
goto LABEL_9;
}
if ( v5 == 2 )
{
v11 = v4 + 1;
*(v11 + a3 - 1) = aAbcdefghijklmn[(*(v3 + a1) >> 2) & 0x3F];
v12 = (v3 + a1 + 1);
LOBYTE(v6) = *v12;
v10 = v11 + 1;
*(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3) + ((v6 >> 4) & 0xF)];
*(v10 + a3) = aAbcdefghijklmn[4 * (*v12 & 0xF)];
goto LABEL_8;
}
LABEL_9:
*(v4 + a3) = 0;
return sub_401030(a3);
}
将密文进行base64解密后,发现不是flag,猜测可能进行了换表。查看函数,发现在进行base64加密前,sub_401000函数对base64表进行了修改。
int sub_401000()
{
int result; // eax
char v1; // cl
for ( result = 6; result < 15; ++result )
{
v1 = aAbcdefghijklmn[result + 10];
aAbcdefghijklmn[result + 10] = aAbcdefghijklmn[result];
aAbcdefghijklmn[result] = v1;
}
return result;
}
通过动态调试来获取修改后的base64表,进行解密发现仍然是错误的,继续查看问题,发现在加密的末尾还有个sub_401030函数,查看函数发现是将密文的字母大小写进行了互换。
int __cdecl sub_401030(const char *a1)
{
__int64 v1; // rax
char v2; // al
v1 = 0i64;
if ( strlen(a1) )
{
do
{
v2 = a1[HIDWORD(v1)];
if ( v2 < 97 || v2 > 122 )
{
if ( v2 < 65 || v2 > 90 )
goto LABEL_9;
LOBYTE(v1) = v2 + 32;
}
else
{
LOBYTE(v1) = v2 - 32;
}
a1[HIDWORD(v1)] = v1;
LABEL_9:
LODWORD(v1) = 0;
++HIDWORD(v1);
}
while ( HIDWORD(v1) < strlen(a1) );
}
return v1;
}
思路理清后,完善解密脚本,脚本如下:
import base64
def main():
str1 = "zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9"
str1 = str1.swapcase()
string1 = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
if __name__ == '__main__':
main()
# b'flag{bAse64_h2s_a_Surprise}'
FLAG:flag{bAse64_h2s_a_Surprise}
日期:2023.5.13
题目:[MRCTF2020]Xor
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,32位,放入IDA中进行分析,找到main函数,但是无法将程序转成伪代码,因为程序很短,所以直接阅读汇编。
先将一段字符串压入栈中,再跳转进一个函数,猜测sub_2E1020是一个输出函数,而后面的sub_2E1050函数有一个"%s"参数,猜测是输入函数。
这一段指令对计算了一下输入的字符串长度,并与0x1b进行比较,这里可以知道输入长度为0x1b,也就是27。
"xor eax, eax"这里将eax寄存器清零,后面用al寄存器与cl进行异或,cl中存放的是输入内容,因为eax作为下标,可以判断是将输入值与其对应下标进行异或。最后再与byte_2FEA08中存放的密文进行比较。
由此理清思路后,即可写出解密脚本,脚本如下:
cipher = "MSAWB~FXZ:J:`tQJ\"N@ bpdd}8g"
for i in range(len(cipher)):
print(chr(ord(cipher[i]) ^ i),end="")
# MRCTF{@_R3@1ly_E2_R3verse!}
FLAG:flag{@_R3@1ly_E2_R3verse!}
日期:2023.5.14
题目:Youngter-drive
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,通过查壳工具发现存在UPX壳。
对其进行脱壳后,放入IDA中进行分析,找到main函数。
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
HANDLE Thread; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]
sub_3610FF();
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Destination, Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
Thread = CreateThread(0, 0, sub_36119F, 0, 0, 0);
CloseHandle(hObject);
CloseHandle(Thread);
while ( dword_368008 != -1 )
;
sub_361190();
CloseHandle(::hObject);
return 0;
}
可以看出程序创建了两个线程来执行两个函数,查看StartAddress函数和sub_36119F函数下的sub_361B10函数。
void __stdcall __noreturn StartAddress_0(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_368008 > -1 )
{
sub_36112C(Source, dword_368008);
--dword_368008;
Sleep(0x64u);
}
ReleaseMutex(hObject);
}
}
void __stdcall __noreturn sub_361B10(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_368008 > -1 )
{
Sleep(0x64u);
--dword_368008;
}
ReleaseMutex(hObject);
}
}
先执行第一个线程,执行一次对输入的字符串第一个元素进行操作后休眠,再执行第二个线程对下一个元素进行操作后休眠,实际就是将输入的字符串下标为奇或偶时进行不同操作,于是可以写出下面的解密脚本:
cipher = "TOiZiZtOrYaToUwPnToBsOaOapsyS"
dist = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
flag = ""
flag1 = []
for i in range(29):
if i % 2 == 1:
if ord(cipher[i]) >= ord('A') and ord(cipher[i]) <= ord('Z'):
flag += chr(dist.index(cipher[i])+96)
else:
flag += chr(dist.index(cipher[i])+38)
else:
flag += cipher[i]
print(flag)
# ThisisthreadofwindowshahaIsES
但是提交发现是错误的,而后看到其他师傅写的wp才知道,实际加密的是30个字符,检查时只检查了29个字符。那最后一个字符需要去猜一下。
FLAG:flag{ThisisthreadofwindowshahaIsESE}
日期:2023.5.15
题目:[MRCTF2020]hello_world_go
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,在IDA中打开,找到一个main_main函数,flag就在这里。
// main.main
void __cdecl main_main()
{
__int64 v0; // rcx
__int64 v1; // rax
__int64 v2; // rax
__int64 v3; // [rsp+20h] [rbp-90h]
__int64 v4; // [rsp+58h] [rbp-58h]
__int64 *v5; // [rsp+60h] [rbp-50h]
__int64 v6[2]; // [rsp+68h] [rbp-48h] BYREF
__int64 v7[2]; // [rsp+78h] [rbp-38h] BYREF
__int64 v8[2]; // [rsp+88h] [rbp-28h] BYREF
__int64 v9[2]; // [rsp+98h] [rbp-18h] BYREF
v5 = runtime_newobject(&RTYPE_string);
v9[0] = &RTYPE_string;
v9[1] = &off_4EA530;
fmt_Fprint(&go_itab__ptr_os_File_comma_io_Writer, os_Stdout, v9, 1LL, 1LL);
v8[0] = &RTYPE__ptr_string;
v8[1] = v5;
fmt_Fscanf(&go_itab__ptr_os_File_comma_io_Reader, os_Stdin, "%s", 2LL, v8, 1LL, 1LL);
v0 = v5[1];
v1 = *v5;
if ( v0 != 24 )
goto LABEL_2;
v4 = *v5;
if ( !runtime_memequal("flag{hello_world_gogogo}", v1, 24LL) )
{
v1 = v4;
v0 = 24LL;
LABEL_2:
runtime_cmpstring("flag{hello_world_gogogo}", 24LL, v1, v0, v3);
if ( v3 >= 0 )
v2 = 1LL;
else
v2 = -1LL;
goto LABEL_4;
}
v2 = 0LL;
LABEL_4:
if ( v2 )
{
v6[0] = &RTYPE_string;
v6[1] = &off_4EA550;
fmt_Fprintln(&go_itab__ptr_os_File_comma_io_Writer, os_Stdout, v6, 1LL, 1LL);
}
else
{
v7[0] = &RTYPE_string;
v7[1] = &off_4EA540;
fmt_Fprintln(&go_itab__ptr_os_File_comma_io_Writer, os_Stdout, v7, 1LL, 1LL);
}
}
FLAG:flag{hello_world_gogogo}
日期:2023.5.17
题目:[FlareOn4]IgniteMe
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,32位,无壳。
根据Description.txt可以知道flag的格式:
在IDA中找到一个sub_401050函数,这里对输入值进行了加密,并于密文进行了比较。
int sub_401050()
{
int v1; // [esp+0h] [ebp-Ch]
int i; // [esp+4h] [ebp-8h]
unsigned int j; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]
v1 = sub_401020(byte_403078);
v4 = sub_401000();
for ( i = v1 - 1; i >= 0; --i )
{
byte_403180[i] = v4 ^ byte_403078[i];
v4 = byte_403078[i];
}
for ( j = 0; j < 39; ++j )
{
if ( byte_403180[j] != byte_403000[j] )
return 0;
}
return 1;
}
在第一个for语句处下一个断点,可以知道v4的值为4,接着就可以写解密脚本:
cipher = [ 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 = 4
flag = ""
for i in range(len(cipher)-1,-1,-1):
cipher[i] = cipher[i] ^ key
key = cipher[i]
for j in range(len(cipher)):
print(chr(cipher[j]),end="")
# R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com
FLAG:flag{R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com}
日期:2023.5.18
题目:[GWCTF 2019]xxor
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
在IDA中进行分析,查看main函数。
__int64 __fastcall main(int a1, char **input, char **a3)
{
int i; // [rsp+8h] [rbp-68h]
int j; // [rsp+Ch] [rbp-64h]
__int64 v6[6]; // [rsp+10h] [rbp-60h] BYREF
__int64 v7[6]; // [rsp+40h] [rbp-30h] BYREF
v7[5] = __readfsqword(0x28u);
puts("Let us play a game?");
puts("you have six chances to input");
puts("Come on!");
memset(v6, 0, 40);
for ( i = 0; i <= 5; ++i )
{
printf("%s", "input: ");
input = (v6 + 4 * i);
__isoc99_scanf("%d", input);
}
memset(v7, 0, 40);
for ( j = 0; j <= 2; ++j )
{
dword_601078 = v6[j];
dword_60107C = HIDWORD(v6[j]);
input = &unk_601060;
sub_400686(&dword_601078, &unk_601060);
LODWORD(v7[j]) = dword_601078;
HIDWORD(v7[j]) = dword_60107C;
}
if ( sub_400770(v7, input) != 1 )
{
puts("NO NO NO~ ");
exit(0);
}
puts("Congratulation!\n");
puts("You seccess half\n");
puts("Do not forget to change input to hex and combine~\n");
puts("ByeBye");
return 0LL;
}
先看最后进行判断的函数sub_400770:
__int64 __fastcall sub_400770(_DWORD *x)
{
if ( x[2] - x[3] == 2225223423LL
&& x[3] + x[4] == 0xFA6CB703LL
&& x[2] - x[4] == 0x42D731A8LL
&& *x == 0xDF48EF7E
&& x[5] == 0x84F30420
&& x[1] == 0x20CAACF4 )
{
puts("good!");
return 1LL;
}
else
{
puts("Wrong!");
return 0LL;
}
}
可以知道数组x中存放的是加密的密文,于是先获取到密文:
from z3 import *
def main():
x = [BitVec("x%d"%i,64)for i in range(6)]
s = Solver()
s.add( x[2] - x[3] == 0x84A236FF)
s.add( x[3] + x[4] == 0xFA6CB703)
s.add( x[2] - x[4] == 0x42D731A8)
s.add( x[0] == 0xDF48EF7E)
s.add( x[5] == 0x84F30420)
s.add( x[1] == 0x20CAACF4)
if s.check() == sat:
v = s.model()
for i in range(6):
print(v[x[i]],end=" ")
if __name__ == '__main__':
main()
# 3746099070 550153460 3774025685 1548802262 2652626477 2230518816
接着查看sub_400686函数,可以明显看出,是进行了tea算法加密:
__int64 __fastcall sub_400686(unsigned int *v, _DWORD *k)
{
__int64 result; // rax
unsigned int v3; // [rsp+1Ch] [rbp-24h]
unsigned int v4; // [rsp+20h] [rbp-20h]
int sum1; // [rsp+24h] [rbp-1Ch]
unsigned int i; // [rsp+28h] [rbp-18h]
v3 = *v;
v4 = v[1];
sum1 = 0;
for ( i = 0; i <= 63; ++i )
{
sum1 += 0x458BCD42;
v3 += (v4 + sum1 + 11) ^ ((v4 << 6) + *k) ^ ((v4 >> 9) + k[1]) ^ 0x20;
v4 += (v3 + sum1 + 20) ^ ((v3 << 6) + k[2]) ^ ((v3 >> 9) + k[3]) ^ 0x10;
}
*v = v3;
result = v4;
v[1] = v4;
return result;
}
sub_400686传了两个参数,第一个是输入的值,第二个是key,通过查看unk_601060即可知道key值。接着写解密脚本即可,尝试了一下用python来写解密tea脚本:
import ctypes
def main():
v = [3746099070,550153460,3774025685,1548802262,2652626477,2230518816]
k = [2,2,3,4]
flag = ""
for i in range(0,6,2):
DELTA = 0x458BCD42
sum1 = 64*DELTA
v0 = ctypes.c_uint32(v[i])
v1 = ctypes.c_uint32(v[i+1])
for j in range(64):
v1.value -= (v0.value + sum1 + 20) ^ ((v0.value << 6) + k[2]) ^ ((v0.value >> 9) + k[3]) ^ 0x10
v0.value -= (v1.value + sum1 + 11) ^ ((v1.value << 6) + k[0]) ^ ((v1.value >> 9) + k[1]) ^ 0x20
sum1 -= DELTA
flag += hex(v0.value)[2:].zfill(6)
flag += hex(v1.value)[2:].zfill(6) # 帮忙补零和去掉前缀"0x"
for i in range(0,len(flag),2):
print(chr(int(flag[i]+flag[i+1],16)),end="")
if __name__ == '__main__':
main()
# flag{re_is_great!}
FLAG:flag{re_is_great!}
题目:[WUSTCTF2020]Cr0ssfun
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
查看其中的main函数。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[48]; // [rsp+0h] [rbp-30h] BYREF
puts(" _ _ _ _ _____ _____ _____ ");
puts("| | | | | | / ___|_ _| / ___| ");
puts("| | | | | | \\ `--. | | \\ `--. ___ ___ ");
puts("| |/\\| | | | |`--. \\ | | `--. \\/ _ \\/ __|");
puts("\\ /\\ / |_| /\\__/ / | | /\\__/ / __/ (__ ");
puts(" \\/ \\/ \\___/\\____/ \\_/ \\____/ \\___|\\___|");
while ( 1 )
{
puts("Input the flag");
__isoc99_scanf("%s", v4);
if ( check(v4) == 1 )
break;
puts("0ops, your flag seems fake.");
puts("==============================");
rewind(_bss_start);
}
puts("Your flag is correct, go and submit it!");
return 0;
}
可以看出关键在check函数。check函数中对输入值进行了判断,按顺序进行拼接就可以拿到flag。
_BOOL8 __fastcall iven_is_handsome(_BYTE *a1)
{
return a1[10] == 'p' && a1[13] == '@' && a1[3] == 'f' && a1[26] == 'r' && a1[20] == 'e' && iven_is_c0ol(a1);
}
但是,感觉这个可以试一试用angr,就尝试写了一个脚本来获取flag,脚本如下:
import angr
import sys
def main(argv):
file_path = argv[1]
p = angr.Project(file_path,auto_load_libs=False)
start_state = p.factory.entry_state()
sm = p.factory.simgr(start_state)
def is_good(state):
res = state.posix.dumps(1)
if b'Your flag is correct, go and submit it!' in res:
return True
else:
return False
def is_bad(state):
res = state.posix.dumps(1)
if b'0ops, your flag seems fake.' in res:
return True
else:
return False
sm.explore(find = is_good,avoid = is_bad)
if sm.found:
found = sm.found[0]
solution = found.posix.dumps(0)
print(solution)
else:
print("NONE")
if __name__ == '__main__':
main(sys.argv)
# wctf2020{cpp_@nd_r3verse_@re_fun}
FLAG:flag{cpp_@nd_r3verse_@re_fun}
日期:2023.5.19
题目:[FlareOn6]Overlong
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,32位,无壳。
在IDA中可以看到,程序只有三个函数。
int __stdcall start(int a1, int a2, int a3, int a4)
{
CHAR Text[128]; // [esp+0h] [ebp-84h] BYREF
int v6; // [esp+80h] [ebp-4h]
v6 = sub_401160(Text, &unk_402008, 28);
Text[v6] = 0;
MessageBoxA(0, Text, Caption, 0);
return 0;
}
int __stdcall start(int a1, int a2, int a3, int a4)
{
CHAR Text[128]; // [esp+0h] [ebp-84h] BYREF
int v6; // [esp+80h] [ebp-4h]
v6 = sub_401160(Text, &unk_402008, 28);
Text[v6] = 0;
MessageBoxA(0, Text, Caption, 0);
return 0;
}
int __cdecl sub_401000(_BYTE *a1, char *a2)
{
int v3; // [esp+0h] [ebp-8h]
char v4; // [esp+4h] [ebp-4h]
if ( (int)(unsigned __int8)*a2 >> 3 == 30 )
{
v4 = a2[3] & 0x3F | ((a2[2] & 0x3F) << 6);
v3 = 4;
}
else if ( (int)(unsigned __int8)*a2 >> 4 == 14 )
{
v4 = a2[2] & 0x3F | ((a2[1] & 0x3F) << 6);
v3 = 3;
}
else if ( (int)(unsigned __int8)*a2 >> 5 == 6 )
{
v4 = a2[1] & 0x3F | ((*a2 & 0x1F) << 6);
v3 = 2;
}
else
{
v4 = *a2;
v3 = 1;
}
*a1 = v4;
return v3;
}
通过分析程序可以知道,是将密文进行解密后,以弹窗的形式显示出来。
但是根据程序可知,将28个字符以弹窗形式显示,实际要解密出的内容并不止那么点。
通过修改sub_401160传的参数,也就是长度,从而可以得到flag。
下断点,直接截取flag。
FLAG: flag{I_a_M_t_h_e_e_n_C_o_D_i_n_g@flare-on.com}
日期:2023.5.20
题目:[UTCTF2020]basic-re
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
用IDA进行分析,发现,flag就藏在main函数中。
FLAG:flag{str1ngs_1s_y0ur_fr13nd}
题目:[FlareOn3]Challenge1
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,32位,无壳。
用IDA进行分析,可以在main函数中找到一串密文,并猜测sub_401260函数对输入内容进行了加密。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Buffer[128]; // [esp+0h] [ebp-94h] BYREF
char *Str1; // [esp+80h] [ebp-14h]
char *Str2; // [esp+84h] [ebp-10h]
HANDLE StdHandle; // [esp+88h] [ebp-Ch]
HANDLE hFile; // [esp+8Ch] [ebp-8h]
DWORD NumberOfBytesWritten; // [esp+90h] [ebp-4h] BYREF
hFile = GetStdHandle(0xFFFFFFF5);
StdHandle = GetStdHandle(0xFFFFFFF6);
Str2 = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q";
WriteFile(hFile, "Enter password:\r\n", 0x12u, &NumberOfBytesWritten, 0);
ReadFile(StdHandle, Buffer, 0x80u, &NumberOfBytesWritten, 0);
Str1 = sub_401260(Buffer, NumberOfBytesWritten - 2);
if ( !strcmp(Str1, Str2) )
WriteFile(hFile, "Correct!\r\n", 0xBu, &NumberOfBytesWritten, 0);
else
WriteFile(hFile, "Wrong password\r\n", 0x11u, &NumberOfBytesWritten, 0);
return 0;
}
查看sub_401260,可以看出sub_401260函数对输入内容进行了base64加密。
_BYTE *__cdecl sub_401260(int a1, unsigned int a2)
{
int v3; // [esp+Ch] [ebp-24h]
int v4; // [esp+10h] [ebp-20h]
int v5; // [esp+14h] [ebp-1Ch]
int i; // [esp+1Ch] [ebp-14h]
unsigned int v7; // [esp+20h] [ebp-10h]
_BYTE *v8; // [esp+24h] [ebp-Ch]
int v9; // [esp+28h] [ebp-8h]
int v10; // [esp+28h] [ebp-8h]
unsigned int v11; // [esp+2Ch] [ebp-4h]
v8 = malloc(4 * ((a2 + 2) / 3) + 1);
if ( !v8 )
return 0;
v11 = 0;
v9 = 0;
while ( v11 < a2 )
{
v5 = *(v11 + a1);
if ( ++v11 >= a2 )
{
v4 = 0;
}
else
{
v4 = *(v11 + a1);
++v11;
}
if ( v11 >= a2 )
{
v3 = 0;
}
else
{
v3 = *(v11 + a1);
++v11;
}
v7 = v3 + (v5 << 16) + (v4 << 8);
v8[v9] = aZyxabcdefghijk[(v7 >> 18) & 0x3F];
v10 = v9 + 1;
v8[v10] = aZyxabcdefghijk[(v7 >> 12) & 0x3F];
v8[++v10] = aZyxabcdefghijk[(v7 >> 6) & 0x3F];
v8[++v10] = aZyxabcdefghijk[v3 & 0x3F];
v9 = v10 + 1;
}
for ( i = 0; i < MEMORY[0x413040][a2 % 3]; ++i )
v8[4 * ((a2 + 2) / 3) - i - 1] = 61;
v8[4 * ((a2 + 2) / 3)] = 0;
return v8;
}
查看base64表,可以看出是变表base64。
.data:00413000 5A 59 58 41 42 43 44 45 46 47+aZyxabcdefghijk db 'ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/',0
由此可以写解密脚本,脚本如下:
import base64
def main():
str1 = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"
string1 = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1,string2))))
if __name__ == '__main__':
main()
# sh00ting_phish_in_a_barrel@flare-on.com
FLAG:flag{sh00ting_phish_in_a_barrel@flare-on.com}
日期:2023.5.23
题目:[ACTF新生赛2020]Oruga
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
放入IDA中进行分析,查看main函数:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int i; // [rsp+0h] [rbp-40h]
char s1[6]; // [rsp+4h] [rbp-3Ch] BYREF
char s2[6]; // [rsp+Ah] [rbp-36h] BYREF
char s[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v8; // [rsp+38h] [rbp-8h]
v8 = __readfsqword(0x28u);
memset(s, 0, 0x19uLL);
printf("Tell me the flag:");
scanf("%s", s);
strcpy(s2, "actf{");
for ( i = 0; i <= 4; ++i )
s1[i] = s[i];
s1[5] = 0;
if ( !strcmp(s1, s2) )
{
if ( sub_78A(s) )
printf("That's True Flag!");
else
printf("don't stop trying...");
return 0LL;
}
else
{
printf("Format false!");
return 0LL;
}
}
看到这样一个结构,感觉可以用angr进行求解flag,就尝试了一下。解密脚本如下:
import angr
import sys
def main(argv):
file_path = argv[1]
p = angr.Project(file_path,auto_load_libs=False)
start_state = p.factory.entry_state()
sm = p.factory.simgr(start_state)
def is_good(state):
res = state.posix.dumps(1)
if b'That\'s True Flag!' in res:
return True
else:
return False
def is_bad(state):
res = state.posix.dumps(1)
if b'don\'t stop trying...' in res:
return True
else:
return False
sm.explore(find = is_good,avoid = is_bad)
if sm.found:
found = sm.found[0]
solution = found.posix.dumps(0)
print(solution)
else:
print("NONE")
if __name__ == '__main__':
main(sys.argv)
结果可以直接跑出flag。
这是一个比较方便的方法,也可以对其中函数进行分析,可以看出关键函数是sub_78A,查看函数:
_BOOL8 __fastcall sub_78A(__int64 a1)
{
int v2; // [rsp+Ch] [rbp-Ch]
int v3; // [rsp+10h] [rbp-8h]
int v4; // [rsp+14h] [rbp-4h]
v2 = 0;
v3 = 5;
v4 = 0;
while ( byte_201020[v2] != '!' )
{
v2 -= v4;
if ( *(v3 + a1) != 'W' || v4 == -16 )
{
if ( *(v3 + a1) != 'E' || v4 == 1 )
{
if ( *(v3 + a1) != 'M' || v4 == 16 )
{
if ( *(v3 + a1) != 'J' || v4 == -1 )
return 0LL;
v4 = -1;
}
else
{
v4 = 16;
}
}
else
{
v4 = 1;
}
}
else
{
v4 = -16;
}
++v3;
while ( !byte_201020[v2] )
{
if ( v4 == -1 && (v2 & 0xF) == 0 )
return 0LL;
if ( v4 == 1 && v2 % 16 == 15 )
return 0LL;
if ( v4 == 16 && (v2 - 240) <= 0xF )
return 0LL;
if ( v4 == -16 && (v2 + 15) <= 0x1E )
return 0LL;
v2 += v4;
}
}
return *(v3 + a1) == '}';
}
分析程序,可以看出这是一个迷宫题。"W"是向上走,"E"是向右走,"M"是向下走,"J"是向左走。走法是会一直往一个方向走直到有障碍物挡着才会停下。
地图存在byte_201020数组中,地图如下:
可以用来走的路是0,其他是障碍物,从左上角走到"!"的位置即可。
FLAG:flag{MEWEMEWJMEWJM}
日期:2023.5.25
题目:特殊的 BASE64
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
放入IDA中进行分析,找到main函数。
int __cdecl main(int argc, const char **argv, const char **envp)
{
std::ostream *v3; // rax
std::ostream *v4; // rax
std::string result; // [rsp+20h] [rbp-60h] BYREF
std::string rightFlag; // [rsp+30h] [rbp-50h] BYREF
std::string str; // [rsp+40h] [rbp-40h] BYREF
char v9; // [rsp+4Fh] [rbp-31h] BYREF
std::string v10; // [rsp+50h] [rbp-30h] BYREF
_main();
std::string::string(&str);
std::allocator<char>::allocator(&v9);
std::string::string(&rightFlag, "mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI==", &v9);
std::allocator<char>::~allocator(&v9);
v3 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Please input your flag!!!!");
refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v3);
std::operator>><char>(refptr__ZSt3cin, &str);
std::string::string(&v10, &str);
base64Encode(&result);
std::string::~string(&v10);
if ( std::operator==<char>(&result, &rightFlag) )
v4 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "The flag is right!!!!!!!!!");
else
v4 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "This is a wrong flag!!!!!!!!");
refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v4);
std::string::~string(&result);
std::string::~string(&rightFlag);
std::string::~string(&str);
return 0;
}
根据题目可知,进行的是base64加密,也在main函数中找到一个很像base64加密的密文"mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI==",但通过解密发现明文不正确,猜测进行了换表,查看base64Encode函数中的base64表,找到真正的表。
接着即可开始写解密脚本,脚本如下:
import base64
def main():
str1 = "mTyqm7wjODkrNLcWl0eqO8K8gc1BPk1GNLgUpI=="
string1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0987654321/+"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1,string2))))
if __name__ == '__main__':
main()
# flag{Special_Base64_By_Lich}
FLAG:flag{Special_Base64_By_Lich}
日期:2023.5.26
题目:[ACTF新生赛2020]Universe_final_answer
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
在IDA中进行分析,找到main函数,发现两个特别的函数,sub_860函数和sub_C50函数。
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v4; // [rsp+0h] [rbp-A8h] BYREF
char v5[104]; // [rsp+20h] [rbp-88h] BYREF
unsigned __int64 v6; // [rsp+88h] [rbp-20h]
v6 = __readfsqword(0x28u);
__printf_chk(1LL, "Please give me the key string:", a3);
scanf("%s", v5);
if ( sub_860(v5) )
{
sub_C50(v5, &v4);
__printf_chk(1LL, "Judgement pass! flag is actf{%s_%s}\n", v5);
}
else
{
puts("False key!");
}
return 0LL;
}
查看这两个函数。
bool __fastcall sub_860(char *x)
{
int v1; // ecx
int v2; // esi
int v3; // edx
int v4; // r9d
int v5; // r11d
int v6; // ebp
int v7; // ebx
int v8; // r8d
int v9; // r10d
bool result; // al
int v11; // [rsp+0h] [rbp-38h]
v1 = x[1];
v2 = *x;
v3 = x[2];
v4 = x[3];
v5 = x[4];
v6 = x[6];
v7 = x[5];
v8 = x[7];
v9 = x[8];
result = 0;
if ( -85 * v9 + 58 * v8 + 97 * v6 + v7 + -45 * v5 + 84 * v4 + 95 * v2 - 20 * v1 + 12 * v3 == 12613 )
{
v11 = x[9];
if ( 30 * v11 + -70 * v9 + -122 * v6 + -81 * v7 + -66 * v5 + -115 * v4 + -41 * v3 + -86 * v1 - 15 * v2 - 30 * v8 == -54400
&& -103 * v11 + 120 * v8 + 108 * v7 + 48 * v4 + -89 * v3 + 78 * v1 - 41 * v2 + 31 * v5 - (v6 << 6) - 120 * v9 == -10283
&& 71 * v6 + (v7 << 7) + 99 * v5 + -111 * v3 + 85 * v1 + 79 * v2 - 30 * v4 - 119 * v8 + 48 * v9 - 16 * v11 == 22855
&& 5 * v11 + 23 * v9 + 122 * v8 + -19 * v6 + 99 * v7 + -117 * v5 + -69 * v3 + 22 * v1 - 98 * v2 + 10 * v4 == -2944
&& -54 * v11 + -23 * v8 + -82 * v3 + -85 * v2 + 124 * v1 - 11 * v4 - 8 * v5 - 60 * v7 + 95 * v6 + 100 * v9 == -2222
&& -83 * v11 + -111 * v7 + -57 * v2 + 41 * v1 + 73 * v3 - 18 * v4 + 26 * v5 + 16 * v6 + 77 * v8 - 63 * v9 == -13258
&& 81 * v11 + -48 * v9 + 66 * v8 + -104 * v6 + -121 * v7 + 95 * v5 + 85 * v4 + 60 * v3 + -85 * v2 + 80 * v1 == -1559
&& 101 * v11 + -85 * v9 + 7 * v6 + 117 * v7 + -83 * v5 + -101 * v4 + 90 * v3 + -28 * v1 + 18 * v2 - v8 == 6308 )
{
return 99 * v11 + -28 * v9 + 5 * v8 + 93 * v6 + -18 * v7 + -127 * v5 + 6 * v4 + -9 * v3 + -93 * v1 + 58 * v2 == -1697;
}
}
return result;
}
unsigned __int64 __fastcall sub_C50(const char *a1, _BYTE *a2)
{
size_t v4; // rax
unsigned int v5; // edx
int v6; // edi
int v7; // ecx
__int64 v8; // r8
__int128 *v9; // rsi
unsigned int v10; // ecx
int v11; // eax
int v12; // edi
int v13; // edx
int v14; // eax
_BYTE *v15; // rsi
_BYTE *v16; // rcx
_BYTE *v17; // r8
int *i; // rax
unsigned __int64 result; // rax
__int128 v20[2]; // [rsp+0h] [rbp-48h] BYREF
__int64 v21; // [rsp+20h] [rbp-28h]
unsigned __int64 v22; // [rsp+28h] [rbp-20h]
v22 = __readfsqword(0x28u);
memset(v20, 0, sizeof(v20));
v21 = 0LL;
v4 = strlen(a1);
v5 = 0;
v6 = 9;
while ( v5 < v4 )
{
v7 = a1[v5++];
v6 ^= v7;
}
if ( v6 )
{
v8 = 0LL;
v9 = v20;
while ( 1 )
{
v9 = (v9 + 4);
v10 = v8 + 1;
v11 = v6 / 10;
v12 = v6 % 10;
*(v9 - 1) = v12;
LOBYTE(v13) = v12;
v6 = v11;
if ( !v11 )
break;
v8 = v10;
}
v14 = v8 - 1;
v15 = a2;
v16 = &a2[v10];
v17 = &a2[v8];
for ( i = v20 + v14; ; --i )
{
*v15 = v13 + 48;
if ( v17 == v15 )
break;
v13 = *i;
++v15;
}
}
else
{
v16 = a2;
}
result = __readfsqword(0x28u) ^ v22;
*v16 = 0;
return result;
}
根据程序分析可以知道,sub_860函数是用来判断输入的key值的,只要将正确的key值求出并输入到程序中,即可得到flag,求解key的脚本:
from z3 import *
def main():
x = [BitVec("x%i"%i,16) for i in range(10)]
s = Solver()
s.add( -85 * x[8] + 58 * x[7] + 97 * x[6] + x[5] + -45 * x[4] + 84 * x[3] + 95 * x[0] - 20 * x[1] + 12 * x[2] == 12613 )
s.add( 30 * x[9] + -70 * x[8] + -122 * x[6] + -81 * x[5] + -66 * x[4] + -115 * x[3] + -41 * x[2] + -86 * x[1] - 15 * x[0] - 30 * x[7] == -54400 )
s.add( -103 * x[9] + 120 * x[7] + 108 * x[5] + 48 * x[3] + -89 * x[2] + 78 * x[1] - 41 * x[0] + 31 * x[4] - (x[6] << 6) - 120 * x[8] == -10283 )
s.add( 71 * x[6] + (x[5] << 7) + 99 * x[4] + -111 * x[2] + 85 * x[1] + 79 * x[0] - 30 * x[3] - 119 * x[7] + 48 * x[8] - 16 * x[9] == 22855 )
s.add( 5 * x[9] + 23 * x[8] + 122 * x[7] + -19 * x[6] + 99 * x[5] + -117 * x[4] + -69 * x[2] + 22 * x[1] - 98 * x[0] + 10 * x[3] == -2944 )
s.add( -54 * x[9] + -23 * x[7] + -82 * x[2] + -85 * x[0] + 124 * x[1] - 11 * x[3] - 8 * x[4] - 60 * x[5] + 95 * x[6] + 100 * x[8] == -2222 )
s.add( -83 * x[9] + -111 * x[5] + -57 * x[0] + 41 * x[1] + 73 * x[2] - 18 * x[3] + 26 * x[4] + 16 * x[6] + 77 * x[7] - 63 * x[8] == -13258 )
s.add( 81 * x[9] + -48 * x[8] + 66 * x[7] + -104 * x[6] + -121 * x[5] + 95 * x[4] + 85 * x[3] + 60 * x[2] + -85 * x[0] + 80 * x[1] == -1559 )
s.add( 101 * x[9] + -85 * x[8] + 7 * x[6] + 117 * x[5] + -83 * x[4] + -101 * x[3] + 90 * x[2] + -28 * x[1] + 18 * x[0] - x[7] == 6308 )
s.add( 99 * x[9] + -28 * x[8] + 5 * x[7] + 93 * x[6] + -18 * x[5] + -127 * x[4] + 6 * x[3] + -9 * x[2] + -93 * x[1] + 58 * x[0] == -1697 )
if s.check() == sat:
value = s.model()
for i in range(10):
print(value[x[i]],end=",")
flag = [70,48,117,82,84,121,95,55,119,64]
for i in range(len(flag)):
print(chr(flag[i]),end="")
if __name__ == '__main__':
main()
从而求出key为"F0uRTy_7w@",将其输入到程序中,即可得到flag。
flag{F0uRTy_7w@_42}
日期:2023.5.27
题目:[羊城杯 2020]easyre
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
放入IDA中进行分析,找到main函数,查看main函数。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // eax
int v5; // eax
char Str[48]; // [rsp+20h] [rbp-60h] BYREF
char Str1[64]; // [rsp+50h] [rbp-30h] BYREF
char v9[64]; // [rsp+90h] [rbp+10h] BYREF
char v10[64]; // [rsp+D0h] [rbp+50h] BYREF
char Str2[60]; // [rsp+110h] [rbp+90h] BYREF
int v12; // [rsp+14Ch] [rbp+CCh] BYREF
_main();
strcpy(Str2, "EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG");
puts("Hello, please input your flag and I will tell you whether it is right or not.");
scanf("%38s", Str);
if ( strlen(Str) != 38
|| (v3 = strlen(Str), encode_one(Str, v3, v10, &v12))
|| (v4 = strlen(v10), encode_two(v10, v4, v9, &v12))
|| (v5 = strlen(v9), encode_three(v9, v5, Str1, &v12))
|| strcmp(Str1, Str2) )
{
printf("Something wrong. Keep going.");
return 0;
}
else
{
puts("you are right!");
return 0;
}
}
可以看出程序依次进行了三个不同的加密,查看这三个函数。
__int64 __fastcall encode_one(char *a1, int a2, char *a3, int *a4)
{
int v5; // esi
int v6; // esi
int v7; // esi
int v8; // [rsp+34h] [rbp-1Ch]
int v9; // [rsp+38h] [rbp-18h]
int v11; // [rsp+48h] [rbp-8h]
int i; // [rsp+4Ch] [rbp-4h]
unsigned __int8 *v13; // [rsp+70h] [rbp+20h]
v13 = a1;
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
v11 = 0;
if ( a2 % 3 )
v11 = 3 - a2 % 3;
v9 = a2 + v11;
v8 = 8 * (a2 + v11) / 6;
for ( i = 0; i < v9; i += 3 )
{
*a3 = alphabet[*v13 >> 2];
if ( a2 + v11 - 3 == i && v11 )
{
if ( v11 == 1 )
{
v5 = cmove_bits(*v13, 6u, 2u);
a3[1] = alphabet[v5 + cmove_bits(v13[1], 0, 4u)];
a3[2] = alphabet[cmove_bits(v13[1], 4u, 2u)];
a3[3] = 61;
}
else if ( v11 == 2 )
{
a3[1] = alphabet[cmove_bits(*v13, 6u, 2u)];
a3[2] = 61;
a3[3] = 61;
}
}
else
{
v6 = cmove_bits(*v13, 6u, 2u);
a3[1] = alphabet[v6 + cmove_bits(v13[1], 0, 4u)];
v7 = cmove_bits(v13[1], 4u, 2u);
a3[2] = alphabet[v7 + cmove_bits(v13[2], 0, 6u)];
a3[3] = alphabet[v13[2] & 0x3F];
}
a3 += 4;
v13 += 3;
}
if ( a4 )
*a4 = v8;
return 0i64;
}
__int64 __fastcall encode_two(const char *a1, int a2, char *a3, int *a4)
{
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
strncpy(a3, a1 + 26, 0xDui64);
strncpy(a3 + 13, a1, 0xDui64);
strncpy(a3 + 26, a1 + 39, 0xDui64);
strncpy(a3 + 39, a1 + 13, 13ui64);
return 0i64;
}
__int64 __fastcall encode_three(const char *a1, int a2, char *a3, int *a4)
{
char v5; // [rsp+Fh] [rbp-11h]
int i; // [rsp+14h] [rbp-Ch]
const char *v8; // [rsp+30h] [rbp+10h]
v8 = a1;
if ( !a1 || !a2 )
return 0xFFFFFFFFi64;
for ( i = 0; i < a2; ++i )
{
v5 = *v8;
if ( *v8 <= '@' || v5 > 'Z' )
{
if ( v5 <= 96 || v5 > 122 )
{
if ( v5 <= '/' || v5 > '9' )
*a3 = v5;
else
*a3 = (v5 - 48 + 3) % 10 + 48;
}
else
{
*a3 = (v5 - 97 + 3) % 26 + 97;
}
}
else
{
*a3 = (v5 - 65 + 3) % 26 + 65;
}
++a3;
++v8;
}
return 0i64;
}
可以看出,第一个加密函数进行的是base64加密(无变表),第二个加密函数是来打乱base64加密后的字符串,第三个加密函数进行的是凯撒加密,偏移值为3。
由此,按相反的顺序来进行对应的逆运算解密,解密脚本如下:
import base64
def main():
cipher = "EmBmP5Pmn7QcPU4gLYKv5QcMmB3PWHcP5YkPq3=cT6QckkPckoRG"
cipher = list(cipher)
flag = ""
for i in range(len(cipher)):
if cipher[i].isupper() == False:
if cipher[i].islower() == False:
if cipher[i].isnumeric() == False:
cipher[i] = cipher[i]
else:
cipher[i] = chr((ord(cipher[i]) - 48 - 3) % 10 + 48)
else:
cipher[i] = chr((ord(cipher[i]) - 97 - 3) % 26 + 97)
else:
cipher[i] = chr((ord(cipher[i]) - 65 - 3) % 26 + 65)
cipher1 = ['0'] * len(cipher)
cipher1[13:26] = cipher[39:52]
cipher1[39:52] = cipher[26:39]
cipher1[0:13] = cipher[13:26]
cipher1[26:39] = cipher[0:13]
for i in range(len(cipher1)):
flag += cipher1[i]
print(base64.b64decode(flag.encode()))
if __name__ == "__main__":
main()
# GWHT{672cc4778a38e80cb362987341133ea2}
FLAG:flag{672cc4778a38e80cb362987341133ea2}
日期:2023.5.28
题目:[ACTF新生赛2020]fungame
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,32位,无壳。
用IDA进行分析,找到main函数。
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *v4; // [esp+1Ch] [ebp-4h]
__main();
v4 = malloc(0x14u);
memset(v4, 0, 0x14u);
memset(x, 0, 0x18u);
sub_401340(v4);
sub_4013BA(v4);
return 0;
}
在sub_401340函数中可以找到对输入值进行的加密。
int __cdecl sub_401340(int a1)
{
char i; // [esp+1Fh] [ebp-9h]
printf("Please input:");
scanf("%s", a1);
for ( i = 0; i <= 15; ++i )
{
if ( (*(i + a1) ^ *(y1 + i)) != y2[i] )
exit(0);
}
return 0;
}
可以看出是将输入值与y1数组中值进行异或后与y2中值进行比较,那接下来只需要将y1和y2中的值提取出来进行逆运算即可,解密脚本如下:
cipher = [ 0x71, 0x04, 0x61, 0x58, 0x27, 0x1E, 0x4B, 0x22, 0x5E, 0x64, 0x03, 0x26, 0x5E, 0x17, 0x3C, 0x7A]
key = [0x23, 0x61, 0x3E, 0x69, 0x54, 0x41, 0x18, 0x4D, 0x6E, 0x3B, 0x65, 0x53, 0x30, 0x79, 0x45, 0x5B, 0x71]
for i in range(len(cipher)):
print(chr(cipher[i]^key[i]),end="")
# Re_1s_So0_funny!
但是提交发现flag错误,于是接着寻找,猜测可能有没发现的加密,用Findcrypt插件进行查找,发现一个base64表。
通过两次交叉引用找到了sub_40233D函数。
void __noreturn sub_40233D()
{
char Str2[13]; // [esp+13h] [ebp-35h] BYREF
char Str1[16]; // [esp+20h] [ebp-28h] BYREF
char Str[12]; // [esp+30h] [ebp-18h] BYREF
size_t v3; // [esp+3Ch] [ebp-Ch]
printf("Please input again:");
strcpy(Str2, "YTFzMF9wV24=");
memset(Str, 0, sizeof(Str));
memset(Str1, 0, sizeof(Str1));
scanf("%s", Str);
v3 = strlen(Str);
sub_402421(Str, v3, Str1);
if ( !strcmp(Str1, Str2) )
{
printf("%s%s", x, Str);
exit(0);
}
exit(0);
}
这里还有一串base64加密后的密文。于是继续写出解密脚本:
import base64
cipher = [ 0x71, 0x04, 0x61, 0x58, 0x27, 0x1E, 0x4B, 0x22, 0x5E, 0x64, 0x03, 0x26, 0x5E, 0x17, 0x3C, 0x7A]
key = [0x23, 0x61, 0x3E, 0x69, 0x54, 0x41, 0x18, 0x4D, 0x6E, 0x3B, 0x65, 0x53, 0x30, 0x79, 0x45, 0x5B, 0x71]
flag = ""
for i in range(len(cipher)):
flag += chr(cipher[i]^key[i])
cipher2 = b"YTFzMF9wV24="
flag += base64.b64decode(cipher2).decode()
print(flag)
# Re_1s_So0_funny!a1s0_pWn
但是flag提交仍然有问题,在此遇到了困难,后面看了一下其他师傅写的wp才知道,还有几个字符缺少,缺的就是sub_40233D中的40 23 3D,而后知道实际这里是有一个溢出,"Re_1s_So0_funny!"后面紧接着需要跳转到的函数地址0x40233D,再接着就是base64解密出的"a1s0_pWn",所以可以写出完整的解密脚本:
import base64
cipher = [ 0x71, 0x04, 0x61, 0x58, 0x27, 0x1E, 0x4B, 0x22, 0x5E, 0x64, 0x03, 0x26, 0x5E, 0x17, 0x3C, 0x7A]
key = [0x23, 0x61, 0x3E, 0x69, 0x54, 0x41, 0x18, 0x4D, 0x6E, 0x3B, 0x65, 0x53, 0x30, 0x79, 0x45, 0x5B, 0x71]
flag = ""
for i in range(len(cipher)):
flag += chr(cipher[i]^key[i])
value = [0x3d,0x23,0x40]
for j in range(len(value)):
flag += chr(value[j])
cipher2 = b"YTFzMF9wV24="
flag += base64.b64decode(cipher2).decode()
print(flag)
# Re_1s_So0_funny!=#@a1s0_pWn
FLAG:flag{Re_1s_So0_funny!=#@a1s0_pWn}
日期:2023.5.30
题目:[RoarCTF2019]polyre
刷题平台:BUUCTF
方向:REVERSE
Write UP:
获取题目附件,64位,无壳。
用IDA来进行分析,在main函数中看到一堆while(1)给看愣住了。
尝试硬啃,发现属实啃不明白,在此陷入困难,感觉题目不是要这样去硬啃的,于是去看了一下其他师傅写的wp,才知道这里进行了混淆,将程序进行了控制流平坦化。关于控制流平坦化的介绍和去除,可以参考这两篇文章利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心 (tencent.com)
通过这个脚本可以对这个程序进行去混淆GitHub - cq674350529/deflat: use angr to deobfuscation
python deflat.py -f attachment --addr 0x400620
去掉混淆后的程序还有一部分没有去干净。
接着就是用IDApython去掉剩下的混淆,但IDApython了解还是太少了,发现了一处自己的薄弱点,这里先用别的师傅写的脚本。
st = 0x0000000000400620 #main开始
end = 0x0000000000402144 #main结束
def patch_nop(start,end):
for i in range(start,end):
ida_bytes.patch_byte(i, 0x90) #修改指定地址处的指令 0x90是最简单的1字节nop
def next_instr(addr):
return addr+idc.get_item_size(addr) #获取指令或数据长度,这个函数的作用就是去往下一条指令
addr = st
while(addr<end):
next = next_instr(addr)
if "ds:dword_603054" in GetDisasm(addr): #GetDisasm(addr)得到addr的反汇编语句
while(True):
addr = next
next = next_instr(addr)
if "jnz" in GetDisasm(addr):
dest = idc.get_operand_value(addr, 0) #得到操作数,就是指令后的数
ida_bytes.patch_byte(addr, 0xe9) #0xe9 jmp后面的四个字节是偏移
ida_bytes.patch_byte(addr+5, 0x90) #nop第五个字节
offset = dest - (addr + 5) #调整为正确的偏移地址 也就是相对偏移地址 - 当前指令后的地址
ida_bytes.patch_dword(addr + 1, offset) #把地址赋值给jmp后
print("patch bcf: 0x%x"%addr)
addr = next
break
else:
addr = next
最后去掉混淆后的程序如下:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
signed __int64 v4; // [rsp+1E0h] [rbp-110h]
int j; // [rsp+1E8h] [rbp-108h]
int i; // [rsp+1ECh] [rbp-104h]
int k; // [rsp+1ECh] [rbp-104h]
char s1[48]; // [rsp+1F0h] [rbp-100h] BYREF
char s[60]; // [rsp+220h] [rbp-D0h] BYREF
unsigned int v10; // [rsp+25Ch] [rbp-94h]
char *v11; // [rsp+260h] [rbp-90h]
int v12; // [rsp+26Ch] [rbp-84h]
bool v13; // [rsp+272h] [rbp-7Eh]
unsigned __int8 v14; // [rsp+273h] [rbp-7Dh]
int v15; // [rsp+274h] [rbp-7Ch]
char *v16; // [rsp+278h] [rbp-78h]
int v17; // [rsp+284h] [rbp-6Ch]
int v18; // [rsp+288h] [rbp-68h]
bool v19; // [rsp+28Fh] [rbp-61h]
char *v20; // [rsp+290h] [rbp-60h]
int v21; // [rsp+298h] [rbp-58h]
bool v22; // [rsp+29Fh] [rbp-51h]
__int64 v23; // [rsp+2A0h] [rbp-50h]
bool v24; // [rsp+2AFh] [rbp-41h]
__int64 v25; // [rsp+2B0h] [rbp-40h]
__int64 v26; // [rsp+2B8h] [rbp-38h]
__int64 v27; // [rsp+2C0h] [rbp-30h]
__int64 v28; // [rsp+2C8h] [rbp-28h]
int v29; // [rsp+2D0h] [rbp-20h]
int v30; // [rsp+2D4h] [rbp-1Ch]
char *v31; // [rsp+2D8h] [rbp-18h]
int v32; // [rsp+2E0h] [rbp-10h]
int v33; // [rsp+2E4h] [rbp-Ch]
bool v34; // [rsp+2EBh] [rbp-5h]
v10 = 0;
memset(s, 0, 0x30uLL);
memset(s1, 0, sizeof(s1));
printf("Input:");
v11 = s;
__isoc99_scanf("%s", s);
for ( i = 0; ; ++i )
{
v12 = i;
v13 = i < 64;
if ( i >= 64 )
break;
v14 = s[i];
v15 = v14;
if ( v14 == 10 )
{
v16 = &s[i];
*v16 = 0;
break;
}
v17 = i + 1;
}
for ( j = 0; ; ++j )
{
v18 = j;
v19 = j < 6;
if ( j >= 6 )
break;
v20 = s;
v4 = *&s[8 * j];
for ( k = 0; ; ++k )
{
v21 = k;
v22 = k < 64;
if ( k >= 64 )
break;
v23 = v4;
v24 = v4 < 0;
if ( v4 >= 0 )
{
v27 = v4;
v28 = 2 * v4;
v4 *= 2LL;
}
else
{
v25 = 2 * v4;
v26 = 2 * v4;
v4 = (2 * v4) ^ 0xB0004B7679FA26B3LL;
}
v29 = k;
}
v30 = 8 * j;
v31 = &s1[8 * j];
*v31 = v4;
v32 = j + 1;
}
v33 = memcmp(s1, &unk_402170, 0x30uLL);
v34 = v33 != 0;
if ( v33 )
puts("Wrong!");
else
puts("Correct!");
return v10;
}
分析加密部分,可以看出是对输入值的正负数进行不同的操作,unk_402170中存放着密文。可以通过按B改变数据进制形式。
接着即可写解密脚本,但在写解密脚本时遇到一些困难,解密始终错误,看了一下其他师傅wp才知道,程序中的这个加密算法实际进行的是一个CRC校验。这又是一个不太熟悉的算法,看了这个师傅写的文章学习了一下CRC校验码原理、实例、手动计算 - 步孤天 - 博客园 (cnblogs.com)
然后发现了自己脚本的一些问题,因为加密程序是通过判断正负来进行不同的加密,所以解密脚本中就用判断符号位的方式来决定正负。但这样是错的。修改后脚本如下,注释部分是错误方式:
# from ctypes import *
from Crypto.Util.number import *
def main():
# cipher = [0xBC8FF26D43536296,0x520100780530EE16,0x4DC0B5EA935F08EC,0x342B90AFD853F450,0x8B250EBCAA2C3681,0x55759F81A2C68AE4]
# cipher1 = c_int64()
# for i in range(6):
# cipher1.value = cipher[i]
# for k in range(64):
# if cipher1.value < 0:
# cipher1.value = (cipher1.value ^ 0xB0004B7679FA26B3)//2
# cipher1.value |= 0x8000000000000000
# else:
# cipher1.value = cipher1.value // 2
# print(hex(cipher1.value))
cipher = [0xBC8FF26D43536296,0x520100780530EE16,0x4DC0B5EA935F08EC,0x342B90AFD853F450,0x8B250EBCAA2C3681,0x55759F81A2C68AE4,0xB0004B7679FA26B3]
cc = []
for c in cipher:
for i in range(64):
# if(((c & 0x8000000000000000)>>63) == 1):
if c & 1 == 1:
c = c ^ 0xB0004B7679FA26B3
c = c // 2 # 相当于向右位移一位
c |= 0x8000000000000000
else:
c = c // 2 # 相当于向右位移一位
# print(hex(c))
cc = list(long_to_bytes(c)[::-1])
for d in cc:
print(chr(d),end="")
if __name__ == '__main__':
main()
脚本解密判断条件是"c & 1 == 1",这是在判断密文是奇数还是偶数,根据加密程序可以看出:
如果是正数,乘二之后会成偶数。
如果是负数,乘二之后也会成偶数,但是后面又异或了一个0xB0004B7679FA26B3,这会使值变成奇数。
所以正数加密后始终会是偶数,而负数始终会是奇数。
同时在奇数判断中将值进行"c |= 0x8000000000000000",来保证负数的符号位不会在位移时被变为0。
最后去除所有错误操作的脚本如下:
from Crypto.Util.number import *
def main():
cipher = [0xBC8FF26D43536296,0x520100780530EE16,0x4DC0B5EA935F08EC,0x342B90AFD853F450,0x8B250EBCAA2C3681,0x55759F81A2C68AE4]
cc = []
for c in cipher:
for i in range(64):
if c & 1 == 1:
c = c ^ 0xB0004B7679FA26B3
c = c // 2 # 相当于向右位移一位
c |= 0x8000000000000000
else:
c = c // 2 # 相当于向右位移一位
cc = list(long_to_bytes(c)[::-1])
for d in cc:
print(chr(d),end="")
if __name__ == '__main__':
main()
# flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}
FLAG:flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}