无壳,IDA打开64
分析start函数。 smc⾃解密,通常处理smc⾃解密有两种⽅法,如果是⽐较简单的⾃解密, 我们可以直接使⽤idapython写⼀个脚本即可解决,如果碰⻅运算不太好实现的smc⾃解密,⽐如这⾥。
尝试动态调试,我们要在下⽅的jmp指令处下断点
jmp跳转
右键analyzed 选Force 把红色代码部分P键定义成函数
F5起始地址,查看到main函数,猜测后面为init函数
优先查看init函数(先于main函数执行
部分main函数:
发现调⽤了fork 来创建⼦进程并执⾏了不同的逻辑
关于fork:
当程序调⽤fork函数时,系统会创建新的进程并为其分配资源;然后,会将原来进程的相关内容全部复制到新的进程中。
fork()函数被调⽤⼀次,但是会返回两次(⽗⼦进程各⼀次)
返回值分析:
1)在⼦进程 中,fork函数返回0
2)在⽗进程中,fork函数返回新创建⼦进程的ID
3)如果出现错误,fork返回⼀个负值
(对1和2的原因分析:①在⼦进程中通过调⽤getppid可以⽅便的知道⽗进程的PID;②没有⼀个函数可以使⽗进程获得其所有⼦进程 的PID。(所以在fork返回时,将⼦进程的PID直接返回给⽗进程))
特点:
1. ⽗、⼦进程共享正⽂段,不共享数据、堆、栈段,⼦进程获得⽗进程数据、堆、栈段的副本。
2. ⼦进程会获得缓冲区的副本,即fork前进程缓冲区中的数据未被flush掉,则fork后,⼦进程能够获得⽗进程缓冲区中的数据。
3. ⽗进程所有被打开的⽂件描述符都会被复制到⼦进程中。 注:fork之后处理⽂件描述符通常有两种情况: ①⽗进程等待⼦进程结束; ②⽗、⼦进程各⾃执⾏不同的正⽂段(⽗、⼦进程各⾃关闭不需要使⽤的⽂件描述符);
4. fork之后⽗、⼦进程的区别: ①fork的返回值; ②进程ID不同; ③⽗进程也不同; ④⼦进程的tms_utime、tms_stime、tms_cutime和tms_ustime均被设置为0; ⑤⽗进程设置的⽂件锁不会被⼦进程继承; ⑥⼦进程的未处理的闹钟被清除; ⑦⼦进程的未处理信号集设置为空集;
5. fork失败的两个主要原因: ①系统中进程数⽬已经达到上限; ②该实际⽤户的进程总数达到系统限制;
13 使⽤⽅法:
①⼀个进程希望复制⾃⼰,使得⽗、⼦进程执⾏不同的代码段。如⽗进程监听端⼝,收到消息后,fork出⼦进程处理消息,⽗进程仍 然负责监听消息。(⽗监听,⼦处理信息)
②⼀个进程需要执⾏另⼀个程序。如fork后执⾏⼀个shell命令。
分析init函数,
其实在这⾥的话就是⽗进程waitpid等待⼦进程,⼦进程执⾏真正的逻辑 并且⼦进程⽤了ptrace来反调试(⼀个进程只能被⼀个进程ptrace,如果你⾃⼰调⽤ptarce,这样其它程序就⽆法通过ptrace调试或者向 您的程序进程注⼊代码) 所以⽤调试器不能调试⼦进程,ptrace的处理要么就是让他不执⾏,要么就是直接将其nop掉 但是我们这⾥就是要想办法调试⼦进程,最⽅便的⽅法就是直接在其fork创建⼦进程之前,就ctrl+n强制设置rip跳转到⼦进程的逻辑执 ⾏
我ctrl+n没起作用。。。
点进去函数
cbc模式的tea,data1和data2每次加密都会更新,且每次的更新都和我们的输⼊有关 我们有最后的⽐较数据,data1和data2只知道初始值,所以以这些条件我们能解开前8字节(cmp → reverse_tea → xor_with_data → inp) 然后我们实现⼀下正向的加密去更新data,就可以得到下⼀轮的data1和data2 从⽽以此类推的计算出每次加密的8个字节 先解开我们的前⼋个字节
先解开我们的前⼋个字节
#include <stdio.h>
#include <stdint.h>
uint32_t data1 = 0x5F797274;
uint32_t data2 = 0x64726168; //初始的 data 值
void decrypt(uint32_t* v, uint32_t* k)
{
uint32_t delta = 0x9E3779B9;
uint32_t v0 = v[0], v1 = v[1], sum = (delta * 32) & 0xffffffff, i;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++)
{
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v0 = v0 ^ data1;
v1 = v1 ^ data2;
v[0] = v0; v[1] = v1;
2021极客⼤挑战逆向疑题讲解 16
}
int main()
{
uint32_t temp[2] = { 0xC9FA3B95, 0x7CFD0735 };
uint32_t key[4] = { 0x65766967, 0x756F795F, 0x7075635F, 0x6165745F };
int i = 0;
decrypt(temp, key);
printf("%c%c%c%c%c%c%c%c", *((char*)&temp[0] + 0), *((char*)&temp[0] + 1), *((char*)&temp[0] + 2), *((char*)&temp[0] + 3), *((char*)&temp
return 0;
}
最后的脚本
#include <stdio.h>
#include <stdint.h>
uint32_t data1 = 0x5F797274;
uint32_t data2 = 0x64726168; //初始的data 值
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
data1 ^= v0;
data2 ^= v1;
v0 = data1;
v1 = data2;
uint32_t delta = 0x9E3779B9;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++)
{
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
data1 = v0;
data2 = v1;
}
void decrypt(uint32_t* v, uint32_t* k)
{
uint32_t delta = 0x9E3779B9;
uint32_t v0 = v[0], v1 = v[1], sum = (delta * 32) & 0xffffffff, i;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++)
{
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v0 = v0 ^ data1;
v1 = v1 ^ data2;
v[0] = v0; v[1] = v1;
}
2021极客⼤挑战逆向疑题讲解 17
int main()
{
uint32_t array[] = { 0xC9FA3B95, 0x7CFD0735, 0x958C7C9F, 0xC143B59E, 0x61741E89, 0xF47DCDC4, 0xD6E2A1F2, 0x6A38E9AD, 0xC2C16FEB, 0x8C0EE99
uint32_t key[4] = { 0x65766967, 0x756F795F, 0x7075635F, 0x6165745F };
int i = 0;
for (i = 0; i < 10; i += 2)
{
uint32_t temp[2];
temp[0] = array[i];
temp[1] = array[i + 1];
decrypt(temp, key);
printf("%c%c%c%c%c%c%c%c", *((char*)&temp[0] + 0), *((char*)&temp[0] + 1), *((char*)&temp[0] + 2), *((char*)&temp[0] + 3), *((char*)&tem
//更新data
encrypt(temp, key);
}
//SYC{ySaySanDian_Zh0n_La_y1n_Cha_xIan}
return 0;
}