Have a tea -(极客大挑战(SMC,fork
无壳,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; }总结
以上是生活随笔为你收集整理的Have a tea -(极客大挑战(SMC,fork的全部内容,希望文章能够帮你解决所遇到的问题。