
f340a90c-13f4-427b-9330-79b824ccf413.png (111.76 KB, 下载次数: 0)
下载附件
查壳
2025-6-19 19:59 上传
没有加壳,好像是python打包的,第一次遇见这种,自己破解之后看官方提供的wp要解包和字节码啥的,没搞过这种,感觉我的破解纯瞎猫碰死耗子了. 下面是我的破解方法:
将程序拖到ida里,转到main函数,看了一眼,ok啊 感觉没啥

2afe400a-dd92-4828-b39a-d98bffcc061e.png (98.36 KB, 下载次数: 0)
下载附件
main函数
2025-6-19 20:17 上传
运行程序之后显示你的输入,猜测是类似scanf这种,开始跟一下,跟到后面发现不是,跟到了一个函数sub_140005C80里,这个函数会被sub_1400027B0这个调用,也就是main函数后面的那个jmp,sub_140005C80调用了CreateProcessW函数创建自身,
不过有了命令行参数应该是为了区分父进程和子进程

730af1f6-3405-4f4b-b02c-843d3ed7eccf.png (150.12 KB, 下载次数: 0)
下载附件
创建自身进程
2025-6-19 20:27 上传

42b0df31-d732-45a7-ae8e-28a2cf1bedbc.png (218.81 KB, 下载次数: 0)
下载附件
命令行参数
2025-6-19 20:32 上传
设置各种属性,创建了自身进程, v1 = _acrt_iob_func(0);
v2 = fileno(v1);
StartupInfo.hStdInput = (HANDLE)get_osfhandle(v2);
v3 = _acrt_iob_func(1u);
v4 = fileno(v3);
StartupInfo.hStdOutput = (HANDLE)get_osfhandle(v4);
v5 = _acrt_iob_func(2u);
v6 = fileno(v5);
StartupInfo.hStdError = (HANDLE)get_osfhandle(v6); 尤其是这几行,我猜测应该是子进程进行校验,有了这个想法之后,拿另一个x64dbg进行附加,刚开始的想法是这样的,我猜测这两进程会用设置好的句柄进行通信,
然后断点设置在了NtReadFile,输入key之后,子进程直接闪退,父进程提示错误之类的信息,然后又将断点设置在了ExitProcess,毕竟子进程会退出,看一下调用堆栈啥的,依旧毫无收获,即使根据调用堆栈将断点设置在了
最里层,父进程依旧显示错误类信息,然后又开始猜测,子进程会不会创建线程啥的,通过这个线程对父进程传入的key进行处理,设置一下调试器选项,断点设置在线程入口,发现有了收获.

47942002-1b38-462f-aff3-c4301b22fe61.png (307.99 KB, 下载次数: 0)
下载附件
线程函数
2025-6-19 20:49 上传
此时rcx的值恰好是我输入的key,后面的那个call 明显是为了重定位,shellcode,事后我在virtualalloc下断点也会中断,在这里不再赘述 通过对shellcode的动态跟踪发现,前面的shellcode是解密,对后面要执行的shellcode进行解密,
解密流程不关心,

bc09f4b1-af1e-4de3-a360-1e324bfebf40.png (126.62 KB, 下载次数: 0)
下载附件
解密前
2025-6-19 20:54 上传

5c3706ff-745b-46a2-8701-d36a3237d503.png (134.75 KB, 下载次数: 0)
下载附件
2025-6-19 20:55 上传
注意看这两张图偏移0x167解密前后的区别,事实上这段shellcode分为两部分,前面是解密后面的,后面的shellcode则是对输入的key进行处理了,然后我将后面的shellcode直接dump下来,拖到ida静态分析

9c0f7c0b-a166-4253-8e58-d98e7349519c.png (165.51 KB, 下载次数: 0)
下载附件
对shellcode的静态分析
2025-6-19 20:59 上传
ok啊,看着有点恶心,不知道在干撒,直接f5看伪代码

