__int64 __fastcall mov_handler(FILE *stream, const struct printf_info *info, const void *const *args)
{
int prec; // [rsp+24h] [rbp-14h]
int width; // [rsp+28h] [rbp-10h]
int src; // [rsp+2Ch] [rbp-Ch]
char *dst; // [rsp+30h] [rbp-8h]
width = info->width;
prec = info->prec;
if ( (*(info + 12) & 0x20) != 0 )
{
dst = &memory[width];
}
else if ( (*(info + 12) & 0x40) != 0 )
{
dst = &memory[regs[width]];
}
else
{
dst = ®s[width];
}
src = 0;
if ( (*(info + 13) & 2) != 0 )
{
src = *&memory[prec];
}
else if ( (*(info + 12) & 2) != 0 )
{
src = *&memory[regs[prec]];
}
else if ( (*(info + 12) & 1) != 0 )
{
src = info->prec;
}
else if ( (*(info + 12) & 4) != 0 )
{
src = regs[prec];
}
*dst = src;
return 0LL;
}
就是根据info结构体的第12字节开始的某些bit来决定src源操作数和dst目的操作数
__int64 __fastcall add_handler(FILE *stream, const struct printf_info *info, const void *const *args)
{
int prec; // [rsp+24h] [rbp-14h]
int width; // [rsp+28h] [rbp-10h]
int src; // [rsp+2Ch] [rbp-Ch]
char *dst; // [rsp+30h] [rbp-8h]
width = info->width;
prec = info->prec;
if ( (*(info + 12) & 0x20) != 0 )
{
dst = &memory[width];
}
else if ( (*(info + 12) & 0x40) != 0 )
{
dst = &memory[regs[width]];
}
else
{
dst = ®s[width];
}
src = 0;
if ( (*(info + 13) & 2) != 0 )
{
src = *&memory[prec];
}
else if ( (*(info + 12) & 2) != 0 )
{
src = *&memory[regs[prec]];
}
else if ( (*(info + 12) & 1) != 0 )
{
src = info->prec;
}
else if ( (*(info + 12) & 4) != 0 )
{
src = regs[prec];
}
*dst += src;
return 0LL;
}
同理,也是最后将dst += src
__int64 __fastcall jcc_handler(FILE *stream, printf_info *info, const void *const *args)
{
int prec; // [rsp+24h] [rbp-Ch]
_BOOL4 v5; // [rsp+2Ch] [rbp-4h]
prec = info->prec;
if ( (*(info + 12) & 0x20) != 0 )
{
v5 = regs[prec] < 0;
}
else if ( (*(info + 12) & 0x40) != 0 )
{
v5 = regs[prec] > 0;
}
else
{
v5 = info->pad != 48 || regs[prec] == 0;
}
if ( v5 )
fprintf(stream, &memory[info->width]);
return 0LL;
}
这个是根据寄存器的值来判断是否跳转
fprintf(stream, &memory[info->width]);就是再次触发回调,相当于goto
#include <printf.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <sstream> //以便下面使用stringstream类型
#include <string>
using namespace std;
char code[2000];
char* regs[32] = { "rax", "rbx", "rcx", "rdx", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26","r27","r28","r29","r30","r31" };
/*
* 定义一个函数来获取src源操作数
*/
string parse_src(const struct printf_info* info)
{
stringstream src;
int src_pc = info->prec;
if ((*((char*)info + 13) & 2) != 0) //memory
src << "[0x" << hex << src_pc << "]";
else if ((*((char*)info + 12) & 2) != 0) //memory[regs[]]
src << "[" << regs[src_pc] << "]";
else if ((*((char*)info + 12) & 1) != 0) //address(rip)
src << "0x" << hex << src_pc;
else if ((*((char*)info + 12) & 4) != 0) //regs[]
src << regs[src_pc];
else
src << "error src";
return src.str();
}
/*
* 定义一个函数来获取dst目标操作数
*/
string parse_dst(const struct printf_info* info) {
stringstream dst;
int dst_pc = info->width;
if ((*((char*)info + 12) & 0x20) != 0)
dst << "[0x" << hex << dst_pc << "]";
else if ((*((char*)info + 12) & 0x40) != 0)
dst << "[" << regs[dst_pc] << "]";
else
dst << regs[dst_pc];
return dst.str();
}
//register_printf_function('C', call_handler, sub_22F0);
int call_handler(FILE* stream, const struct printf_info* info, const void* const* args)
{
string cond;
int addr = info->width;
int reg_id = info->prec;
int eflag;
if ((*((char*)info + 12) & 0x20) != 0)
{
cond = "< 0";
eflag = 1; //小于则跳转
}
else if ((*((char*)info + 12) & 0x40) != 0)
{
cond = "> 0";
eflag = 2; //大于则跳转
}
else if (info->pad == 48)
{
cond = "== 0";
eflag = 3; //je
}
else
{
eflag = 0; // jmp
}
if (cond.empty() && eflag == 0)
{
//printf("jmp 0x%04x\\n", addr, cond.c_str());
printf("jmp 0x%04x\\n\\n", addr, cond.c_str()); //前面的0x2000我们重新写文件的时候当成内存数据区
}
else if(eflag == 3)
{
//printf("if(%s %s) {jmp 0x%04x}\\n", regs[reg_id], cond.c_str(), addr);
printf("test %s %s\\njmp 0x%04x\\n\\n", regs[reg_id], regs[reg_id], addr);
}
else if (eflag == 1)
{
printf("cmp %s 0\\njb 0x%04x\\n\\n", regs[reg_id], addr);
}
else if (eflag == 2)
{
printf("cmp %s 0\\nja 0x%04x\\n\\n", regs[reg_id], addr);
}
return 0;
}
//register_printf_function('M', mov_handler, sub_22F0);
int mov_handler(FILE* stream, const struct printf_info* info, const void* const* args)
{
string src = parse_src(info);
string dst = parse_dst(info);
printf("mov %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
int add_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("add %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('O', subb_handler, sub_22F0);
int sub_handler(FILE* stream, const struct printf_info* info,const void* const* args)
{
string src = parse_src(info);
string dst = parse_dst(info);
printf("sub %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
// register_printf_function('X', mul_handler, sub_22F0);
int mul_handler(FILE* stream, const struct printf_info* info,const void* const* args)
{
string src = parse_src(info);
string dst = parse_dst(info);
printf("mul %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('V', div_handler, sub_22F0);
int div_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("div %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('N', mod_handler, sub_22F0);
int mod_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("mod %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('L', shl_handler, sub_22F0);
int shl_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("shl %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('R', shr_handler, sub_22F0);
int shr_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("shr %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('E', xor_handler, sub_22F0);
int xor_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("xor %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('I', and_handler, sub_22F0);
int and_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("and %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
//register_printf_function('U', or_handler, sub_22F0);
int or_handler(FILE* stream, const struct printf_info* info,
const void* const* args) {
string src = parse_src(info);
string dst = parse_dst(info);
printf("or %s, %s\\n", dst.c_str(), src.c_str());
return 0;
}
int dummy_arginfo(const struct printf_info* info, size_t n, int* argtypes) {
return 0;
}
//注册格式化字符串回调
void register_conv_specs()
{
register_printf_function('C', call_handler, dummy_arginfo);
register_printf_function('M', mov_handler, dummy_arginfo);
register_printf_function('S', add_handler, dummy_arginfo);
register_printf_function('O', sub_handler, dummy_arginfo);
register_printf_function('X', mul_handler, dummy_arginfo);
register_printf_function('V', div_handler, dummy_arginfo);
register_printf_function('N', mod_handler, dummy_arginfo);
register_printf_function('L', shl_handler, dummy_arginfo);
register_printf_function('R', shr_handler, dummy_arginfo);
register_printf_function('E', xor_handler, dummy_arginfo);
register_printf_function('I', and_handler, dummy_arginfo);
register_printf_function('U', or_handler, dummy_arginfo);
}
//反汇编第一段的
void disassemble1()
{
char temp[256];
char* code_end = code + sizeof(code); //获取code的结束地址
char* addr = (char*)memchr(code, '%', sizeof(code)); //获取到第一条指令开始的标志'%'的指针
while (addr)
{
char* end = strchr(addr + 1, '%'); //找到下一条指令的开始'%s'的指针
memset(temp, 0, sizeof(temp));
if (end) //如果可以找到说明这是一条完整的指令
strncpy(temp, addr, end - addr); //将指令暂时赋值给temp
else
strcpy(temp, addr);
if (!strstr(temp, "%s")) //指令中不含%s,排除了"%52C%s"
{
printf("0x%04x: ", addr - code);
printf(temp); //这里触发回调
}
addr = (char*)memchr(addr + 1, '%', code_end - addr - 1); //找到下一条指令开始的"%s"的指针
}
}
//反汇编第二段的
void disassemble2()
{
char temp[256];
char* code_end = code + sizeof(code); //获取code的结束地址
char* addr = (char*)memchr(code+0xc8, '%', sizeof(code)); //获取到第二段的第一条指令开始的标志'%'的指针
while (addr)
{
char* end = strchr(addr + 1, '%'); //找到下一条指令的开始'%s'的指针
memset(temp, 0, sizeof(temp));
if (end) //如果可以找到说明这是一条完整的指令
strncpy(temp, addr, end - addr); //将指令暂时赋值给temp
else
strcpy(temp, addr);
if (!strstr(temp, "%s")) //指令中不含%s,排除了"%52C%s"
{
printf("0x%04x: ", addr - code);
printf(temp); //这里触发回调
}
addr = (char*)memchr(addr + 1, '%', code_end - addr - 1); //找到下一条指令开始的"%s"的指针
}
}
void decrypt() //定义来decrypt code
{
printf("%c\\n", code[0xc8] & 0xff - 0x25); //这是第一层结尾对0xc8的一个运算,没有写回内存中,printf出来看下是哪个字符
for (int addr = 0xc8; addr < 0x6fc; ++addr) //解密第二层
{
code[addr] ^= 'T';
printf("%c",code[addr]); //这里将第二层的代码打印到控制台
}
printf("\\n");
}
void read_code()
{
ifstream fin("memory.bin", ios::binary); //输入文件流
fin.seekg(0, ios_base::end); //把末尾的读指针往回移0字节,获取文件末尾指针
streampos file_size = fin.tellg(); //告诉文件的大小
fin.seekg(0, ios_base::beg); //重置文件指针到开始
fin.read(code, file_size); //读取文件到code全局变量
}
int main()
{
register_conv_specs(); //注册回调
read_code(); //读取code
disassemble1(); //反汇编第一段的vm指令
decrypt();
disassemble2(); //反汇编第二段指令
return 0;
}
将VM指令第一段反汇编出来如下:
分析下:取出flag的第一个字节,进行下面这个运算之后,给rbx赋值位0xc8,rcx赋值0x6fc,然后上面循环用这个字符和memory[0xc8]-memory[0x6fc]的数据进行异或
printf("%x",(((0x61 & 0xff) << 8) | (0x61 & 0xff))&0xff); //结果还是0x61,不变
说明这里就是一个对memory进行解密的
然后decrypt()函数对代码进行解密: