Xenocode是.net下非常著名的软件加密公司,Fox是其产品之一,是一个反汇编器(另外还有个postbuild是加密的)。Fox 2007的功能包括查看,分析,及Profile .Net程序。可惜免费的Community版不提供Profile的功能,Professional版的网上又没的下。不过无所谓,PEBrowse里连Profiler的源代码都提供了,为了这个花钱也不值得。Fox 2007 CE的下载地址是http://www.xenocode.com/Products/Fox/Setup.msi,让我们看看这款著名的软件其最新版的保护方式吧。
安装后目录下就Fox.exe这一个程序,用Reflector打开,发现错误,不含CLR头。不是吧,现在这么流行用win32加密.net程序吗?
用PEiD查一下壳,显示UPolyX v0.5 *。通常这是不可信的,好像我试过的被本地代码加密过的.net程序查出都是这个壳。就算是,偶也不会脱,哈哈。用OD载入,果然是本地代码程序。试着跟了一会,没有头绪。还是从.net下PE执行的原理着手吧。
过去讲过,.net下加壳分两种,一种是像MaxtoCode那样,分Method加密解密,在内存中不存在完整的assembly。另一种是用传统的加壳技术,解密后原来完整的assembly会出现在内存中,这就给我们dump之提供了先决条件。Fox用的是第二种。可是断点下在哪儿呢?就下在“万能断点”,mscorjit.dll的compileMethod处,在我的机上是7906E7F4。在OD中下断,bp 7906E7F4。这里要注意,就是在入口点下断会显示地址无效,因为mscorjit.dll还没有被载入,运行一段后就可以下断了。
当第一次中断时,我们看堆栈值
0012EDE8 79E9776F 返回到 mscorwks.79E9776F
0012EDEC 790AF170 mscorjit.790AF170
0012EDF0 0012EF2C
0012EDF4 0012EFB8
0012EDF8 00107210
0012EDFC 0012F06C
原来讲过,第四行的值指向了struct CORINFO_METHOD_INFO_结构,该结构如下:
struct CORINFO_METHOD_INFO
{
CORINFO_METHOD_HANDLE ftn;
CORINFO_MODULE_HANDLE scope;
BYTE * ILCode;
unsigned ILCodeSize;
unsigned short maxStack;
unsigned short EHcount;
CorInfoOptions options;
CORINFO_SIG_INFO args;
CORINFO_SIG_INFO locals;
};
我们在OD中这样选择:
然后在数据窗口中查看BYTE * ILCode的地址。
至于这段代码的内容是什么,我们不关心,但这是JIT引擎编译的第一个method,也就是说如果FOX是整个assembly解密,这时已经解密完成了。根据.net下PE结构的定义,IL代码是定义在.text节中的,和win32下的PE差不多,在内存中也遵循rva偏移定位的原则。我们来到0138DA40所在的段:
双击,查看这段内存的数据,看到了什么?PE文件头!难道这就是解密后的原文件?不妨dump下来看看。点右键,选“保存数据到文件”,然后把扩展名从mem改为exe。呵呵,图标一下又变成fox了。
用Reflector再次打开dump.exe,这次Reflector能完全显示出来了。这就是Fox的本体。
试着运行一下,报错。“Unalbe to find a runtime version to run this application.”。没事,这种小困难不算啥,我不会修复让微软帮忙。直接用ildasm.exe反编译,然后原封不动地再用ilasm.exe编译回来。OK,一切问题解决,程序可以正常运行了。
/////////////////////////////////////////////////////////////
下面解决网络验证问题。当你联网运行fox时,会出现个小窗口,提示你输入在fox网站的用户名和密码,进行验证。这可不爽,难道每次运行FOX我都得联网?而且还有天数限制。不过既然源代码已经出来了,就把网络验证块给爆了吧。
在Reflector中来到入口处,代码如下。(跟踪窗口的产生过程,用PEBrowse调试比较方便。如果哪个call你跳过去后,窗口就显示的话,就跟进去,直到找到网络验证的地方,就像OD中的F7,F8。我已经调试过了,所以这里直接给出静态的流程。)
[STAThread]
private static void xc447809891322395()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
[COLOR=red]x3e4e23fadc83a77e xeefadcae1 = new x3e4e23fadc83a77e();[/COLOR]
while (xeefadcae1.x69b9cb33c60f0e3b)
{
Application.Run(xeefadcae1);
if (2 != 0)
{
return;
}
}
}
注意红色的那句x3e4e23fadc83a77e(),直接跟进去。来到一个很大的代码段。先通篇浏览一下,没有出现任何和网络验证相关的函数调用,看来还要跟进去。真正调用的是下面代码中加红色的一段(同样,这是动态调试跟踪出来的):
Label_06C4:
this.x85601834555fb7d5();
base.Height = Screen.PrimaryScreen.WorkingArea.Height;
using (xf17d6cdd9cc7b699 xfdcddccb1 = new xf17d6cdd9cc7b699(0xfa0))
{
xfdcddccb1.ShowDialog(this);
}
this._x8af6efb682bc47ce = new x8af6efb682bc47ce();
using (x02e1e2a3949fc710 xeeafc1 = new x02e1e2a3949fc710())
{
xeeafc1.Text = x77fa6322561797a0.x6886d5a1867d55cb;
xeeafc1.Icon = base.Icon;
xeeafc1.x7598bfd06959c5c4 = x77fa6322561797a0.x6886d5a1867d55cb;
xeeafc1.x77fa6322561797a0 = x77fa6322561797a0.x4cdaa3594901b8ae;
[COLOR=red]x02e1e2a3949fc710.x658c509a55e4e71a xcaeea1 = xeeafc1.x59b56fb51064be4a(this);[/COLOR]
再次点x59b56fb51064be4a(this),跟进去。终于,我们看到了“UserSerial”,和WebRequest,这明显是网络验证的代码段了。
到这里一切很明显了,下面的分析一切从简。首先在Software\Oak Vale Networks这项中新建UserSerial项,然后随便输入注册码。然后爆破三个地方。爆破时在反编译出的il文件中修改。
第一处,il文件的354889行
IL_00b8: /* 33 | 5D */
bne.un.s IL_0117
改为beq.s
第二处,il文件的354853行
IL_0075: /* 2D | 51 */
brtrue.s IL_00c8
改为brfalse.s
第三处,
IL_00d0: /* 2C | 13 */
brfalse.s IL_00e5
改为brtrue.s
改完后保存,用ilasm.exe编译,命令行如下:
ilasm /resource=dump.res dump.il
完成后运行,OK,一切搞定。即使联网也不会提示激活了。
最后说几句,对dump后的fox.exe进行动态调试是很重要的一环,因为看着网络验证函数的返回值调试起来非常方便。比如,在PEBrowse中可以清楚地看到验证服务器的返回值。如下图
这句是建立一个读写流,读取网络验证的返回值,保存在eax中。我们双击eax,就会看到服务器返回的验证值。这里返回的是“FAIL”,失败。还有的值包括“BAD”,“EXP”过期等。这样结合静态分析,看代码是非常方便的。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课