78dbb026-fd43-4482-9951-ed78caf0b84d.png (66.59 KB, 下载次数: 0)
下载附件
伪代码
2025-6-19 21:01 上传
ida已经很还原了 我将伪代码用vs写了一份对输入的key进行相关操作,得到的结果是一模一样的, 通过对伪代码以及动态调试可知,key的长度是42,然后两个字节一组进行操作,最后得到一个新的数组,做完这个事情,
该线程也会直接退出,那么就是有其他线程继续对加密后的key进行校验了,直接在加密key数组的起始地址下硬件断点,单步f8让加密线程退出之后发现程序断在了vc运行库里面,运行到返回之后到了一个python310.dll的空间
调用的函数是memmove,此时返回值rax就是新的数组了,对rax返回地址进行下断点,最终到达了验证

3e3afa9f-650d-4f4a-b6ef-850f3b251de2.png (199.24 KB, 下载次数: 0)
下载附件
memmove调用
2025-6-19 21:14 上传

b0e02587-6ab7-4d99-a728-98ae6a387b18.png (273.15 KB, 下载次数: 0)
下载附件
验证
2025-6-19 21:16 上传
这里就是验证了,从[rdx]的内存中读取一个字节和[rcx]的内存处字节进行判断,rcx就是memmove返回的rax地址,直接从rdx指向的内存中复制42个字节到rcx指向的内存中,删除所有断点运行试一下,发现和我想象的一样,父进程提示成功

403de6fa-068e-43c5-8c78-f1a8e1fe4b3b.png (13.54 KB, 下载次数: 0)
下载附件
父进程提示成功
2025-6-19 21:21 上传
那么说明只要将rdx指向的内存中扣出42个字节再进行解密,即可得到真正的key,然后甩给ds(开深度思考),前两次没开解密全是错的,开了深度思考才解密得到正确的flag

ea44fddd-1220-4d55-8ab3-499212213e0a.png (185.2 KB, 下载次数: 0)
下载附件
ds解密
2025-6-19 21:24 上传
加密算法和ai给的解密算法如下
#include
#include
// 快速幂模运算 (计算 base^exponent mod modulus)
unsigned int pow_mod(unsigned int base, unsigned int exponent, unsigned int modulus) {
unsigned long long result = 1;
base = base % modulus;
while (exponent) {
if (exponent & 1)
result = (result * base) % modulus;
base = (unsigned long long)base * base % modulus;
exponent >>= 1;
}
return (unsigned int)result;
}
// 逆向算法:从变换后的数组恢复原始key
void reverse_sub_0(const unsigned char* transformed, unsigned char* original) {
static int table_initialized = 0;
static unsigned int reverse_table[53743]; // 0xD1EF = 53743
// 初始化映射表(只需计算一次)
if (!table_initialized) {
for (unsigned int x = 0; x > 8; // 高字节
}
}
int main() {
// 变换后的数组(已知)
unsigned char transformed[] = {
0xDB, 0x1B, 0x00, 0x44, 0x79, 0x5C, 0x43, 0xCC, 0x90, 0x5F, 0xCA, 0x2E, 0xB0, 0xB7,
0x6D, 0xAB,0x11, 0x9B, 0x5E, 0x68, 0x90, 0x1B, 0x6C, 0x19, 0x01, 0x0C, 0xED, 0x75,
0x50, 0x36, 0x0C, 0x30,0x7F, 0xC5, 0x45, 0x2D, 0x4C, 0xB0, 0xFB, 0xBA, 0xF6, 0x9F
};
unsigned char original_key[42]; // 存储原始key
// 执行逆向算法
reverse_sub_0(transformed, original_key);
// 输出原始key(可打印字符)
printf("Recovered Key: ");
for (int i = 0; i
将真正的数组替换,运行得到key

a5812275-bb67-44f1-a754-9cd59641d7b4.png (22.47 KB, 下载次数: 0)
下载附件
真正的key
2025-6-19 21:26 上传
回顾整个过程,感觉运气占比70%,全靠猜测,提交flag之后看官方的wp,感觉真的瞎猫碰上死耗子了,剩下就是全靠ai,省时又省心