HVM版本 v3.2.5.0 这个版本应该没加DNGuard吹嘘的虚拟机的保护 (不过也无非是些IL替换之类的东东
)
<1>首先用CFF打开, 发现元数据比较乱, 函数名也是些乱码。 感觉不太对 , 然后用Asmex.exe这个工具打开看看(个人认为Asmex.exe比CFF 要好),发现#~变成了#- , MethodPtr也存在!!!
原来CFF存在这个BUG ,无法解析非压缩的元数据!!
<2>看看Method->RVA这个元数据项,发现很多地址都指向一个地方。 但有一些函数 RunHvm ,Startup函数肯定是真实的RVA。 不用跟踪,也知道这个几个函数是加载运行时的。 故先不去管它 (无非是在.cctor里加入指令之类的弱智伎俩)。
<3>用Windbg加载调试一下, 这个出问题了。 HVM加了反调试! 真是够火大的。。。。 退出,先启动程序,然后用Windbg Attach。。 呵呵!在几个点下断点 mscorwks!_CorExeMain ,mscorjit!jitNativeCode , 这里有个技巧, 因为HVM替换了mscorjit!CILJit::compileMethod 这个编译函数,所以,我们在更深层的地方也就是mscorjit!jitNativeCode下断。 根据mscorjit!jitNativeCode的定义 我们发现该函数里有一个参数是这样的,
typedef struct _tagCORINFO_METHOD_INFO
{
MethodDesc* ftn;
VOID * scope;
BYTE * ILCode;
unsigned ILCodeSize;
unsigned short maxStack;
unsigned short EHcount;
CorInfoOptions options;
CORINFO_SIG_INFO args;
CORINFO_SIG_INFO locals;
}CORINFO_METHOD_INFO;
这个不用我再说了吧, ILCode和ILCodeSize已经够我们用了。 如果,还想获取更多的信息, 可以用ftn这个东东。。。。这里要注意的是 ILCode这个东东,并不是函数的RVA, 要还原函数的RVA, 需要根据ILCode这个偏移,向前推。 分析函数的Tiny , FAT的函数头, 找出函数的真正的RVA。 这里还有个技巧, 用分析出的这个RVA,在程序里用UE查找。 可以发现一个表。 用来存放函数的真实的RVA的表。 剩下的不用我说了吧。。。
<4>最让我气愤的是HVM居然FAT的函数头中的LocalVarSigTok也替换成了一个0xFF002061类似之类的东东。 我们知道LocalVarSigTok是用来描述函数的局部变量的一个TOKEN ,它指向StandAloneSig ,StandAloneSig 然后又指向#Blob流。 在上一步中,我们获取了一个CORINFO_METHOD_INFO得参数,可以看到里面有个locals的项,在jitNativeCode里,它应该是指向一个正确的#Blob的Sig数据。关于CORINFO_METHOD_INFO的定义如下:
typedef struct _tagCORINFO_SIG_INFO
{
CorInfoCallConv callConv;
VOID * retTypeClass; // if the return type is a value class, this is its handle (enums are normalized)
VOID * retTypeSigClass;// returns the value class as it is in the sig (enums are not converted to primitives)
CorInfoType retType : 8;
unsigned flags : 8; // used by IL stubs code
unsigned numArgs : 16;
CORINFO_SIG_INST sigInst; // information about how type variables are being instantiated in generic code
VOID * args;
VOID * sig;
VOID * scope; // passed to getArgClass
DWORD token;
}CORINFO_SIG_INFO;
CORINFO_SIG_INFO 里面的sig就是指向#Blob的真实的LocalVarSigTok的值。 剩下的就是列举StandAloneSig ,对比每一个TOKEN的值是否和sig一样,然后算出TOKEN的值,最后将LocalVarSigTok替换成0x11000001之类的StandAloneSig TOKEN值。
<5>最后就是要去除HVM的元数据混淆。 这个也有个技巧,先用PEVerify这个工具,验证一下HVM加过密的恶心东东, 会发现一堆元数据错误。 基本是些越界之类的,或者是函数名为空。 把出错的元数据表项去除就OK了。 不过让我更惊喜的是,我这个程序居然加了dotfuscator的混淆,难道。。。。 哎!!!!
<6>可以用ILDASM打开了。 剩下的,就是你改IL代码了。 呵呵。。。。
最后要严重鄙视一下Rick, 你写的壳垃圾就垃圾吧,干嘛还加人家VMP的壳。 就你的那些元数据重排,加.cctor, 替换jit->mscorjit!CILJit::compileMethod函数,加密LocalVarToken ,等等唬人的伎俩,大家心知肚明。 何必自欺欺人呢???? 你的混淆做的不过关就算了, 干嘛还用dotfuscator?
欢迎Rick你出来批评指正!! 我接受你如何形式的质问!!!
最后要表达一下我对VMP的敬佩,本人跟踪VMP的一个解密函数2天,楞是没还原出程序执行流程。 哎。 惭愧啊。
关于第四步中的还原,我补充一下。 本人写了一个DLL , 用来在程序启动的时候HOOK jitNativeCode 这个内核函数。 然后枚举LocalVarToken 。 因为实在无法分析出VMP的解密代码。。。。。。。
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界