逆向签到题

一道简单的逆向题目,直接将所给的elf文件放进ida中即可获得flag

int __fastcall main(int argc, const char **argv, const char **envp)
{
char s2[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v5; // [rsp+78h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("plz input the key:");
__isoc99_scanf("%s", s2);
if ( !strcmp("flag{7ujm8ikhy6}", s2) )
puts("flag{7ujm8ikhy6}");
else
puts("key error");
return 0;
}

flag为flag{7ujm8ikhy6}

re2

这道题目给了一个enflag.txt文件与勒索病毒.exe文件,查看enflag.txt文件

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/re2$ cat enflag.txt 
�%�L6;Y����2���5

应该是一个加密文件,不知道是什么加密,运行一下.exe文件

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/re2$ ./勒索病毒.exe
**************************我的Flag出了什么问题??**************************
您的一些重要数据被我们加密了,就算您叫破喉咙来也没有办法恢复。
**************************那有没有恢复的方法呢??**************************
有的。只能通过我们的财付通,支付宝服务才能恢复,我以人格担保,只要充钱,就能解密

做出你的选择:
1.充钱
2.退出
1
打开源文件失败!

可以看出有两个选项,一个是充钱,一个是退出,但是你随便点击任何一个选项都会退出程序,因此放进ida中进行分析

if ( *(_DWORD *)p_Str == 1 )
{
v13 = fopen("flag.txt", "r");
if ( !v13 )
{
sub_401037((char *)&Format__2, v4);
getchar();
exit(0);
}
v12 = fopen("enflag.txt", "w");
if ( !v12 )
{
sub_401037((char *)&Format__3, v4);
getchar();
exit(0);
}
sub_401037(asc_406D84, v4);
sub_401073("%s", (char)Str);
sub_401069(Str, Str1);
sub_401028(Str, v15, v14, v13, v12);
}

通过这一段代码就可以知道,得先在本地新创建一个flag.txt文件,才能继续运行下面的代码,所以先创建,然后再运行一次程序

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/re2$ ./Ransomware.exe
**************************我的Flag出了什么问题??**************************
您的一些重要数据被我们加密了,就算您叫破喉咙来也没有办法恢复。
**************************那有没有恢复的方法呢??**************************
有的。只能通过我们的财付通,支付宝服务才能恢复,我以人格担保,只要充钱,就能解密

做出你的选择:
1.充钱
2.退出
1

请输入您的密钥:

首先会让我们输入密钥,输入的字符串会作为函数sub_401069与函数sub_401028的参数运行,首先看一下sub_401069(Str, Str1);

char __cdecl sub_401A70(char *Str, char *Str1)
{
char v3; // [esp+0h] [ebp-E4h]
signed int i; // [esp+D0h] [ebp-14h]
signed int v5; // [esp+DCh] [ebp-8h]

__CheckForDebuggerJustMyCode(&unk_40B027);
v5 = strlen(Str);
for ( i = 0; i < v5; ++i )
Str1[i] += Str[i] ^ 0x1F;
if ( !strcmp(Str1, "DH~mqqvqxB^||zll@Jq~jkwpmvez{") )
sub_401037((char *)&Format__4, v3);
else
sub_401037("Error!\n", v3);
return *Str1;
}

这段代码首先计算了我们输入密钥的长度,接着每个字符与0x1F进行异或,如果异或后得到的结果与字符串DHmqqvqxB^||zll@Jqjkwpmvez{一样,则输出变量Format__4对应的中文字符。因此我们可以通过编写脚本得到我们需要输入的字符串密钥是什么,脚本如下:

str_1 = "DH~mqqvqxB^||zll@Jq~jkwpmvez{"
str_2 = ""
for i in str_1:
str_2 += chr(ord(i) ^ 0x1F)
print(str_2)
haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/re2$ python3 xor_re.py
[Warnning]Access_Unauthorized

得到需要输入的密钥后再次运行一遍程序

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/re2$ ./Ransomware.exe
**************************我的Flag出了什么问题??**************************
您的一些重要数据被我们加密了,就算您叫破喉咙来也没有办法恢复。
**************************那有没有恢复的方法呢??**************************
有的。只能通过我们的财付通,支付宝服务才能恢复,我以人格担保,只要充钱,就能解密

做出你的选择:
1.充钱
2.退出
1

请输入您的密钥:[Warnning]Access_Unauthorized
充值成功.

加密成功!
您的充值金额不足。

**************************我的Flag出了什么问题??**************************
您的一些重要数据被我们加密了,就算您叫破喉咙来也没有办法恢复。
**************************那有没有恢复的方法呢??**************************
有的。只能通过我们的财付通,支付宝服务才能恢复,我以人格担保,只要充钱,就能解密

做出你的选择:
1.充钱
2.退出

行,还是没有flag,那么就跟函数sub_401028(Str, v15, v14, v13, v12);有关了,那么就进一步分析该函数

int __cdecl sub_4014E0(char *Str, int a2, int a3, FILE *Stream, Stream *Stream_1)
{
char v6; // [esp+0h] [ebp-D8h]
size_t v7; // [esp+D0h] [ebp-8h]

__CheckForDebuggerJustMyCode(&unk_40B027);
v7 = strlen(Str);
sub_4010F0(a2, Str, v7);
sub_4010C8(a3);
sub_40116D(a3, a2);
sub_4010EB(a3, Stream, Stream_1);
fclose(Stream);
fclose(Stream_1);
return sub_401037(asc_406B34, v6);
}
其中char *Str这一个参数是我们输入的变量

发现有4个函数操作,看起来有点复杂,一个一个来看吧。

第一个函数

int __cdecl sub_401800(int a1, char *Str, signed int n256_1)
{
int result; // eax
int n256_2; // [esp+D0h] [ebp-14h]
int n256; // [esp+DCh] [ebp-8h]

result = __CheckForDebuggerJustMyCode(&unk_40B027);
if ( n256_1 <= 256 )
{
for ( n256 = 0; n256 < 256; ++n256 )
{
*(_BYTE *)(n256 + a1) = Str[n256 % n256_1];
result = n256 + 1;
}
}
if ( n256_1 > 256 )
{
for ( n256_2 = 0; n256_2 < 256; ++n256_2 )
{
*(_BYTE *)(n256_2 + a1) = Str[n256_2];
result = n256_2 + 1;
}
}
return result;
}
其中n256_1就是我们输入的字符串的长度

这一段代码主要实现的是将输入的密钥填充进一个大小为256个字节的临时数组中,若密钥长度不足256个字节,则反复存入,若超过,则只取前256个字节。

第二个函数

int __cdecl sub_401780(int a1)
{
int result; // eax
int n256; // [esp+D0h] [ebp-8h]

result = __CheckForDebuggerJustMyCode(&unk_40B027);
for ( n256 = 0; n256 < 256; ++n256 )
{
*(_BYTE *)(n256 + a1) = n256;
result = n256 + 1;
}
return result;
}

这一段代码是初始化一个数组(S盒),将传入的一个256个字节初始化为0,1,2……255。

第三个函数

int __cdecl sub_4018E0(int a1, int a2)
{
int result; // eax
int n256; // [esp+D0h] [ebp-2Ch]
char v4; // [esp+EBh] [ebp-11h]
int v5; // [esp+F4h] [ebp-8h]

result = __CheckForDebuggerJustMyCode(&unk_40B027);
v5 = 0;
for ( n256 = 0; n256 < 256; ++n256 )
{
v5 = (*(unsigned __int8 *)(n256 + a2) + v5 + *(unsigned __int8 *)(n256 + a1)) % 256;
v4 = *(_BYTE *)(n256 + a1);
*(_BYTE *)(n256 + a1) = *(_BYTE *)(v5 + a1);
*(_BYTE *)(v5 + a1) = v4;
result = n256 + 1;
}
return result;
}

这一段代码是一个密钥调度算法,是将我们得到的输入密钥的临时数组去打乱初始化的S盒,完成RC4的初始化置换

第四个函数

int __cdecl sub_4015E0(int a1, FILE *Stream, FILE *Stream_1)
{
int result; // eax
char v4; // [esp+103h] [ebp-35h]
char i; // [esp+11Bh] [ebp-1Dh]
int v6; // [esp+124h] [ebp-14h]
int v7; // [esp+130h] [ebp-8h]

__CheckForDebuggerJustMyCode(&unk_40B027);
v7 = 0;
v6 = 0;
for ( i = fgetc(Stream); ; i = fgetc(Stream) )
{
result = i;
if ( i == -1 )
break;
v7 = (v7 + 1) % 256;
v6 = (v6 + *(unsigned __int8 *)(v7 + a1)) % 256;
v4 = *(_BYTE *)(v7 + a1);
*(_BYTE *)(v7 + a1) = *(_BYTE *)(v6 + a1);
*(_BYTE *)(v6 + a1) = v4;
fputc(*(_BYTE *)((*(unsigned __int8 *)(v6 + a1) + *(unsigned __int8 *)(v7 + a1)) % 256 + a1) ^ i, Stream_1);
}
return result;
}

这一段代码的作用就是逐字节读取输入流,用打乱后的 S 盒生成伪随机密钥字节,并与明/密文异或,写入输出流。

总的来说,这几个函数的作用就是实现RC4流密码算法,包括密钥调度和加解密过程。那么什么是RC4流密码算法?RC4是一种由Ron Rivest于1987年设计的经典对称流密码算法,密钥长度可变(1~256字节),通过密钥调度算法(KSA)将初始有序的S盒打乱为密钥依赖的伪随机排列,再经伪随机生成算法(PRGA)持续输出密钥流,与明文逐字节异或实现加解密;其代码极其简洁高效,曾广泛用于WEP、SSL/TLS等协议,但因密钥流存在统计偏差和易受FMS等攻击,现已被视为不安全并逐渐被AES-GCM等现代算法取代。

OK,大致知道了题目所给的enflag.txt文件是经过了RC4加密得到的,我们又知道了密钥,那么就可以编写解密脚本从而得到flag了。解密脚本如下:

def rc4_crypt(data: bytes, key: bytes) -> bytes:
"""RC4 加解密(加密与解密操作完全相同)"""
# 1. 初始化 S 盒 0~255
S = list(range(256))

# 2. 密钥调度算法 (KSA)
j = 0
key_len = len(key)
for i in range(256):
j = (j + S[i] + key[i % key_len]) & 0xFF
S[i], S[j] = S[j], S[i]

# 3. 伪随机生成与异或 (PRGA)
i = j = 0
result = bytearray()
for byte in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) & 0xFF
keystream_byte = S[t]
result.append(byte ^ keystream_byte)

return bytes(result)

# 读取密文文件
with open('enflag.txt', 'rb') as f:
ciphertext = f.read()

# 密钥(注意直接使用字符串,需编码为字节)
key = b"[Warnning]Access_Unauthorized"

# 解密(RC4 的加密与解密相同)
plaintext = rc4_crypt(ciphertext, key)

# 输出结果(通常 flag 是可见字符串)
print(plaintext.decode('utf-8', errors='replace'))

re3

放入ida中进行分析

v21 = __readfsqword(0x28u);
n80 = 80;
n64227 = 0xFAE3;
n226312059 = 0xD7D3F7B;
v10 = 0xA43499F6;
n5 = 5;
n16 = 0x10;
n3833 = 0xEF9;
v5 = 0;
puts("plz input the key:");
__isoc99_scanf("%s", s);
v3 = strlen(s);
strncpy(dest, src_, v3 - 6);
dest[strlen(s) - 6] = 0;
__isoc99_sscanf(dest, "%x", &v5);
v17[0] = n80;
v17[1] = n64227;
v17[2] = n226312059;
v17[3] = v10;
v17[4] = (n5 << 12) + n16;
v17[5] = n3833;
v17[6] = v5;
n0xFFFF = 0;
for ( n6 = 0; n6 <= 6; ++n6 )
{
for ( n0xFFFF += (unsigned int)v17[n6]; n0xFFFF > 0xFFFF; n0xFFFF = v15 + (unsigned int)(unsigned __int16)n0xFFFF )
{
n0xFFFF_1 = (unsigned __int16)n0xFFFF;
v15 = n0xFFFF >> 16;
}
}
if ( n0xFFFF == 0xFFFF )
puts("OK");
else
puts("Error");

由于没有长度限制,我们输入的字符串如果超过了5字节会覆盖到src_中,发现这道题就是一个累加的过程,前6个数是固定的,因此加起来是一个定值,通过动调看一下

0x5555554009bb <main+449>    cmp    qword ptr [rbp - 0x108], 0xffff     0xe560 - 0xffff     EFLAGS => 0x293 [ CF pf AF zf SF IF df of ac ]

可以知道是0xe560,那么就可以计算出我们需要输入0x1A9F,因为这道题的flag是4位值,因此直接1A9F就行了,想要程序输出OK,就必须得在1A9F前面再输入其他5个字符,这样1A9F才会被存入到V5中。

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re$ ./re3
plz input the key:
aaaaa1a9fa
OK

逆向4

首先运行一下程序

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re$ ./mingyue.exe
请输入正确的数字:
aa

是一个猜数字的程序,再将其放入到ida中进行逆向分析

void __fastcall __noreturn encode(char *a1, __int64 a2)
{
int v2; // r9d
__int64 v3; // r8
char *v4; // r10
char v5; // al
__int64 v6; // rbx
unsigned __int8 v7; // cl
char v8; // [rsp+1Fh] [rbp-3F9h]
char v9; // [rsp+20h] [rbp-3F8h] BYREF

v2 = 0;
v3 = (__int64)a1;
if ( a1 )
{
v4 = &v9;
do
{
++v4;
++v2;
a1 = &a4890572163qwe[-26 * (v3 / 26)]; //)(*&^%489$!057@#><:2163qwe
v5 = a1[v3];
v3 /= 26;
a2 = v3;
*(v4 - 1) = v5;
}
while ( v3 );
}
v6 = v2;
while ( v6 )
{
v7 = *(&v8 + v6--);
sub_1400011E0(v7 ^ 7u, a2, v3);
}
sub_140001220(a1, a2, v3);
}

这里是将v3当作26进制数进行处理的。

这部分最难以理解的部分就是

a1 = &a4890572163qwe[-26 * (v3 / 26)];    // )(*&^%489$!057@#><:2163qwe
v5 = a1[v3];
v3 /= 26;
a2 = v3;

首先-26 * (v3 / 26)相当于v3 % 26 - v3,因为在这里v3=26*(v3/26)+v3%26。因此a1 = &a4890572163qwe[v3 % 26 - v3];而v5=a1[v3]就相当于v5=a1[v3%26],然后v3再整除26并保存到a2中。最后保存修改后的字符串。

接着进入函数sub_1400011E0(v7 ^ 7u, a2, v3);

_QWORD *__fastcall sub_1400011E0(char a1)
{
_QWORD *result; // rax
__int64 v3; // rdx

result = malloc(0x10u);
v3 = qword_140004618;
qword_140004618 = (__int64)result;
*(_QWORD *)(v3 + 8) = result;
*(_BYTE *)v3 = a1;
result[1] = 0;
return result;
}

这段代码就是将异或得到的东西放入到一段空间里面。

再来看sub_140001220()

void __noreturn sub_140001220()
{
__int64 v0; // r9
int n14_3; // ecx
__int64 n14; // rdx
char v3; // al
int n14_2; // r8d
__int64 v5; // r9
char v6; // cl
int n14_1; // eax

v0 = qword_140004620;
n14_3 = 0;
n14 = 0;
while ( 1 )
{
v3 = *(_BYTE *)v0;
n14_2 = n14_3 + 1;
v5 = *(_QWORD *)(v0 + 8);
if ( v3 != aV4pY59[n14] ) // /..v4p$$!>Y59-
n14_2 = n14_3;
qword_140004620 = v5;
if ( !v5 )
break;
v6 = *(_BYTE *)v5;
n14_1 = n14_2 + 1;
v0 = *(_QWORD *)(v5 + 8);
if ( v6 != aV4pY59[n14 + 1] )
n14_1 = n14_2;
qword_140004620 = v0;
if ( v0 )
{
n14 += 2;
n14_3 = n14_1;
if ( n14 < 14 )
continue;
}
goto LABEL_11;
}
n14_1 = n14_2;
LABEL_11:
if ( n14_1 == 14 )
sub_1400012E0();
sub_1400012B0();
}

此时我们知道了,关键在于n14_1是否等于14,若等于就可以获得flag了。简单来说,这段代码就是一个校验过程,判断异或结果的字符串是否与字符串/..v4p$$!>Y59-相同。

大致了解了这个程序的功能是什么了,就是让我们输入一个数字,接着将这个数字进行处理后按索引在字符串)(*&^%489$!057@#><:2163qwe里面找到相应字符并保存后得到新的字符串,再将这个字符串与7异或后与字符串/..v4p$$!>Y59-比较是否一致,一致则那串数字就是flag。接下来就是编写脚本了

str1 = ')(*&^%489$!057@#><:2163qwe'
str2 = '/..v4p$$!>Y59-'
v7 = ''
a = []

for i in range(len(str2)):
v7 += chr(ord(str2[i]) ^ 7)

for j in v7:
a.append(str1.index(j))

flag = 0

for z in range(len(a)):
flag *= 26
flag += a[z]

print(flag)

逆向5

这道题的关键点在于函数sub_4015BD(1);

int (*__stdcall sub_4015BD(int a1))()
{
int (*_dba54edb0?d6_7??3ef0f1caf2ad3102_)(); // eax
int (*p_sub_401520)(); // [esp+24h] [ebp+4h]

_dba54edb0?d6_7??3ef0f1caf2ad3102_ = (int (*)())(unsigned __int8)Str[1];// "dba54edb0?d6>7??3ef0f1caf2ad3102"
if ( Str[1] == 1 ) // "dba54edb0?d6>7??3ef0f1caf2ad3102"
{
_dba54edb0?d6_7??3ef0f1caf2ad3102_ = sub_401520;
p_sub_401520 = sub_401520;
}
return _dba54edb0?d6_7??3ef0f1caf2ad3102_;
}

给了我们一个字符串dba54edb0?d6>7??3ef0f1caf2ad3102,接着发现(*p_sub_401520)()这个是返回地址,也就是说执行完这个函数会执行sub_401520这个函数,再来看看sub_401520这个函数以及这个的返回函数sub_40163E(i)

int sub_401520()
{
FARPROC H; // [esp+14h] [ebp-14h]
HMODULE hModule; // [esp+18h] [ebp-10h]
size_t i; // [esp+1Ch] [ebp-Ch]

hModule = LoadLibraryA("1.dll");
H = GetProcAddress(hModule, "H");
// "dba54edb0?d6>7??3ef0f1caf2ad3102"
for ( i = 0; i < strlen(Str); ++i )
{
a12345678901111[i] = Str[i]; // "dba54edb0?d6>7??3ef0f1caf2ad3102"
a12345678901111[i] = ((int (__cdecl *)(_DWORD))H)(a12345678901111[i]);// "12345678901111111222222222222222"
}
return sub_40163E(i);
}
size_t sub_40163E()
{
size_t _dba54edb0?d6_7??3ef0f1caf2ad3102_; // eax
size_t _dba54edb0?d6_7??3ef0f1caf2ad3102__1; // [esp+1Ch] [ebp-Ch]

for ( _dba54edb0?d6_7??3ef0f1caf2ad3102__1 = 0; ; ++_dba54edb0?d6_7??3ef0f1caf2ad3102__1 )
{
_dba54edb0?d6_7??3ef0f1caf2ad3102_ = strlen(Str);// "dba54edb0?d6>7??3ef0f1caf2ad3102"
if ( _dba54edb0?d6_7??3ef0f1caf2ad3102__1 >= _dba54edb0?d6_7??3ef0f1caf2ad3102_ )
break;
putchar(a12345678901111[_dba54edb0?d6_7??3ef0f1caf2ad3102__1]);// "12345678901111111222222222222222"
}
dword_405020 = 1;
return _dba54edb0?d6_7??3ef0f1caf2ad3102_;
}

我大致猜测这个就是输出flag的函数。那么这道题关键就在于这个1.dll文件了。这个文件加了壳,使用工具将其脱壳后,查看函数H干了什么

int __cdecl H(unsigned __int8 a1)
{
return a1 ^ 7;
}

发现就是简单的与7进行异或,那么就简单了,直接写脚本就行了

str_1 = "dba54edb0?d6>7??3ef0f1caf2ad3102"
flag = ""
for i in str_1:
flag += chr(ord(i)^7)
print(flag)

红包题 武穆遗书

这道题加了壳,使用upx脱壳工具进行脱壳,工具下载与使用命令如下

sudo apt update && sudo apt install upx
upx -d re6.exe

接着,放入ida中进行分析

int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v4; // [esp+0h] [ebp-3Ch]
_BYTE v5[4]; // [esp+4h] [ebp-38h] BYREF
char v6[52]; // [esp+8h] [ebp-34h] BYREF

sub_4011D0();
sub_401200();
sub_401280();
if ( sub_401040() )
exit(0);
v4 = (const char *)operator new(0x1Cu);
sub_401390(v4, v5, &unk_4070F4, 28);
if ( sub_4010E0() )
exit(0);
while ( 1 )
{
printf("input the password less than 50 char:\n");
gets(v6);
fflush((FILE *)iob[0]._ptr);
if ( !strcmp(v4, v6) )
break;
printf("password error!!! please try again!\nyour input is %s \n", v6);
}
printf("win!!!the password and your input are all %s\n", v6);
system((const char *)Command); // "pause"
return 0;
}

