主要是校赛的复现
Wuyu6a的猫猫寻找器
android逆向,正常拖jadx,发现rc4加密,解出来是‘youarejokkkker’,从别的师傅那了解MT资源管理器,一些信息可能藏在数据目录下,发现了一个新的apk,导出拖jadx解rc4得到真正flag。
rc4
有反调试
进程id的范围:win32是1~32,767,win64是1~4,294,967,295。
v0是根据当前时间的时间戳生成的
肯定是需要爆破找出来id
还有一个对文件读取,重命名的操作。
还有一个chacha20加密(说是通过字符串expand 32-byte k搜索得到),又去学了一下
chacha20是对称加密,加解密算法一样。是个矩阵加密。
有很多新东西,一点一点慢慢啃的
1.时间戳 2.rc4 3.chacha20 4.enc文件读取 5.反调试 6.进程id
还是有一点没搞懂,但逻辑已经很清晰了。
main.cpp
#include <stdio.h>
#include <windows.h>
#include "chacha20.h"
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len) //初始化函数
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; // 交换s[i]和s[j]
s[j] = tmp;
}
}
void rc4_crypt(unsigned char* s, unsigned char* Data, unsigned long Len) //加解密
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
void get_flag(unsigned char* mykey, int v0, int pid) {
unsigned char s[256] = { 0 };
unsigned char key[12] = "Encrypted!!";
char hexData[48] = { 0xA3,0xA0,0xB2,0x7F,0xD1,0xB8,0xCA,0x06,0xDB,0xC6,0xA4,0x22,0x3B,0x2A,0x85,0x6E
,0x82,0x55,0x73,0xC1,0x0C,0x40,0xE8,0x81,0xCA,0x9A,0x62,0x01,0x31,0xAF,0xAF,0x70
,0x64,0xD3,0xBC,0xD7,0x6D,0x12,0xF4,0x2C,0x60,0x1D,0x7E,0x7F,0x67,0xDB,0x00,0x00
}; //flag.enc
int enc_len = strlen(hexData);
rc4_init(s, key, strlen((const char*)key));
rc4_crypt(s, (uint8_t*)hexData, enc_len);
ChaCha20XOR((uint8_t*)mykey, 1, key, (uint8_t*)hexData, strlen(hexData));
if (hexData[0] == 'h' && hexData[1] == 'y' && hexData[2] == 'n') { //判定前三个字母是fla输出即可
printf("timestamp:%d,pid:%d ", v0, pid);
for (int i = 0; i < 48; i++) {
printf("%c", hexData[i]);
}
printf("\n");
exit(0);
}
/*else {
printf("you are wrong!\n");
}*/
}
int main() {
unsigned char mykey[32];
int timestamp;
DWORD Seed;
timestamp = 1712827218; // time(0); 2024-04-11 17:11:00
for (int pid = 20000; pid <23000; pid++) {
for (timestamp = 1712826660; timestamp <= 1712827218; timestamp++) { //时间戳要从出题时间点开始算起
Seed = timestamp ^ pid;
srand(Seed);
for (int i = 0; i < 32; ++i)
mykey[i] = (unsigned __int16)rand() >> 8;
get_flag(mykey, timestamp, pid);
}
}
printf("end\n");
return 0;
}
NSS中[柏鹭杯 2021]baby_go
和RC4这题比较像,写写试试,不过是go语言写的,只有一个ChaCha20-Poly1305,是一个组合加密算法,由ChaCha20和Poly1305两个密码学原语组成。
ChaCha20是一个流加密算法,它可以对任意长度的数据进行加密,并产生一个相同长度的密文。Poly1305是一个消息验证码,它可以对任意长度的消息进行验证,并输出一个固定长度的摘要。将这两个密码学原语结合起来,就可以实现一个安全高效的加密算法,即ChaCha20-Poly1305
对称密码,直接再次运行
得到flag.txt拿到flag
codeischange
加了反调试处一般为关键信息,进去看看
看汇编也不是花指令,那可能是smc自解密,动调一下,重定义函数。
输入15位,9*9排列,提取数据,发现是数独游戏。解出答案得到flag
该题属于是smc自解密。
[网鼎杯 2020 青龙组]jocker
VitualProtect函数,这里对内存权限进行了修改,大概率是SMC,或者函数加壳。
点encrypt函数报错,
先看汇编代码
动调进入encrypt函数
看别人的wp发现下面问题:
代码逆向的思路,动态调试使用的一些细节,堆栈不平衡相关知识
堆栈不平衡那里没弄明白为什么sp是改为0,改成其他值会怎么样?
f5不能反编译可能是堆栈不平衡,须进行栈指针修改。options-->general-->勾选stack pointer
动态调试时是怎么定位到循环结束后的语句的?
可以使用条件断点。
代码逆向,最后那个找出key的思路要记起来
动态调试结束后encrypt函数又会识别错误,这是因为每次运行程序会执行脱壳代码,而回到静态调试时会回到脱壳前的状态,所以导致识别错误
一看好像还是没有结束,继续往下翻一下发现一个和encrypt()差不多函数,再次操作一下
代码差不多都出来了,开始分析写脚本
根据encrypt()得到
buffer='hahahaha_do_you_find_me?'
v1=[ 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x09, 0x00,
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x56, 0x00,
0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00,
0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x6B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x59, 0x00,
0x00, 0x00, 0x0D]
enc=[]
flag=''
for i in range(0,len(v1),4):
enc.append(v1[i])
for i in range(19):
flag+=chr(enc[i]^ord(buffer[i]))
print(flag)
#flag{d07abccf8a410c
另一半应该在finally(),基于时间,好像不是伪随机,但可以通过(v3[(unsigned __int8)v3[5]] != a1[(unsigned __int8)v3[5]]),找到异或的值:猜测是 ':'^v4='}',得到v4
import random
import time
v3='%tp&:'
v1=time.time()
random.seed(v1)
# v4=random.randint()%100 需要两个参数
v4=random.randint(0,99)
print(v4)
v4=ord(':')^ord('}')
for i in range(len(v3)):
print(chr(ord(v3[i])^v4),end='')
# b37a}
[NSSRound#3 Team]jump_by_jump_revenge
主要运用倒序,取模,爆破。
// v3 = sub_411037("%s", (char)Str1);
//for (i = 0; i < 29; ++i)
//{
// Str1[i] = (Str1[i] + Str1[(i * v3 + 123) % 21]) % 96 + 32;
// v3 = i + 1;
//}
//if (!j_strcmp(Str1, "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*"))
//puts("right!");
//else
//puts("nope!");
//return 0;
//}
//取模可以直接考虑爆破
#include <iostream>
#include <string>
using namespace std;
int main()
{
string Str1 = "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*";
int v3 = 0;
for (int i = 28; i >= 0; i--) {
int pos = (i * i + 123)%21;
for (int j = 0; j <= 3; j++) {
int tmp = Str1[i] - 32 + j * 96 - Str1[pos];
if (tmp >= 33 && tmp <= 126) {
Str1[i] = (char)tmp;
break;
}
}
}
cout << Str1;
return 0;
}
[HDCTF 2023]double_code
打开,没有main函数,string也没有什么特别明显信息,又从start一直进去,发现了一个code
v0 = &v10;
for ( i = 254i64; i; --i )
{
*(_DWORD *)v0 = 3435973836;
v0 += 4;
} // GetCurrentThreadId()
// 返回值是调用线程的线程标识符。
v20 = 0;
sub_7FF6BC2A137A((__int64)&unk_7FF6BC2B401B);
printf("alloc:%p\n", sub_7FF6BC2AF000); // ***
memset(v11, 0, 0x70ui64);
v11[0] = 0x4000000070i64;
v11[2] = L"open";
v11[3] = L"notepad.exe";
sub_7FF6BC2A10FA((__int64)v11); // ***
hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);// 创建进程快照
if ( hSnapshot == (HANDLE)-1i64 )
{
LastError = GetLastError();
printf("CreateToolhelp32Snapshot:%d\n", LastError);
}
pe.dwSize = 568; // pe结构体的大小为568字节
v14 = j_Process32FirstW(hSnapshot, &pe);
if ( !v14 )
{
v3 = GetLastError(); // 获取最近一次的错误代码
printf("Process32First:%d\n", v3);
}
while ( v14 )
{
if ( !wcscmp(pe.szExeFile, L"notepad.exe") )// 比较两个宽字符,正数,0,负数
{
v20 = 1;
th32ProcessID = pe.th32ProcessID;
break;
}
v14 = j_Process32NextW(hSnapshot, &pe);
}
if ( !v20 )
sub_7FF6BC2A109B("pid");
v16 = qword_7FF6BC2AF368(0x1FFFFFi64, 0i64, th32ProcessID);
if ( !v16 )
{
v4 = GetLastError();
printf("\nopenprocess error%d\n", v4);
}
if ( !v20 )
sub_7FF6BC2A109B("pid");
printf("pid:%d", th32ProcessID);
v17 = qword_7FF6BC2AF370(v16, 0i64, 405i64, 4096i64, 64);
if ( !v17 )
{
v5 = GetLastError();
printf("VirtualAllocEx error%d\n", v5);
}
if ( !(unsigned int)qword_7FF6BC2AF360(v16, v17, sub_7FF6BC2AF000, 405i64, 0i64) )
{
v6 = GetLastError();
printf("WriteProcessMemory:%d\n", v6);
}
if ( !qword_7FF6BC2AF378(v16, 0i64, 0i64, v17, 0i64, 0, 0i64) )
{
v7 = GetLastError();
printf("CreateRemoteThread:%d\n", v7);
}
sub_7FF6BC2A1311((__int64)v9, (__int64)&unk_7FF6BC2ABE80);
return 0i64;
继续跟进去,发现了一个应该是关键函数,查了资料,搞清楚了
alloca是向栈申请内存,因此无需释放。
malloc 分配的内存是位于 堆中 的,并且没有 初始化内存的内容 ,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间,需要用 Free 方式释放空间.
calloc则将初始化malloc这部分的内存,设置为0.
realloc则对malloc申请的内存进行大小的调整.
free将malloc申请的内存最终需要通过该函数进行释放.
sbrk则是增加数据段的大小;
alloca()和malloc()函数都会返回分配空间的首地址,一般是void型指针。
好像VirtualAllocEx WriteProcessMemory CreateRemoteThread 也是关键,分别是
分配内存,写入内存,创建进程。
BOOL WriteProcessMemory(
[in] HANDLE hProcess,
[in] LPVOID lpBaseAddress,
[in] LPCVOID lpBuffer, //指向写入内存的指针
[in] SIZE_T nSize,
[out] SIZE_T *lpNumberOfBytesWritten
);
__int64 sub_14001F000()
{
__int64 v0; // rdx
__int64 v1; // rcx
__int64 v2; // r8
__int64 v3; // r9
int v4; // esp
unsigned __int64 v5; // rax
int v6; // esp
unsigned __int64 v7; // rax
int v8; // esp
unsigned __int64 v9; // rax
int v10; // esp
unsigned __int64 v11; // rax
int v12; // esp
unsigned __int64 v13; // rax
char v15[41]; // [rsp+1Fh] [rbp-41h] BYREF
int v16; // [rsp+48h] [rbp-18h]
int v17; // [rsp+4Ch] [rbp-14h]
int v18; // [rsp+50h] [rbp-10h]
int v19; // [rsp+54h] [rbp-Ch]
int v20; // [rsp+58h] [rbp-8h]
int i; // [rsp+5Ch] [rbp-4h]
MEMORY[0x140029C60]();
*(_DWORD *)&v15[37] = 1;
v16 = 5;
v17 = 2;
v18 = 4;
v19 = 3;
strcpy(v15, "************************************");
for ( i = 0;
(unsigned int)MEMORY[0x14003A250](v1, v0, v2, v3, *(_QWORD *)&v15[1], *(_QWORD *)&v15[9], *(_QWORD *)&v15[17]) > i;
++i )
{
v0 = (unsigned int)(i / 5);
v1 = (unsigned int)(i % 5);
v20 = i % 5;
if ( i % 5 == 1 )
{
v5 = (unsigned int)(v4 + 31 + i); //v5指向一个新地址
v1 = *(unsigned __int8 *)v5 ^ 0x23u; //(unsigned __int8 *) 强制将 v5 视为一个指向无符号字符(字节)的指针
v0 = (unsigned int)(v4 + 31); 为下一次寻址做准备
*(_BYTE *)(unsigned int)(v0 + i) = *(_BYTE *)v5 ^ 0x23;
//将地址 (v0 + i) 处的字节数据取出,并与 0x23 进行异或运算后,存储回原地址。这样就实现了对指定位置的字节数据进行修改
}
else
{
switch ( v20 )
{
case 2:
v7 = (unsigned int)(v6 + 31 + i);
v0 = (unsigned int)*(unsigned __int8 *)v7 + 2;
v1 = (unsigned int)(v6 + 31);
*(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v7 + 2;
break;
case 3:
v9 = (unsigned int)(v8 + 31 + i);
v0 = (unsigned int)*(unsigned __int8 *)v9 - 3;
v1 = (unsigned int)(v8 + 31);
*(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v9 - 3;
break;
case 4:
v11 = (unsigned int)(v10 + 31 + i);
v0 = (unsigned int)*(unsigned __int8 *)v11 - 4;
v1 = (unsigned int)(v10 + 31);
*(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v11 - 4;
break;
case 5:
v13 = (unsigned int)(v12 + 31 + i);
v0 = (unsigned int)*(unsigned __int8 *)v13 - 25;
v1 = (unsigned int)(v12 + 31);
*(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v13 - 25;
break;
}
}
}
return 0i64;
}
故这就是加密算法了。
MEMORY[0x140029C60](); // 调用地址为 0x140029C60 的函数指针所指向的函数
for (i = 0; (unsigned int)MEMORY[0x14003A250](v1, v0, v2, v3, *(_QWORD *)&v15[1], *(_QWORD *)&v15[9], *(_QWORD *)&v15[17]) > i; ++i)
// 使用地址为 0x14003A250 的函数指针所指向的函数,进行循环条件(函数返回值和i)判断
v15应该就是flag,进行了循环switch-case操作
X = [0x48,0x67,0x45,0x51,0x42,0x7b,0x70,0x6a,0x30,0x68,0x6c,0x60,0x32,0x61,0x61,0x5f,0x42,0x70,0x61,0x5b,0x30,0x53,0x65,0x6c,0x60,0x65,0x7c,0x63,0x69,0x2d,0x5f,0x46,0x35,0x70,0x75,0x7d]
for i in range(len(X)):
if(i%5 == 1):
X[i] ^= 0x23
if(i%5 == 2):
X[i] -= 2
if(i%5 == 3):
X[i] += 3
if(i%5 == 4):
X[i] += 4
if(i%5 == 5):
X[i] += 25
for i in X:
print(chr(i),end='')
# HDCTF{Sh3llC0de_and_0pcode_al1_e3sy}
攻防世界Windows_Reverse2
先脱北斗壳,pushad下硬件断点F9到popad,调到call,jmp一个极远地址后,dump程序,拖进ida
挨个分析
再看return的函数,本来没看出来是什么,gpt上问了一下,三字节变四字节发现应该是base64
看一下byte又不是base表
但这有一个补‘=’的函数
所以应该就是base64
发现好像要异或byte_83020[(unsigned __int8)*(&v17 + v8)] ^ 0x76
之后得到标准base表
总的流程,输入的大写的十六进制----->转为十进制实数------>base64加密------->与reverse+相比较
所以逆向逻辑就是reverse+来base64解密------>转大写十六进制。
import base64
s='reverse+'
print base64.b64decode(s).encode('hex').upper()
得到flag
[HNCTF 2022 WEEK2]Try2Bebug_Plus
一道动调的题目,发现一些关键函数不能直接f8跳过了,需要慢慢调,查看关键变量的内存地址
猜测v3就是flag,循环后查看v3
或者把数据提取出来,然后跑程序。
reverse-for-the-holy-grail-350
是c++写的,比较难看,要输入三次,但前两次没有影响,最后一次输入即为flag
倒着看
__int64 __fastcall stringMod(__int64 *a1)
{
__int64 v1; // r9
__int64 v2; // r10
__int64 v3; // rcx
int v4; // r8d
int *v5; // rdi
int *v6; // rsi
int v7; // ecx
int v8; // r9d
int v9; // r10d
unsigned int v10; // eax
int v11; // esi
int v12; // esi
int v14[18]; // [rsp+0h] [rbp-60h] BYREF
__int64 v15; // [rsp+48h] [rbp-18h] BYREF
memset(v14, 0, sizeof(v14)); // v4>=0
v1 = a1[1];
if ( v1 )
{
v2 = *a1;
v3 = 0LL;
v4 = 0;
do
{
v12 = *(char *)(v2 + v3); // 读取基址v12+偏移量v3的空间,即读取a1
v14[v3] = v12; // v14.append(v12)
if ( 3 * ((unsigned int)v3 / 3) == (_DWORD)v3 && v12 != firstchar[(unsigned int)v3 / 3] )
v4 = -1; // first是v3是3倍数,
// 后一个v12==fir[v3/3]
// 第0,3,6,9,12,15字符
++v3;
}
while ( v3 != v1 ); // wp说全部循环
}
else
{
v4 = 0;
} //
//
v5 = v14; // 指向同一空间的两指针
v6 = v14;
v7 = 666;
do
{
*v6 = v7 ^ *(unsigned __int8 *)v6;
v7 += v7 % 5;
++v6;
}
while ( &v15 != (__int64 *)v6 ); // 应该也是全部循环
// v7也在变
//
v8 = 1;
v9 = 0;
v10 = 1;
v11 = 0;
do
{
if ( v11 == 2 ) // 对第2,5,8,11,14,17进行操作
{
if ( *v5 != thirdchar[v9] )
v4 = -1;
if ( v10 % *v5 != masterArray[v9] ) // 相当于v5[0]*v5[1]%v5[2]!=masterArray[v9]
v4 = -1; // 得到第1,4,7,10,13,16个数字
++v9;
v10 = 1;
v11 = 0;
}
else
{
v10 *= *v5;
if ( ++v11 == 3 ) // 控制上面每3位执行一次
v11 = 0;
}
++v8;
++v5;
}
while ( v8 != 19 ); // flag应该18位
//
return (unsigned int)(v7 * v4);
}
嗯,分析起来还是有点难度的。
这个函数没有改变输入。
就开始写脚本了(对字符操作的话c简单些):
#include <stdio.h> int main(int argc, char* argv[]) { int firstchar[] = { 0x41, 0x69, 0x6E, 0x45, 0x6F, 0x61 }; int thirdchar[] = { 0x2EF, 0x2C4, 0x2DC, 0x2C7, 0x2DE, 0x2FC }; int masterArray[] = { 0x1D7, 0x0C, 0x244, 0x25E, 0x93, 0x6C }; int v7 = 666; int v77[18] = { 0 }; char flag[19] = { 0 }; // 0 3 6 ... 位 先执行所以初次不用异或 for (int i = 0; i < 18; i = i + 3) { flag[i] = firstchar[i / 3]; } // 先把每位要异或数据求出 for (int i = 0; i < 18; i++) { v77[i] = v7; v7 += v7 % 5; } // 2 5 8 ... 位 后执行需要异或 for (int i = 2; i < 18; i = i + 3) { flag[i] = thirdchar[i / 3] ^ v77[i]; } // 1 4 7 ... 关键判断,暴力破解 for (int i = 1; i < 18; i = i + 3) { // 缩小判断,可打印字符 for (int j = 32; j < 127; j++) { if ((flag[i - 1] ^ v77[i - 1]) * (j ^ v77[i]) % (flag[i + 1] ^ v77[i + 1]) == masterArray[i / 3]) { flag[i] = j; break; } } } printf("tuctf{%s}\n", flag); return 0; }
babymips
这个题还是比较简单的
按位运算,v1=(a1[i]>>2 | (a1[i])<<6)即a1低2位和高6位互换
如101100_11--->11_101100
flag="Q|j{g"
flag=list(map(ord,flag))
flag+=[0x52,0xFD,0x16,0xA4,0x89,0xBD,0x92,0x80,0x13,0x41,0x54,0xA0,0x8D,0x45,0x18,
0x81,0xDE,0xFC,0x95,0xF0,0x16,0x79,0x1A,0x15,0x5B,0x75,0x1F]
for i in range(5,32):
if i&1!=0:
flag[i]=((flag[i]<<2)|(flag[i]>>6))&0xFF
else:
flag[i]=((flag[i]>>2)|(flag[i]<<6))&0xFF
for i in range(32):
flag[i]=chr(flag[i]^(32-i))
print(''.join(flag))
[HDCTF 2023]enc
ida中一个橙色变量value may be undefined,可能是未定义,无法确定初始值
典型的TEA加密
v7,v8连续是密文,v9是密钥,得到明文
from ctypes import *
#tea
def decrypt(v, k):
v0 = c_uint32(v[0])
v1 = c_uint32(v[1])
delta = 0x9e3779b9
sum1 = c_uint32(delta * 32)
for i in range(32):
v1.value -= ((v0.value << 4) + k[2]) ^ (v0.value + sum1.value) ^ ((v0.value >> 5) + k[3])
v0.value -= ((v1.value << 4) + k[0]) ^ (v1.value + sum1.value) ^ ((v1.value >> 5) + k[1])
sum1.value -= delta
return v0.value, v1.value
if __name__ == '__main__':
a = [1627184887, 37149676]
k = [18, 52, 86, 120]
print("解密后数据:", decrypt(a,k))
# 得到(3,4) 3就是我们输入的
又发现一个函数
没有逻辑问题,不是花指令,再结合什么对内存操作,判断是smc加密
下断点调试,可知smc解密的位置,可在反汇编窗口,通过搜索”hdctf“得到具体位置。
重新定义函数选中按p
发现是rc4加密
def rc4_decrypt(ciphertext, key):
# 初始化 S-box
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# 初始化变量
i = j = 0
plaintext = []
# 解密过程
for byte in ciphertext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
plaintext.append(byte ^ k)
return bytes(plaintext)
# 示例用法
encrypted_data = [0xF,0x94,0xAE,0xF2,0xC0,0x57,0xC2,0xE0,0x9A,0x45,0x37,0x50,0xF5,0xA0,0x5E,0xCB,0x2C,0x16,0x28,0x29,0xFE,0xFF,0x33,0x46,0xE,0x57,0x82,0x22,0x52,0x26,0x2B,0x6E,0xE4,0x82,0x24]
# 替换成你的密文
encryption_key = b'you_are_master' # 替换成你的密钥
decrypted_data = rc4_decrypt(encrypted_data, encryption_key)
print("Decrypted Data:", decrypted_data.decode('utf-8'))
# Decrypted Data: HDCTF{y0u_ar3_rc4_t3a_smc_m4ster!!}
也可以使用ida内嵌脚本
for i in range(0x0041D000,0x0041E600):
patch_byte(i,get_wide_byte(i)^3)
print('done')
感谢https://blog.csdn.net/liaochonxiang/article/details/130997949