vm逆向
虚拟机逆向与实现-CSDN博客
对上面博客的总结。
引
vm逆向题,一般是小型虚拟机程序,可以理解为一种模拟器,有start,dispatcher,opcode等结构。常见使用while-switch/if这类循环+选择结构来实现简单的虚拟机模拟,如下:
逆向重点:
-
分析入口,搞清输入和opcode的位置
-
理清结构,包括dispatcher和各handler
-
逆向各handler,分析opcode意义
小型虚拟机实现
目标:
-
定义一套opcode
-
实现opcode解释器
要点:
-
定义系列虚拟寄存器ri,指针eip指向正在解释的地址,opcode及其对应处理函数的列表
-
初始化。将寄存器置0,eip指向opcode首地址,并初始化opcode列表每一项的opcode值以及对应的handle函数。
-
start。用while循环结构,如果eip目前指向的opcode不为ret(或其他标识程序结束的指令),那么调用dispatcher来执行当前eip所指向的指令。
-
dispatcher。在opcode列表中查找当前需要的opcode,如果找到,那么执行对应的handler函数,退出遍历。
-
注意要关注handler函数所占字节数,函数执行完毕后要将eip偏移相应位移。
例题:moectf2024 moejvav
用jadx打开,看Main:
package defpackage;import exceptions.BuDaoLePaoException;
import exceptions.DxIsNanTongException;
import exceptions.GenshinImpactException;
import exceptions.LuoIsNotDogException;
import exceptions.NotSigninException;
import exceptions.NullCafeException;
import exceptions.StarrySkyMeowNotFoundException;
import exceptions.TokioEatWhatException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;/* renamed from: Main reason: default package */
/* loaded from: moejvav.jar:Main.class */
public class Main {public static void main(String[] args) {System.out.println("这里是moejvav! 请输入你的flag:");String flag = new Scanner(System.in).next();if (flag.length() != 44) {System.out.println("flag长度不对");return;}List<Byte> array = new ArrayList<>();for (byte b : flag.getBytes(StandardCharsets.UTF_8)) {array.add(Byte.valueOf((byte) ((Byte.valueOf(b).byteValue() ^ 202) + 32)));}int[] vmInsn = new int[310];vmInsn[0] = 0;vmInsn[1] = 1;vmInsn[2] = 60;vmInsn[3] = 2;vmInsn[4] = -20;vmInsn[5] = 6;vmInsn[6] = -25;vmInsn[7] = 0;vmInsn[8] = 1;vmInsn[9] = 60;vmInsn[10] = 2;vmInsn[11] = -20;vmInsn[12] = 6;vmInsn[13] = -27;vmInsn[14] = 0;vmInsn[15] = 1;vmInsn[16] = 60;vmInsn[17] = 2;vmInsn[18] = -20;vmInsn[19] = 6;vmInsn[20] = -33;vmInsn[21] = 0;vmInsn[22] = 1;vmInsn[23] = 60;vmInsn[24] = 2;vmInsn[25] = -20;vmInsn[26] = 6;vmInsn[27] = -31;vmInsn[28] = 0;vmInsn[29] = 1;vmInsn[30] = 60;vmInsn[31] = 2;vmInsn[32] = -20;vmInsn[33] = 6;vmInsn[34] = -50;vmInsn[35] = 0;vmInsn[36] = 1;vmInsn[37] = 60;vmInsn[38] = 2;vmInsn[39] = -20;vmInsn[40] = 6;vmInsn[41] = -36;vmInsn[42] = 0;vmInsn[43] = 1;vmInsn[44] = 60;vmInsn[45] = 2;vmInsn[46] = -20;vmInsn[47] = 6;vmInsn[48] = -39;vmInsn[49] = 0;vmInsn[50] = 1;vmInsn[51] = 60;vmInsn[52] = 2;vmInsn[53] = -20;vmInsn[54] = 6;vmInsn[55] = -24;vmInsn[56] = 0;vmInsn[57] = 1;vmInsn[58] = 60;vmInsn[59] = 2;vmInsn[60] = -20;vmInsn[61] = 6;vmInsn[62] = -52;vmInsn[63] = 0;vmInsn[64] = 1;vmInsn[65] = 60;vmInsn[66] = 2;vmInsn[67] = -20;vmInsn[68] = 6;vmInsn[69] = -29;vmInsn[70] = 0;vmInsn[71] = 1;vmInsn[72] = 60;vmInsn[73] = 2;vmInsn[74] = -20;vmInsn[75] = 6;vmInsn[76] = -52;vmInsn[77] = 0;vmInsn[78] = 1;vmInsn[79] = 14;vmInsn[80] = 2;vmInsn[81] = 5;vmInsn[82] = 6;vmInsn[83] = -64;vmInsn[84] = 0;vmInsn[85] = 1;vmInsn[86] = 14;vmInsn[87] = 2;vmInsn[88] = 5;vmInsn[89] = 6;vmInsn[90] = -58;vmInsn[91] = 0;vmInsn[92] = 1;vmInsn[93] = 14;vmInsn[94] = 2;vmInsn[95] = 5;vmInsn[96] = 6;vmInsn[97] = -63;vmInsn[98] = 0;vmInsn[99] = 1;vmInsn[100] = 14;vmInsn[101] = 2;vmInsn[102] = 5;vmInsn[103] = 6;vmInsn[104] = -52;vmInsn[105] = 0;vmInsn[106] = 1;vmInsn[107] = 14;vmInsn[108] = 2;vmInsn[109] = 5;vmInsn[110] = 6;vmInsn[111] = -90;vmInsn[112] = 0;vmInsn[113] = 1;vmInsn[114] = 14;vmInsn[115] = 2;vmInsn[116] = 5;vmInsn[117] = 6;vmInsn[118] = -39;vmInsn[119] = 0;vmInsn[120] = 1;vmInsn[121] = 14;vmInsn[122] = 2;vmInsn[123] = 5;vmInsn[124] = 6;vmInsn[125] = -43;vmInsn[126] = 0;vmInsn[127] = 1;vmInsn[128] = 14;vmInsn[129] = 2;vmInsn[130] = 5;vmInsn[131] = 6;vmInsn[132] = 26;vmInsn[133] = 0;vmInsn[134] = 1;vmInsn[135] = 14;vmInsn[136] = 2;vmInsn[137] = 5;vmInsn[138] = 6;vmInsn[139] = 25;vmInsn[140] = 0;vmInsn[141] = 1;vmInsn[142] = 14;vmInsn[143] = 2;vmInsn[144] = 5;vmInsn[145] = 6;vmInsn[146] = -49;vmInsn[147] = 0;vmInsn[148] = 1;vmInsn[149] = 14;vmInsn[150] = 2;vmInsn[151] = 5;vmInsn[152] = 6;vmInsn[153] = -64;vmInsn[154] = 0;vmInsn[155] = 1;vmInsn[156] = 10;vmInsn[157] = 2;vmInsn[158] = 5;vmInsn[159] = 6;vmInsn[160] = -51;vmInsn[161] = 0;vmInsn[162] = 1;vmInsn[163] = 10;vmInsn[164] = 2;vmInsn[165] = 5;vmInsn[166] = 6;vmInsn[167] = 25;vmInsn[168] = 0;vmInsn[169] = 1;vmInsn[170] = 10;vmInsn[171] = 2;vmInsn[172] = 5;vmInsn[173] = 6;vmInsn[174] = -45;vmInsn[175] = 0;vmInsn[176] = 1;vmInsn[177] = 10;vmInsn[178] = 2;vmInsn[179] = 5;vmInsn[180] = 6;vmInsn[181] = -55;vmInsn[182] = 0;vmInsn[183] = 1;vmInsn[184] = 10;vmInsn[185] = 2;vmInsn[186] = 5;vmInsn[187] = 6;vmInsn[188] = -47;vmInsn[189] = 0;vmInsn[190] = 1;vmInsn[191] = 10;vmInsn[192] = 2;vmInsn[193] = 5;vmInsn[194] = 6;vmInsn[195] = 24;vmInsn[196] = 0;vmInsn[197] = 1;vmInsn[198] = 10;vmInsn[199] = 2;vmInsn[200] = 5;vmInsn[201] = 6;vmInsn[202] = -41;vmInsn[203] = 0;vmInsn[204] = 1;vmInsn[205] = 10;vmInsn[206] = 2;vmInsn[207] = 5;vmInsn[208] = 6;vmInsn[209] = -60;vmInsn[210] = 0;vmInsn[211] = 1;vmInsn[212] = 10;vmInsn[213] = 2;vmInsn[214] = 5;vmInsn[215] = 6;vmInsn[216] = 22;vmInsn[217] = 0;vmInsn[218] = 1;vmInsn[219] = 10;vmInsn[220] = 2;vmInsn[221] = 5;vmInsn[222] = 6;vmInsn[223] = -40;vmInsn[224] = 0;vmInsn[225] = 1;vmInsn[226] = 10;vmInsn[227] = 2;vmInsn[228] = 5;vmInsn[229] = 6;vmInsn[230] = -60;vmInsn[231] = 0;vmInsn[232] = 2;vmInsn[233] = 14;vmInsn[234] = 2;vmInsn[235] = 10;vmInsn[236] = 6;vmInsn[237] = -15;vmInsn[238] = 0;vmInsn[239] = 2;vmInsn[240] = 14;vmInsn[241] = 2;vmInsn[242] = 10;vmInsn[243] = 6;vmInsn[244] = 50;vmInsn[245] = 0;vmInsn[246] = 2;vmInsn[247] = 14;vmInsn[248] = 2;vmInsn[249] = 10;vmInsn[250] = 6;vmInsn[251] = -51;vmInsn[252] = 0;vmInsn[253] = 2;vmInsn[254] = 14;vmInsn[255] = 2;vmInsn[256] = 10;vmInsn[257] = 6;vmInsn[258] = -31;vmInsn[259] = 0;vmInsn[260] = 2;vmInsn[261] = 14;vmInsn[262] = 2;vmInsn[263] = 10;vmInsn[264] = 6;vmInsn[265] = 50;vmInsn[266] = 0;vmInsn[267] = 2;vmInsn[268] = 14;vmInsn[269] = 2;vmInsn[270] = 10;vmInsn[271] = 6;vmInsn[272] = 50;vmInsn[273] = 0;vmInsn[274] = 2;vmInsn[275] = 14;vmInsn[276] = 2;vmInsn[277] = 10;vmInsn[278] = 6;vmInsn[279] = -35;vmInsn[280] = 0;vmInsn[281] = 2;vmInsn[282] = 14;vmInsn[283] = 2;vmInsn[284] = 10;vmInsn[285] = 6;vmInsn[286] = 50;vmInsn[287] = 0;vmInsn[288] = 2;vmInsn[289] = 14;vmInsn[290] = 2;vmInsn[291] = 10;vmInsn[292] = 6;vmInsn[293] = -35;vmInsn[294] = 0;vmInsn[295] = 2;vmInsn[296] = 14;vmInsn[297] = 2;vmInsn[298] = 10;vmInsn[299] = 6;vmInsn[300] = 51;vmInsn[301] = 0;vmInsn[302] = 2;vmInsn[303] = 14;vmInsn[304] = 2;vmInsn[305] = 10;vmInsn[306] = 6;vmInsn[307] = -17;vmInsn[308] = 114514;vmInsn[309] = 1919810;Exception[] exceptions2 = {new BuDaoLePaoException(), new DxIsNanTongException(), new GenshinImpactException(), new LuoIsNotDogException(), new NotSigninException(), new NullCafeException(), new StarrySkyMeowNotFoundException(), new TokioEatWhatException(), new RuntimeException()};int i = 0;int store = 0;while (i < vmInsn.length) {int insn = vmInsn[i];i++;if (insn == 114514) {break;}try {throw exceptions2[insn];break;} catch (BuDaoLePaoException e) {store = array.get(0).byteValue();array.remove(0);} catch (DxIsNanTongException e2) {store ^= vmInsn[i];i++;} catch (GenshinImpactException e3) {store += vmInsn[i];i++;} catch (LuoIsNotDogException e4) {store &= vmInsn[i];i++;} catch (NotSigninException e5) {store <<= vmInsn[i];i++;} catch (NullCafeException e6) {store |= vmInsn[i];i++;} catch (StarrySkyMeowNotFoundException e7) {i++;if (store != vmInsn[i]) {vmInsn[i] = 7;}} catch (TokioEatWhatException e8) {vmInsn[i] = 8;} catch (Exception e9) {System.out.println("wrong flag, oh no...");throw new RuntimeException(e9);}}System.out.println("输入的flag正确!");}
}
这里使用了try-catch抛出异常来实现switch分支选择结构,原理是throw出expections2数组中的函数,catch捕捉到对应编号的函数异常,编号即为该分支的case条件。这个虚拟机的opcode与其对应的handler可以明显地看出。每当opcode为0,取出flag的一个字符,opcode为1~5,对字符进行加密运算,opcode为6,进行一次check,若check不通过则说wrong flag。因此对每一次opcode6的结果一步步向上解密,直到遇到opcode0,则输出该字符,就能得到flag。
exp:
#include<bits/stdc++.h>
using namespace std;
int vmInsn[310];
int decode1(int x)
{return (x-32)^202;
}
int cracker_and(int chip,int x)
{for(int i=-255;i<=255;i++)if(i&x==chip)return i;
}
int cracker_or(int chip,int x)
{for(int i=-255;i<=255;i++)if(i|x==chip)return i;
}int main()
{//vmInsn[...]=...int cnt=0;for(int i=0;;i++){if(vmInsn[i]==114514)break;if(vmInsn[i]!=6)continue;int p=i,chip=vmInsn[p+1];while(1){if(vmInsn[p-1]==0)break;p-=2;int opnum=vmInsn[p+1];switch(vmInsn[p]){case 1:chip^=opnum;break;case 2:chip-=opnum;break;case 3:chip=cracker_and(chip,opnum);break;case 4:chip>>=opnum;break;case 5:chip=cracker_or(chip,opnum);break;}}printf("%c",decode1(chip));}return 0;//cout<<cnt;}
//moectf{jvav_eXcEpt10n_h4ndl3r_1s_s0_c00o0o1}