这道题看起来是有点复杂了,于是采用动调的方式进行分析,因为这道题在前面几个函数里面有判断然后退出程序,因此就需要在前面下断点,该nop的nop,该改值的改值。最后就能得到flag了。这道题主要考察的就是是否会使用ida进行动态调试。

红包六

给了一个jar包文件,但一般来说,可以把这个就看作是一个压缩包,解压后得到Java代码

import java.util.Base64;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;

public class EzJar {
//hint: flag not here
public static void main(String[] args) throws Exception {
JOptionPane.showMessageDialog(null, "Give me your flag:", "alert", JOptionPane.QUESTION_MESSAGE);
System.out.print("Give me your flag:");
Scanner sc = new Scanner(System.in);
String s = sc.next();
Cipher cipher = Cipher.getInstance("DES");
cipher.init(1, new SecretKeySpec("easy_key".getBytes(), "DES"));
String result = new String(Base64.getEncoder().encode(cipher.doFinal(s.getBytes())));
System.out.println(result);
if ("UUwnbEk0rzJT9G+ET6MU+Y+6ChoFhCceRnfdDTcuEeJ9+6qwaZFV3w==".equals(result)) {
JOptionPane.showMessageDialog(null, "Accept!");
System.out.print("Accept!");
} else {
JOptionPane.showMessageDialog(null, "Wrong answer!");
System.out.print("Wrong answer!");
}
}
}

