vctf2024-vm
一.vctf2024vm题的题解,一直没有整理,是赛后看大佬wp才知道是upx魔改+rc4的。。
二.解题思路
1.去upx魔改:
VCTF 2024 ezvm (虚拟机逆向初探)_vctf vm-CSDN博客
[原创] UPX源码学习和简单修改-加壳脱壳-看雪-安全社区|安全招聘|kanxue.com
加壳流程:(博客总结)
a.写入文件的elf头与程序表头,写入I_info结构
b.对每个PT_LOAD的段进行压缩存储,其中第一个PT_LOAD=elf头+程序头表+其他数据
c.压缩存储其他段
d.写入其他数据-PackerHeader和overlay_offset,其中overlay_offset是I-info的偏移值,一般为F4 00 00 00
2.vm逆向
1)定义关键结构体
动调程序将函数参数的int v1修改成_cpu* v1
2)dump下关键指令并跟踪程序指令,查看每一步进行的操作,先跟进第一个opcode指令,观察数据变化
//第一处循环:为了方便理解 eax[0]指的是高位八字节,eax[1]指的是低位八字节
0xF0, 0xE0, 0x02, 0x00,
0xF0, 0xE0, 0x00, 0xE0, 0x02, //eax=1 03 00 00 00 73h, eax=1 03 00 00 00 04h
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x02, //eax不变
0xF0, 0xE0, 0x01, 0x10,
0xF2, 0xE0, 0x00, 0xE0, 0x01,
0xF1, 0xE0, 0x00, 0x20, 0x02,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00, //eax=10 00 00 02 24 eax[0]=v3=0x5F eax=10 00 00 00 5Fh
0xF0, 0xE0, 0x01, 0xE0, 0x02, //eax=10 00 00 00 5F eax[1]=eax[2] eax=10 00 00 00 5F
0xF1, 0xE0, 0x01, 0x00, 0x01, //v3=1, v1=(1+4)<<8+eip[3]+eax[v3]=0x104, eax[1]=v3,
//eax=104 0000005F 这里有个dword*,是双字 占四个字节
0xF0, 0xE1, 0xE0, 0x01, 0xE0, 0x00, //eax不变,修改s的值,s存放的应该是个字符串
0xF3, 0xE0, 0x02, //(Dword*)eax[2]++,eax不变,eax=1040000005Fh,但(Dword*)eax[2]
//指向ebx,实际上是ebx++
0xF6, 0xE0, 0x02, 0x00, 0x01, //ebx<256
0xF7, 0x04, //跳转到第一条opcode,这是个循环指令,ebx用来记录循环次数
3)写个脚本查看一下eax的变化
#include <stdio.h>
#include <Windows.h>
//第一段循环的脚本,查看数组的变化以及_eax的值
int main() {
/*
定义了三个寄存器eax,ebx,ecx,都是dq也就是8字节的int64类型
*/
/*
由于在反汇编代码中存在类型转换,在此脚本中eax[0]代表eax的高位,eax[1]代表的是eax的低位,eax[2]则代表ebx
(eax[0]<<8)|eax[1]=eax
*/
unsigned int opcode[] = {
0xF0, 0xE0, 0x02, 0x00, //第一次循环
0xF0, 0xE0, 0x00, 0xE0, 0x02,
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x02,
0xF0, 0xE0, 0x01, 0x10,
0xF2, 0xE0, 0x00, 0xE0, 0x01,
0xF1, 0xE0, 0x00, 0x20, 0x02,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00,
0xF0, 0xE0, 0x01, 0xE0, 0x02,
0xF1, 0xE0, 0x01, 0x00, 0x01,
0xF0, 0xE1, 0xE0, 0x01, 0xE0, 0x00,
0xF3, 0xE0, 0x02,
0xF6, 0xE0, 0x02, 0x00, 0x01,
0xF7, 0x04,
0xF0, 0xE0, 0x02, 0x00,
0xF0, 0xE0, 0x03, 0x00,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //循环跳转处
0xF1, 0xE0, 0x03, 0xE0, 0x00,
0xF0, 0xE0, 0x00, 0xE1, 0x02,
0xF1, 0xE0, 0x00, 0x00, 0x01,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00,
0xF1, 0xE0, 0x03, 0xE0, 0x00,
0xF2, 0xE0, 0x03, 0x00, 0x01,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02,
0xF0, 0xE0, 0x01, 0xE1, 0xE0, 0x03,
0xF0, 0xE1, 0xE0, 0x03, 0xE0, 0x00,
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x01,
0xF3, 0xE0, 0x02,
0xF6, 0xE0, 0x02, 0x00, 0x01, //这里也是循环256次
0xF7, 0x45,
};
long eax[2] = {0};
unsigned int ebx = 0;
unsigned int s[600] = { 0 }; //在动调vm过程中还会观察到对一个数组进行了初始化
unsigned int enc[16] = {
0x54, 0x68, 0x69, 0x73, 0x5F, 0x31, 0x73, 0x5F, 0x66, 0x31,
0x6C, 0x4C, 0x6C, 0x6C, 0x61, 0x67 };
for(int i=0;i<256;i++)
{
eax[1] = ebx;
s[ebx] = ebx;
eax[0] = 0x10; //0是高位
unsigned int v1 = eax[1] % eax[0];
eax[1] = v1;
unsigned int v2 = (2 << 8) + 0x20 + eax[1];
eax[1] = v2;
unsigned v3 = enc[i%16];
eax[1] = v3;
eax[0] = ebx;
eax[0] = (1 << 8) + 0 + eax[0];
s[eax[0]] = enc[i%16];
ebx++;
printf("当前eax的值为:0x%x%x\n", eax[0]<<20,eax[1]);
}
for (int i = 0; i < 600; i++) {
if (i % 16 == 0)
printf("\n");
printf("%-d ",s[i]);
}
return 0;
}
4)同理根据F0-F6函数进行的操作,对每段opcode进行解读
//第二处循环:
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //循环跳转处:以下已经过高低位处理
0xF1, 0xE0, 0x03, 0xE0, 0x00, //v3=3,v1=eax[1]+ecx,ecx=v1
0xF0, 0xE0, 0x00, 0xE1, 0x02, //eax[1]=ebx
0xF1, 0xE0, 0x00, 0x00, 0x01, //v3=0,v1=1<<8+0+eax[1],eax[1]=v1
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00, //v2=0xE0,v3=s[eax[0]],eax[1]=v3
0xF1, 0xE0, 0x03, 0xE0, 0x00, //v3=3,v1=eax[1]+ecx
0xF2, 0xE0, 0x03, 0x00, 0x01, //v3=3,v1=ecx%(1<<8)+0,ecx=v1
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //v2=0xE0,v3=s[ecx],eax[1]=v3=s[ecx]
0xF0, 0xE0, 0x01, 0xE1, 0xE0, 0x03, //v2=0xE0,v3=s[(int)ecx+1],eax[0]=v3=s[ecx+1]
0xF0, 0xE1, 0xE0, 0x03, 0xE0, 0x00, //v2=0xE1,s[ecx+1]=eax[1]
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x01, //v2=0xE1,s[ecx]=eax[0]
0xF3, 0xE0, 0x02, //ebx++
0xF6, 0xE0, 0x02, 0x00, 0x01, //这里也是循环256次,ecx=1
0xF7, 0x45,
//修正后:第二处循环:
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //循环跳转处:以下已经过高低位处理
0xF1, 0xE0, 0x03, 0xE0, 0x00, //ecx=eax[1]+ecx
0xF0, 0xE0, 0x00, 0xE1, 0x02, //eax[1]=ebx
0xF1, 0xE0, 0x00, 0x00, 0x01, //eax[1]=256+eax[1]
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00, //eax[1]=s[eax[1]]
0xF1, 0xE0, 0x03, 0xE0, 0x00, //ecx=eax[1]+ecx
0xF2, 0xE0, 0x03, 0x00, 0x01, //ecx=ecx%256
//这里循环交换了s[i]与s[i+1]的值
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //eax[1]=s[ecx]
0xF0, 0xE0, 0x01, 0xE1, 0xE0, 0x03, //eax[0]=s[ecx+1]
0xF0, 0xE1, 0xE0, 0x03, 0xE0, 0x00, //s[ecx+1]=eax[1]
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x01, //s[ecx]=eax[0]
0xF3, 0xE0, 0x02, //ebx++
0xF6, 0xE0, 0x02, 0x00, 0x01, //这里也是循环256次,ecx=1
0xF7, 0x45,
0xF0, 0xE0, 0x02, 0x00, //ebx=0
0xF0, 0xE0, 0x03, 0x00, //ecx=0
//第三处循环
0xF3, 0xE0, 0x02, //ebx++
0xF2, 0xE0, 0x02, 0x00, 0x01, //ebx=ebx%256,
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //eax[1]=s[ecx]
0xF1, 0xE0, 0x03, 0xE0, 0x00, //ecx=eax[1]+ecx,
0xF2, 0xE0, 0x03, 0x00, 0x01, //ecx=ecx%256
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x02, //eax[1]=s[ecx]
0xF0, 0xE0, 0x01, 0xE1, 0xE0, 0x03, //eax[0]=s[ecx+1]
0xF0, 0xE1, 0xE0, 0x03, 0xE0, 0x00, //s[ecx+1]=eax[1]
0xF0, 0xE1, 0xE0, 0x02, 0xE0, 0x01, //s[ecx]=eax[0]
0xF1, 0xE0, 0x00, 0xE0, 0x01, //eax[1]=eax[0]+eax[1]
0xF2, 0xE0, 0x00, 0x00, 0x01, //eax[1]=eax[1]%256
0xF0, 0xE0, 0x00, 0xE1, 0xE0, 0x00, //eax[1]=s[eax[1]]
0xF0, 0xE0, 0x01, 0xE0, 0x02, //eax[0]=ebx
0xF4, 0xE0, 0x01, //eax[0]--
0xF1, 0xE0, 0x01, 0x00, 0x02, //eax[0]=512+eax[0]
0xF0, 0xE0, 0x01, 0xE1, 0xE0, 0x01, //eax[0]=s[ebx]
0xF5, 0xE0, 0x00, 0xE0, 0x01, //eax[1]^=eax[0] :eax[1]存放的是加密后的数据
0xF1, 0xE0, 0x00, 0xE0, 0x02, //eax[1]=ebx+eax[1] :eax[1]+i
0xF0, 0xE0, 0x01, 0xE0, 0x02, //eax[0]=ebx+eax[0]
0xF4, 0xE0, 0x01, //eax[0]--
0xF1, 0xE0, 0x01, 0x00, 0x02, //eax[0]=ebx+eax[0]
0xF0, 0xE1, 0xE0, 0x01, 0xE0, 0x00, //s[ebx]=eax[1] :将加密后的数据存放到s[ebx]中
0xF0, 0xE0, 0x01, 0xE0, 0x02, //eax[0]=ebx
0xF4, 0xE0, 0x01, //eax[0]--
0xF6, 0xE0, 0x01, 0x20, 0x00, //循环32次
0xF7, 0x94,
5)可以看出来进行了三次循环,前两处opcode循环进行了256次,第三处循环进行了32次,并且有rc4加密的特征
(指路博客:
6)解密脚本如下:
#include <stdio.h>
#include <math.h>
#include <string.h>
#include<stdio.h>
/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
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_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
/*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
Data[k] -= i; //这里有魔改
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}
void main()
{
//字符串密钥
unsigned char key[] = "This_1s_f1lLllag";
unsigned long key_len = sizeof(key) - 1;
//数组密钥
//unsigned char key[] = {};
//unsigned long key_len = sizeof(key);
//加解密数据
unsigned char data[] = { 0x56, 0x54, 0xD9, 0xB5, 0xF3, 0xB1, 0xFD, 0x67, 0x15, 0xEE, 0xB0, 0x68, 0xB7, 0x2B, 0x4A, 0x64,
0x10, 0x27, 0x52, 0xDE, 0x43, 0x26, 0x0F, 0x2A, 0x41, 0x30, 0x75, 0x30, 0x98, 0x9E, 0x79, 0x5E };
//加解密
rc4_crypt(data, sizeof(data), key, key_len);
for (int i = 0; i < sizeof(data); i++)
{
printf("%c", data[i]);
}
printf("\n");
return;
}
//zstuctf{xXx_team_Is_GooD
//(c语言源码解释:https://www.cnblogs.com/Moomin/p/15023601.html)
//(解密脚本:https://blog.csdn.net/weixin_45582916/article/details/121429688
标签:0xE0,eax,0x01,0x00,vm,0xF0,vctf2024,re,0x02
From: https://www.cnblogs.com/a1rhthm/p/18150808