反射、反射加壳、反射脱壳、反射注册机


 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
      反射的用途很大,在上节文章中的“晚期绑定”就是利用了反射的性质,在插件的编写上更是需要反射。我们常用的反编译工具Refiector 也是用了反射的性质。
     我们今天就来写一个简单的类似于Refiector的东西,通过反射来获得程序集的多种信息。
1、获得程序集所有的namespace


        public static ArrayList GetAllNameSpace(Assembly assembly)
        {
           ArrayList namePaceArry = new ArrayList();
           foreach(Type type in assembly.GetTypes())
           {
              if (!namePaceArry.Contains(type.Namespace) && type.Namespace != null)
              {
                   namePaceArry.Add(type.Namespace);
              }
           }
           return namePaceArry;
        }


2、获得程序集的所有类
 

       public static ArrayList GetClassNames(Assembly assembly, string nameSpace)
        {
            Type[] types = assembly.GetTypes();
            ArrayList classArray = new ArrayList();
            foreach (Type type in types)
            {
                if (type.FullName == nameSpace + "." + type.Name && type.BaseType.Name != "Enum")
                {
                    classArray.Add(type.Name);
                }
            }
            return classArray;
        }


3、获得程序集的某一类中的所有方法


        public static MethodInfo [] GetMethod(Assembly assembly, string fullName)
        {
            Type type = assembly.GetType(fullName);
            return type.GetMethods();
        }


这里的fullName是包含namespace和类名合在一起的。

4、获得程序集中的某一类中的所有属性

        public static PropertyInfo[] GetPropertys(Assembly assembly, string fullName)
        {
            Type type = assembly.GetType(fullName);
            return type.GetProperties();
        }


5、获得程序集的枚举类型

        public static ArrayList GetEnums(Assembly assembly, string nameSpace)
        {
            Type[] types = assembly.GetTypes();
            ArrayList classArray = new ArrayList();
            foreach (Type type in types)
            {
                if (type.FullName == nameSpace + "." + type.Name && type.BaseType.Name == "Enum")
                {
                    classArray.Add(type.Name);
                }
            }
            return classArray;
        }



6、获得程序集中枚举的内容,以及每项的值

        public static FieldInfo [] GetSubEnums(Assembly assembly, string fullName)
        {
            return assembly.GetType(fullName).GetFields();
        }

        public static string GetSubEnumsData(Assembly assembly, string fullName,string name)
        {
            FieldInfo info = assembly.GetType(fullName).GetField(name);
            return info.GetRawConstantValue().ToString();
        }


7、获得程序集中某一类的所有属性

        public static PropertyInfo[] GetPropertys(Assembly assembly, string fullName)
        {
            Type type = assembly.GetType(fullName);
            return type.GetProperties();
        }


8、获得程序集中某一具体方法的IL代码

        public static string GetMethodData(Assembly assembly, string fullName,string name, Type [] types)
        

{
            Type type = assembly.GetType(fullName);
            MethodInfo info = type.GetMethod(name, types);
            string data = String.Empty;
            if (info.GetMethodBody() != null)
            {
                byte[] il = info.GetMethodBody().GetILAsByteArray();
                if (il != null)
                {
                    int inslength;
                    for (int currentpos = 0;currentpos < il.Length;currentpos += inslength)
                    {
                        inslength = 0;
                        data += String.Format("{0:X8}: {1}", currentpos,Disassembler.Decode(il, currentpos, out inslength)) + "\r\n";
                    }
                }
                return data;
            }
            return "此函数不做反编译处理";
        }



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(nullnull);

注:这里的“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、反射注册机
       反射注册机的就是通过反射的方法来运行软件中的注册算法函数,直接获得注册码的一种注册机方式。反射注册机的原理虽然很简单,但是编写者却要对软件的注册流程相当清晰,一旦抓住了软件的注册算法核心,那么写一个反射注册机就会易如反掌了。 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值