知道了这是一个DES与base64一起加密的程序,最后进行解密就可以得到flag了是不是?当然不是因为这里有个提示

//hint: flag not here

行,那么就又只能乖乖返回那个jar包进行分析了。将这个放入到010中发现有压缩包,那么使用脚本提取出来

import zlib

inflator = zlib.decompressobj(-zlib.MAX_WBITS)

f = open("EzJar.jar", "rb")
f.seek(659)
a = f.read(3248)
f.close()

x = inflator.decompress(a)

f = open("EzJar.class", "wb")
f.write(x)
f.close()

将得到的文件放入jadx中进行分析

package defpackage;

import java.awt.Component;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JOptionPane;

/* renamed from: EzJar reason: default package */
/* loaded from: EzJar.class */
public class EzJar {
private static final /* synthetic */ String[] I = null;
private static final /* synthetic */ int[] l = null;
private static final /* synthetic */ String banner;
private static final /* synthetic */ String flag;
private static final /* synthetic */ String WA;
private static final /* synthetic */ String AC;
private static final /* synthetic */ String enc;
private static final /* synthetic */ String key;

public static void main(String[] strArr) throws Exception {
JOptionPane.showMessageDialog((Component) null, I[l[0]], I[l[1]], l[2]);
System.out.print(I[l[3]]);
String next = new Scanner(System.in).next();
Cipher cipher = Cipher.getInstance(I[l[2]]);
cipher.init(l[1], new SecretKeySpec(I[l[4]].getBytes(), I[l[5]]));
if (!lIl(I[l[6]].equals(new String(Base64.getEncoder().encode(cipher.doFinal(next.getBytes())))) ? 1 : 0)) {
JOptionPane.showMessageDialog((Component) null, I[l[9]]);
System.out.print(I[l[10]]);
return;
}
JOptionPane.showMessageDialog((Component) null, I[l[7]]);
System.out.print(I[l[8]]);
"".length();
if (0 == 0) {
}
}

private static void lII() {
l = new int[18];
l[0] = (156 ^ 136) & ((127 ^ 107) ^ (-1));
l[1] = " ".length();
l[2] = " ".length();
l[3] = " ".length();
l[4] = (185 ^ 197) ^ (32 ^ 88);
l[5] = " ".length() ^ (28 ^ 24);
l[6] = 16 ^ 22;
l[7] = (69 ^ 77) ^ (70 ^ 73);
l[8] = (((106 + 107) - 66) + 32) ^ (((33 + 90) - 98) + 162);
l[9] = (((154 + 21) - 48) + 38) ^ (((78 + 146) - 163) + 111);
l[10] = 29 ^ 23;
l[11] = 64 ^ 75;
l[12] = 105 ^ 101;
l[13] = " ".length() ^ (106 ^ 101);
l[14] = 72 ^ 70;
l[15] = 144 ^ 159;
l[16] = 160 ^ 176;
l[17] = 177 ^ 160;
}

private static String lI(String lllllllllIlIlII, String lllllllllIlIlIl) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(MessageDigest.getInstance("MD5").digest(lllllllllIlIlIl.getBytes(StandardCharsets.UTF_8)), "Blowfish");
Cipher lllllllllIllIII = Cipher.getInstance("Blowfish");
lllllllllIllIII.init(l[3], secretKeySpec);
return new String(lllllllllIllIII.doFinal(Base64.getDecoder().decode(lllllllllIlIlII.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
} catch (Exception lllllllllIlIlll) {
lllllllllIlIlll.printStackTrace();
return null;
}
}

private static String I(String lllllllllIIlIIl, String lllllllllIIlIII) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(Arrays.copyOf(MessageDigest.getInstance("MD5").digest(lllllllllIIlIII.getBytes(StandardCharsets.UTF_8)), l[8]), "DES");
Cipher lllllllllIIlIll = Cipher.getInstance("DES");
lllllllllIIlIll.init(l[3], secretKeySpec);
return new String(lllllllllIIlIll.doFinal(Base64.getDecoder().decode(lllllllllIIlIIl.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
} catch (Exception lllllllllIIlIlI) {
lllllllllIIlIlI.printStackTrace();
return null;
}
}

private static void ll() {
I = new String[l[17]];
I[l[0]] = I("AQiA0bYffm9HvMlm7RnEMX/tEQAUj4Xb", "FrlAZ");
I[l[1]] = I("hXzZyx8IUHw=", "Esxsh");
I[l[3]] = l("ID8PFlEKM1kKHhIkWRUdBjFD", "gVysq");
I[l[2]] = I("50fO6ARqllg=", "VZbFF");
I[l[4]] = lI("mvXqH+/XIESPZaSG3ZbZlA==", "TuZSw");
I[l[5]] = l("JQ0R", "aHBFu");
I[l[6]] = I("dMKiRQ19iTevvzL7NtVg5+ye5BywL2QaxtVANFLuC5B2/KuC+/5L6BwtCB7zpWK1XBTQr0VWC3Vt/uYEl2xmjskE0dDrCk2C", "dPxYA");
I[l[7]] = lI("B/MVYKSzgq8=", "phiUP");
I[l[8]] = I("ZtBOhuHeK3Y=", "MfnkQ");
I[l[9]] = lI("aPhz+GjGynRlU3Alo00QeQ==", "wWtUj");
I[l[10]] = l("BRQFNiRyBwQrNDcUSw==", "RfjXC");
I[l[11]] = I("pT10j0lChvyrNwYRFdqBzxqFp1ruTgo9", "hGcuT");
I[l[12]] = lI("UhBbCDk5yqaWl1uHJyS/OGmtcfVyvOOsk78/1f0MU8U3UfAf1Xf0FWNbpcKes/0HRz9SU/icRJHswW2xWjHrcFzhpsvwzqUl", "eeMoV");
I[l[13]] = I("s11BihYBzRBpcX9EF43utw==", "RjiXK");
I[l[14]] = I("wk5jH1cyKoA=", "frsxP");
I[l[15]] = I("Nfa0rxB8IRArMq2F4iLlLg==", "ulQWJ");
I[l[16]] = lI("wLcWNd0Xsbw=", "JgPGn");
}

static {
lII();
ll();
banner = I[l[11]];
flag = I[l[12]];
key = I[l[13]];
enc = I[l[14]];
WA = I[l[15]];
AC = I[l[16]];
}

private static boolean llI(int i, int i2) {
return i < i2;
}

private static String l(String llllllllllIlIll, String llllllllllIlIlI) {
String str = new String(Base64.getDecoder().decode(llllllllllIlIll.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
StringBuilder llllllllllIlIIl = new StringBuilder();
char[] llllllllllIlIII = llllllllllIlIlI.toCharArray();
int llllllllllIIIlI = l[0];
char[] charArray = str.toCharArray();
int length = charArray.length;
int i = l[0];
while (llI(i, length)) {
char llllllllllIllII = charArray[i];
llllllllllIlIIl.append((char) (llllllllllIllII ^ llllllllllIlIII[llllllllllIIIlI % llllllllllIlIII.length]));
"".length();
llllllllllIIIlI++;
i++;
"".length();
if ((-" ".length()) > " ".length()) {
return null;
}
}
return String.valueOf(llllllllllIlIIl);
}

private static boolean lIl(int i) {
return i != 0;
}
}

这段代码有点长,但大致就是密钥和密文都被加密了,得先解出密文和密钥,再写脚本得到密码,这个密码就是flag。

因此我们需要分别得出密文(I[6])和密钥(I[4]),脚本如下

# get_expected_cipher.py
import base64
import hashlib
from Crypto.Cipher import DES

# 密文与密钥种子
cipher_b64 = "dMKiRQ19iTevvzL7NtVg5+ye5BywL2QaxtVANFLuC5B2/KuC+/5L6BwtCB7zpWK1XBTQr0VWC3Vt/uYEl2xmjskE0dDrCk2C"
key_seed = "dPxYA"

# 计算 DES 密钥:MD5("dPxYA") 的前 8 字节
full_md5 = hashlib.md5(key_seed.encode()).digest()
des_key = full_md5[:8]

# Base64 解码 → DES 解密
ciphertext = base64.b64decode(cipher_b64)
cipher = DES.new(des_key, DES.MODE_ECB)
plain = cipher.decrypt(ciphertext)

# 去除 PKCS5 填充
pad_len = plain[-1]
expected_base64 = plain[:-pad_len].decode()

print("Expected cipher (Base64):", expected_base64)
# 结果为 Dg/TZuRXF4+UwSZ8Dpwgw8+VOoHVl1YlPL1QRVhroCy4ptnKEcdC05iXcpLyDnuR
# get_des_key.py
import base64
import hashlib
from Crypto.Cipher import Blowfish

# 密文与密钥种子
cipher_b64 = "mvXqH+/XIESPZaSG3ZbZlA=="
key_seed = "TuZSw"

# 计算 Blowfish 密钥:MD5("TuZSw")
key = hashlib.md5(key_seed.encode()).digest()

# Base64 解码 → Blowfish 解密
ciphertext = base64.b64decode(cipher_b64)
cipher = Blowfish.new(key, Blowfish.MODE_ECB)
plain = cipher.decrypt(ciphertext)

# 去除 PKCS5 填充
pad_len = plain[-1]
des_key = plain[:-pad_len].decode()

print("DES key:", des_key) # 结果为 Ctf3r_me

得到最后的flag的脚本如下:

import base64
from Crypto.Cipher import DES

# 密文 (Base64)
cipher_b64 = "Dg/TZuRXF4+UwSZ8Dpwgw8+VOoHVl1YlPL1QRVhroCy4ptnKEcdC05iXcpLyDnuR"
ciphertext = base64.b64decode(cipher_b64)

# 密钥(8字节,正好满足DES)
key = b"Ctf3r_me"

# DES 解密,默认 ECB 模式,PKCS5 填充
cipher = DES.new(key, DES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)

# 去除填充 (PKCS5)
pad_len = plaintext[-1]
flag = plaintext[:-pad_len].decode()
print(flag)

萌新赛

数学不及格

int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int n200; // [rsp+14h] [rbp-4Ch]
char *endptr; // [rsp+18h] [rbp-48h] BYREF
char *endptr_; // [rsp+20h] [rbp-40h] BYREF
char *endptr__1; // [rsp+28h] [rbp-38h] BYREF
char *endptr__2; // [rsp+30h] [rbp-30h] BYREF
__int64 v9; // [rsp+38h] [rbp-28h]
__int64 v10; // [rsp+40h] [rbp-20h]
__int64 v11; // [rsp+48h] [rbp-18h]
__int64 v12; // [rsp+50h] [rbp-10h]
unsigned __int64 v13; // [rsp+58h] [rbp-8h]

v13 = __readfsqword(0x28u);
if ( argc != 5 )
{
puts("argc nonono");
exit(1);
}
n200 = strtol(argv[4], &endptr, 16) - 0x6543;
v9 = f(n200);
v10 = strtol(argv[1], &endptr_, 0x10);
v11 = strtol(argv[2], &endptr__1, 0x10);
v12 = strtol(argv[3], &endptr__2, 0x10);
if ( v9 - v10 != 0x233F0E151CLL )
{
puts("argv1 nonono!");
exit(1);
}
if ( v9 - v11 != 0x1B45F81A32LL )
{
puts("argv2 nonono!");
exit(1);
}
if ( v9 - v12 != 0x244C071725LL )
{
puts("argv3 nonono!");
exit(1);
}
if ( (int)n200 + v12 + v11 + v10 != 0x13A31412F8CLL )
{
puts("argv sum nonono!");
exit(1);
}
puts("well done!decode your argv!");
return 0;
}

首先得知道long int strtol(const char *str, char endptr, int base);函数,把参数 str所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0。接着这个程序就是进行一系列的数学计算问题,如果结果正确,再将这个一大串数字转化为字符串就是flag了。

再来看一下f()函数

__int64 __fastcall f(int n200)
{
int n2; // [rsp+1Ch] [rbp-14h]
__int64 v3; // [rsp+20h] [rbp-10h]
_QWORD *ptr; // [rsp+28h] [rbp-8h]

if ( n200 <= 1 || n200 > 200 )
return 0;
ptr = malloc(8LL * n200);
*ptr = 1;
ptr[1] = 1;
v3 = 0;
for ( n2 = 2; n2 < n200; ++n2 )
{
ptr[n2] = ptr[n2 - 1] + ptr[n2 - 2];
v3 = ptr[n2];
}
free(ptr);
return v3;
}

这实现的是斐波那契数列。

接下来就是直接上脚本就行了

def fib(n):
"""返回第 n 个斐波那契数 (fib(1)=1, fib(2)=1)"""
if n <= 0:
return 0
a, b = 1, 1
for _ in range(3, n + 1):
a, b = b, a + b
return b

K1 = 0x233F0E151C
K2 = 0x1B45F81A32
K3 = 0x244C071725
TARGET_SUM = 0x13A31412F8C

for n200 in range(2, 201):
v9 = fib(n200)
v10 = v9 - K1
v11 = v9 - K2
v12 = v9 - K3
if n200 + v10 + v11 + v12 == TARGET_SUM:
print(f"[+] Found n200 = {n200}")
print(f" v9 = {v9}")
print(f" v10 = {v10:#x}")
print(f" v11 = {v11:#x}")
print(f" v12 = {v12:#x}")

argv1 = hex(v10)
argv2 = hex(v11)
argv3 = hex(v12)
argv4 = hex(n200 + 0x6543)

print("\n[+] Command line arguments (hex, without 0x if preferred):")
print(f" argv[1] = {argv1}")
print(f" argv[2] = {argv2}")
print(f" argv[3] = {argv3}")
print(f" argv[4] = {argv4}")
break
else:
print("[-] No valid n200 found in range [2,200].")
argv[1] = 0x666c61677b
argv[2] = 0x6e65776265
argv[3] = 0x655f686572
argv[4] = 0x657d

再将得到的参数通过ASCII码转换为字符串得到最后的flag

argv = [0x666c61677b, 0x6e65776265, 0x655f686572, 0x657d]
flag_bytes = b''.join(x.to_bytes((x.bit_length() + 7) // 8, 'big') for x in argv)
flag = flag_bytes.decode('ascii')
print(flag)

flag白给

这个程序加了壳的,先用upx工具进行脱壳后,再放入ida中进行分析。

关键是下面这段代码

int __fastcall TForm1_Button1Click(int a1)
{
int System::AnsiString; // edx
unsigned int v3[2]; // [esp-10h] [ebp-14h] BYREF
int *v4; // [esp-8h] [ebp-Ch]
int v5; // [esp+0h] [ebp-4h] BYREF
int savedregs; // [esp+4h] [ebp+0h] BYREF

v5 = 0;
v4 = &savedregs;
v3[1] = (unsigned int)&loc_454799;
v3[0] = (unsigned int)NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)v3);
Controls::TControl::GetText(*(Controls::TControl **)(a1 + 764));
if ( Sysutils::CompareStr(v5, &str_HackAv[1]) )
Dialogs::ShowMessage((Dialogs *)&str_____[1], System::AnsiString);
else
Dialogs::ShowMessage((Dialogs *)&str______[1], System::AnsiString);
__writefsdword(0, v3[0]);
v4 = (int *)&loc_4547A0;
return System::__linkproc__ LStrClr(&v5);
}

主要就是将我们输入的字符串与&str_HackAv[1]进行比较,那么就再来看一下&str_HackAv[1]里面存放了什么内容

CODE:004547A4 _str_HackAv     dd 0FFFFFFFFh           ; _top
CODE:004547A4 ; DATA XREF: _TForm1_Button1Click+27↑o
CODE:004547A8 dd 6 ; Len
CODE:004547AC db 'HackAv',0 ; Text

输入HackAv试一下,程序输出成功了,那么这就是我们要的flag。

签退

给了一个.pyc文件,这是由python解释器在导入或执行python脚本时生成的编译字节码文件,是一个二进制文件。可以使用uncompyle6将python的字节码文件反编译回可读的python源代码。下载与使用如下:

haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/萌新赛$ pipx install uncompyle6
installed package uncompyle6 3.9.3, installed using Python 3.12.3
These apps are now globally available
- uncompyle6
- uncompyle6-tokenize
done! ✨ 🌟 ✨
haoo@LAPTOP-QH6DKIQF:/mnt/d/CTF/CTFSHOW/re/萌新赛$ uncompyle6 re3.pyc > re3.py

打开得到的re3.py文件

# uncompyle6 version 3.9.3
# Python bytecode version base 2.7 (62211)
# Decompiled from: Python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0]
# Embedded file name: re3.py
# Compiled at: 2020-03-06 17:43:28
import string
c_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '()'
flag = 'BozjB3vlZ3ThBn9bZ2jhOH93ZaH9'

def encode(origin_bytes):
c_bytes = [('{:0>8}').format(str(bin(b)).replace('0b', '')) for b in origin_bytes]
resp = ''
nums = len(c_bytes) // 3
remain = len(c_bytes) % 3
integral_part = c_bytes[0:3 * nums]
while integral_part:
tmp_unit = ('').join(integral_part[0:3])
tmp_unit = [int(tmp_unit[x:x + 6], 2) for x in [0, 6, 12, 18]]
resp += ('').join([c_charset[i] for i in tmp_unit])
integral_part = integral_part[3:]

if remain:
remain_part = ('').join(c_bytes[3 * nums:]) + (3 - remain) * '0' * 8
tmp_unit = [int(remain_part[x:x + 6], 2) for x in [0, 6, 12, 18]][:remain + 1]
resp += ('').join([c_charset[i] for i in tmp_unit]) + (3 - remain) * '.'
return rend(resp)


def rend(s):

def encodeCh(ch):
f = lambda x: chr((ord(ch) - x + 2) % 26 + x)
if ch.islower():
return f(97)
if ch.isupper():
return f(65)
return ch

return ('').join(encodeCh(c) for c in s)


return

# okay decompiling re3.pyc

大致就是字符串’BozjB3vlZ3ThBn9bZ2jhOH93ZaH9’是密文,先通过base64编码再通过凯撒密码进行加密得到的,因此我们需要先将其进行凯撒密码进行解密,再进行base64的解码得到最后的flag。脚本如下:

import string

# 自定义字符集
c_charset = string.ascii_uppercase + string.ascii_lowercase + string.digits + '()'

# 凯撒逆变换(字母前移2位)
def unrend(s):
def decode_ch(ch):
if ch.islower():
return chr((ord(ch) - 97 - 2) % 26 + 97)
elif ch.isupper():
return chr((ord(ch) - 65 - 2) % 26 + 65)
else:
return ch
return ''.join(decode_ch(c) for c in s)

# 自定义 Base64 解码
def custom_b64_decode(s):
# 移除填充点(如果有)
s = s.rstrip('.')
binary_str = ''
for ch in s:
idx = c_charset.index(ch)
binary_str += '{:06b}'.format(idx)
# 按8位分割还原字节
bytes_list = []
for i in range(0, len(binary_str), 8):
byte_chunk = binary_str[i:i+8]
if len(byte_chunk) == 8:
bytes_list.append(int(byte_chunk, 2))
return bytes(bytes_list)

# 目标密文
flag_encoded = 'BozjB3vlZ3ThBn9bZ2jhOH93ZaH9'

# 第一步:凯撒逆向
b64_str = unrend(flag_encoded)
print(f'凯撒逆向后的 Base64 串:{b64_str}')

# 第二步:自定义 Base64 解码
original_bytes = custom_b64_decode(b64_str)
flag = original_bytes.decode('ascii')
print(f'解码结果:{flag}')