1、反射加壳
我曾经在06年的《黑客防线》上发表过一篇名为《C# 实现从自身资源提取EXE文件》的文章,主要讲的就是如何将EXE文件以资源的形式保存在PE文件中,然后自我释放出来(模仿木马的自我释放功能),当时就用了反射技术。时隔2年,当年的熬夜奋斗的情景我还依稀记得,通过该文章得指引,释放出来后将会得到一个独立的EXE文件。而在反射壳中仅仅将托管程序释放到内存中,并找到其入口点,然后执行入口函数(不释放真实文件),这便是DONET反射壳的原理了。怎么样?貌似很简单吧。
我们今天就拿一个CrackMe来加一层简单的反射壳。
首先新建一个CMD项目,将CrackME拷贝到这个项目文件中,并在解决方案中设置成为这个项目的“嵌入式资源”。
然后我们在代码中将这个资源转换为的字节数组:
Stream sr
=
Assembly.GetExecutingAssembly().GetManifestResourceStream(
"
CiCiPackDemo.CrackMe1.exe
"
);
byte
[] fileBytes
=
new
byte
[sr.Length];
sr.Read(fileBytes,
0
, (
int
)sr.Length
-
1
);
Assembly assembly
=
Assembly.Load(fileBytes);
MethodInfo mi
=
assembly.EntryPoint;
mi.Invoke(
null
,
null
);
注:这里的“CiCiPackDemo”为该项目的命名空间,而“CrackMe1.exe”为嵌入式资源名。
然后我们再找到CrackME1.exe的函数入口点并运行就可以了。
最后我们再把这个项目的编译类型强制设置为Windows程序,编译一次,这个最简单的反射壳就完成了。我们可以看到其流程是:
读取自身资源->转换资源为Assembly->找到入口点->执行入口函数。
我们用Reflector分别打开加壳和未加壳的程序查看:
未加壳程序如下:
已经加壳程序如下:
经过对比我们发现,加壳后的程序把CrackMe1.exe作为资源文件保存了,而我们的Refletor也不能查看其代码了。起到了简单的加密保护作用。
这就是反射加壳,很简单吧^_^!
2、反射脱壳
能加壳就能脱壳,这当然是天经地义的事情。在Win32时代OllyDbg似乎成了该平台下的宠儿,然而对于反射类的DONET平台壳,这款软件一加载就会将程序跑飞。有一身本领却施展不出来……..。
那么如何对付DONET平台下的反射壳呢?再回顾一下加壳原理,我们仔细看看这句代码:Assembly assembly = Assembly.Load(fileBytes); 这说明不管怎样系统都会将加壳的程序在内存中还原成一个Assembly的对象。那么我们只要获得了这个对象,也就可以获得这个程序相关的信息,结合上一篇文章我们甚至可以获得IL代码。那么如何获得这个对象呢?在程序域中有这样的方法 AppDomain.CurrentDomain.GetAssemblies(),可惜只有在本程序集中才能这样调用,一番思考后,我们发现可以将托管代码注射进入程序中,再利用该方法就可以获得其对象了。
通过上一片文章中我写的这个工具:“
通用托管代码注射器”。就可以将托管程序注入到任意进程中。根据上篇文章注入cicireflection到上面的加壳的CiCiPackDemo中可以得到Crackme1.exe的完整信息。
那么我们如何把这个Crackme1.exe程序集完整的Dump下来呢?在介绍原理前先告诉大家一件失望的事情,这种反射脱壳在目前的DONET解密中并不实用了,更多的则是基于JIT层的脱壳,而不是基于软件本身,但是作为一种脱壳思路还是有必要和大家分享的。
其一:分享一下RICK大牛的方法,这是我拜读了RICK大牛的一些文章后自己理解的。(因为牛人写的文章经常只有代码并且点到为止,我等菜鸟可是要消化很久的)。用第三方的Dump软件把这个程序集先DUMP下来,然后根据Assembly对象获得一些数据来修复Dump后的文件,就相当于Win32程序脱壳后需要修复输入输出表的道理是一样的。而DONET反射脱壳则是来修复“方法体、头文件、元数据”之类的。为了不误导大家或者故意标榜自己的嫌疑,我还是请大家来看看Rick大牛的代码:http://bbs.pediy.com/showthread.php?t=47330
其二:说完了RICK大牛的原理,再看看我直接注入进去反射获得数据的办法。当然我们要用到我的
通用托管代码注射器,自己写一个插件代码如下:
if
(folderBrowserDialog1.ShowDialog()
==
DialogResult.OK)
{
try
{
Assembly [] assemblies
=
AppDomain.CurrentDomain.GetAssemblies();
bool
isUnpack
=
false
;
foreach
(Assembly assembly
in
assemblies)
{
if
(Path.GetFileName(assembly.Location).ToLower()
==
"
cicipackdemo.exe
"
)
{
using
(Stream sr
=
assembly.GetManifestResourceStream(
"
CiCiPackDemo.CrackMe1.exe
"
))
{
byte
[] fileBytes
=
new
byte
[sr.Length];
sr.Read(fileBytes,
0
, (
int
)sr.Length
-
1
);
File.WriteAllBytes(folderBrowserDialog1.SelectedPath
+
"
\\CrackMe1_UnPack.exe
"
, fileBytes);
}
MessageBox.Show(
"
脱壳成功!
"
,
""
, MessageBoxButtons.OK, MessageBoxIcon.Information);
isUnpack
=
true
;
break
;
}
}
if
(
!
isUnpack)
{
MessageBox.Show(
"
没有找到CiCiPackDemo.exe
"
,
""
, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
catch
(Exception ex)
{
MessageBox.Show(ex.Message,
"
Error
"
, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
原理很简单吧,找到进程中的程序集,然后直接通过反射读取CrackME1.exe文件,将其写入到具体的文件中。编译后放在“
通用托管代码注射器”的Plugin目录下,将这个dll注入到ciciPackDemo.exe中,选择保存的路径,就可以脱壳了。完整代码请参考附件。
如果不懂没关系,因为反射脱壳早已经不是主流了,知道是这么回事就行了,以后我会和大家一起分享JIT层的脱壳方法。
3、反射注册机
反射注册机的就是通过反射的方法来运行软件中的注册算法函数,直接获得注册码的一种注册机方式。反射注册机的原理虽然很简单,但是编写者却要对软件的注册流程相当清晰,一旦抓住了软件的注册算法核心,那么写一个反射注册机就会易如反掌了。
下载了“
通用托管代码注射器”附件的朋友有兴趣可以看看我是如何编写反射注册机的。
点击此处下载本章附件。