栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

逆向工程实验——lab3(.NET平台逆向)

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

逆向工程实验——lab3(.NET平台逆向)

文章目录
  • 1、阅读
  • 2、图片隐写解密
  • 3、CrackMe1
    • (1)修改exe使得出现成功提示
      • i.用dnSpy修改CrackMe1.exe
      • ii.用IDA修改CrackMe1.exe
      • iii.用ILDASM和ILASM修改CrackMe1.exe
    • (2)不修改exe输入正确的密码达到成功的目的
  • 4、(选做)修改CrackMe1.exe
    • (1)输入你的学号才能出现成功提示
    • (2)把“大家好...net”这段话全部换成自己自定义的英文。
  • 5. 运行login.exe,提示 "You Get It!" 代表成功。

1、阅读

阅读https://blog.csdn.net/cnhk1225/article/details/53568996 然后回答问题: What does this code do?

通过比较输入字符的ASCII码,来确定它是不是字母,如果输入的是字母则程序返回1,否则返回0。

这是.NET框架下编译后的MSIL(微软中间语言):

Optimizing csc .NET compiler from MSVS 2015 (/o switch), ildasm output:


  .method public hidebysig static bool  f(char a) cil managed
  {
    // Code size       26 (0x1a)
    .maxstack  8   // 计算出计算堆栈的能存几个值
    IL_0000:  ldarg.0    //将索引为 0 的参数加载到计算堆栈上
    IL_0001:  ldc.i4.s   97  //将提供的值作为int32推送到计算堆栈上(短格式)
    IL_0003:  blt.s      IL_000c    //如果第一个值小于第二个值97,则将控制转移到目标指令(短格式)
    IL_0005:  ldarg.0   //将索引为 0 的参数加载到计算堆栈上
    IL_0006:  ldc.i4.s   122   //将提供的 int8 值作为 int32 推送到计算堆栈上(短格式)
    IL_0008:  bgt.s      IL_000c   //如果第一个值大于第二个值122,则将控制转移到目标指令(短格式)
    IL_000a:  ldc.i4.1   //将整数值 1 作为 int32 推送到计算堆栈上
    IL_000b:  ret  //返回
//上面这一段代码的意思是,当输入的值小于97或大于122时,跳转到IL_000c,否则返回1。
    IL_000c:  ldarg.0   //将索引为 0 的参数加载到计算堆栈上
    IL_000d:  ldc.i4.s   65   //将提供的 int8 值作为 int32 推送到计算堆栈上(短格式)
    IL_000f:  blt.s      IL_0018   //如果第一个值小于第二个值65,则将控制转移到目标指令(短格式)
    IL_0011:  ldarg.0   //将索引为 0 的参数加载到计算堆栈上
    IL_0012:  ldc.i4.s   90   //将提供的 int8 值作为 int32 推送到计算堆栈上(短格式)
    IL_0014:  bgt.s      IL_0018   //如果第一个值大于第二个值90,则将控制转移到目标指令(短格式)
    IL_0016:  ldc.i4.1   //将整数值 1 作为 int32 推送到计算堆栈上
    IL_0017:  ret  //返回
    IL_0018:  ldc.i4.0  //将整数值 0 作为 int32 推送到计算堆栈上
    IL_0019:  ret  //返回
  } // end of method some_class::f
//上面这一段代码的意思是,当输入的值小于65或大于90时,跳转到IL_0018:返回0;否则返回1。

所以整段代码的意思就是:通过比较输入字符的ASCII码,来确定它是不是字母,如果输入的是字母则程序返回1,否则返回0。

一样的代码,只不过换成了Java字节码
Java 1.8 compiler:

public boolean f(char);
    descriptor: (C)Z
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1        //第二个int型局部变量进栈顶
         1: bipush        97    //将一个byte型常量值97作为一个int整型值推送至栈顶
         3: if_icmplt     14     //比较栈顶两int型数值大小,当结果小于0时跳转到位置14
         6: iload_1         //第二个int型局部变量进栈
         7: bipush        122   //将一个byte型常量122值作为一个int整型值推送至栈顶
         9: if_icmpgt     14    //比较栈顶两int型数值大小,当结果大于0时跳转到位置14
        12: iconst_1       //int型常量值1进栈
        13: ireturn         //当前方法返回int
        14: iload_1          //第二个int型局部变量进栈
        15: bipush        65   //将一个byte型常量值65作为一个int整型值推送至栈顶
        17: if_icmplt     28    //比较栈顶两int型数值大小,当结果小于0时跳转到位置28
        20: iload_1         //第二个int型局部变量进栈
        21: bipush        90    //将一个byte型常量值90作为一个int整型值推送至栈顶
        23: if_icmpgt     28    //比较栈顶两int型数值大小,当结果大于0时跳转到位置28
        26: iconst_1      //int型常量值1进栈
        27: ireturn       //当前方法返回int值
        28: iconst_0      //int型常量值0进栈
        29: ireturn       //当前方法返回int值

这段指令集的作用和第一段一样,通过比较输入字符的ASCII码,来确定它是不是字母,如果输入的是字母则程序返回1,否则返回0。

2、图片隐写解密

2017bath这张图片有一些文字片段,可能简单加密了,是某人说的,此人的姓名是什么?
hint:hex editor

刚开始根据提示用hex editor打开这张照片,设置为Hex 16进制查看,字符集设为utf-8:

果然在文件中看到了Rail-Fence.txt,这时候就怀疑它是不是在图片里放了txt文本文件,这时想起曾做过一道将文件隐藏在图片中的题。将文件后缀名改为.zip文件,尝试用压缩软件打开图片,果然发现了一个隐藏的文件:

打开txt文件,得到一长串乱序的字符:

应该是经过某种加密的,注意到文件名是Rail-Fence:栅栏加密算法。接下来就是编程解密的过程:
代码如下:

#include

using namespace std;
int main()
{
    string s="CrudreasrsheheanaulhfouCmhlaopiaeifltouermrtdpiyeCntpmafhnpbgmbaoagspeuteuwtecwokpetwutileolctnreoicottievhetepriogoseIretodaiithgottfeorhersnueualtebtfamtnseetehrnieipooilrshashyhsrmttmanariaictntgutdeeonmotaudeowuselctnfeoghrtsbaCrgarhtbprthcirarQktodbeatees";
    int num,len=s.length();
    cout<<"待解密的密文字符串是:"<>num;
    int ary=ceil(len/(double)num);
    cout<<"按照分组长度解密后的明文字符串长度为:"<
        for(int j=0;j
            cout< 

运行时,先假定每组2个字符,解密后发现还是乱序:

故慢慢加大每组字符的长度,直到数字为9的时候,发现解密后的字符串是可以识别的,解密成功:

解密后的文本:Cpp is a horrible language.It made more horrible by the fact that a lot of substandard programmers use it to the point where its much much easier to generate total and utter crap with it.Quite frankly even if the choice of C were to do nothing but keep the Cpp programmers out that in itself would be a huge reason to use C.
通过网络查询到上面这段话出自Linus Torvalds(“Linux之父”,Linux核心的创作者),故本题的答案应该是Linus Torvalds。

3、CrackMe1

运行CrackMe1.exe,提示 “嗯,对了” 代表成功。首先修改exe使得出现成功提示,其次不修改exe输入正确的密码达到成功的目的。

先打开CrackMe1.exe,看来是输入一个字符串得到正确答案的题:

(1)修改exe使得出现成功提示 i.用dnSpy修改CrackMe1.exe

用dnSpy打开CrackMe1.exe,反编译:

直接修改代码,将代码

“fOCPTVF0diO+B0IMXntkPoRJDUj5CCsT”==this.Encode(this.textBox1.Text)

中的相等==修改为不相等!=

这样只要我们输入错误的答案都能输出“嗯,对了。。”

结果果然输出了“嗯,对了。。”

ii.用IDA修改CrackMe1.exe

先用IDA打开CrackMe1.exe,
原本想通过字符串“嗯,对了。。”找到文件的关键位置

但发现可能因为是中文或是.Net弹窗的原因,反汇编中没有任何字符串。

只好查看出现的函数名称和各部分Names

找到了加密函数的入口位置:

点进去查看:

找到加密部分函数的中间代码MSIL 反汇编程序:

用Graph view来看逻辑更清楚

Ceq指令的意思就是判断前面的两个值相等就返回1,不相等就返回0。所以我们现在的目的就是修改ceq,改变它的返回值。双击ceq,定位到它的位置上去:

打开IDA显示指令对应的16进制机器码,将ceq对应的机器码:FE 01 修改成clt对应的机器码:FE 04

运行成功输出了“嗯,对了。。”:

iii.用ILDASM和ILASM修改CrackMe1.exe

先用ILDASM打开CrackMe1.exe

转储,保存为:

用编辑器打开:

将ceq跳转修改为clt跳转:

再用ILASM将.il文件编译成.exe文件。

成功输出了“嗯,对了。。”:

(2)不修改exe输入正确的密码达到成功的目的

先用.NET Reflector打开CrackMe1.exe。

将反编译出来的C#代码输出到本地:

因为虽然代码在Reflector上虽然已经有了,但是是以每个函数为单位的,输出到本地可以是整体的文件,可以更好的看出代码结构和逻辑:

我们可以看出来这个CrackMe1.exe主要的逻辑就是,将输入的字符串加密后与“fOCPTVF0diO+B0IMXntkPoRJDUj5CCsT”相等,就输出“嗯,对了。。”。

主要的就是这个加密函数:

所以我们只要将“fOCPTVF0diO+B0IMXntkPoRJDUj5CCsT”解密出需要输入的字符串即可。
C#源代码:

using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace Project
{
    class Program
    {
        public static void Main(string[] args)
        {
            
            string result; 
            
            byte[] byte1 = Convert.FromBase64String("fOCPTVF0diO+B0IMXntkPoRJDUj5CCsT");  
            byte[] bytes = Encoding.ASCII.GetBytes("wctf{wol");  
            byte[] bytes2 = Encoding.ASCII.GetBytes("dy_crack}"); 
            
            DESCryptoServiceProvider dESCryptoServiceProvider = new DESCryptoServiceProvider();  
            
            
            MemoryStream memoryStream = new MemoryStream();  
            
            CryptoStream cryptoStream = new CryptoStream(memoryStream, dESCryptoServiceProvider.CreateDecryptor(bytes, bytes2), CryptoStreamMode.Write);  
            
            cryptoStream.Write(byte1, 0, byte1.Length);
            
            cryptoStream.FlushFinalBlock(); 
            
            
            MemoryStream ms = new MemoryStream();
            
            IFormatter formatter = new BinaryFormatter();
            
            formatter.Serialize(ms, memoryStream);
            
            
            System.Text.Encoding encoding = System.Text.Encoding.UTF8;
            
            result = encoding.GetString(memoryStream.GetBuffer());    
                      
            Console.WriteLine(result);
            
            Console.ReadKey();
        }
    }
}

运行结果:

输出解密后的字符串为:wctf{dotnet_crackme1}
将它输入到原始CrackMe1.exe的输入框:

结果正确。

4、(选做)修改CrackMe1.exe

修改CrackMe1.exe使得:只有输入你的学号才能出现成功提示。并且把“大家好…net”这段话全部换成自己自定义的英文。

(1)输入你的学号才能出现成功提示

第一步我们需要获得学号加密后的密文。参考CrackMe1.exe的加密过程,不难得到。
C#源代码:

using System;
using System.Text;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace Project
{
    /// 
    /// Description of Class1.
    /// 
    public class Lab3_4
    {
        public static void Main(string[] args)
        {
            string result;
            string data = "123456789";
            byte[] bytes = Encoding.ASCII.GetBytes("wctf{wol");
            byte[] bytes2 = Encoding.ASCII.GetBytes("dy_crack}");
            DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
            int keySize = descryptoServiceProvider.KeySize;
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateEncryptor(bytes, bytes2), CryptoStreamMode.Write);
            StreamWriter streamWriter = new StreamWriter(cryptoStream);
            streamWriter.Write(data);
            streamWriter.Flush();
            cryptoStream.FlushFinalBlock();
            streamWriter.Flush();
            result = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
            Console.WriteLine("加密后的结果为:"+result);
            Console.ReadKey();
        }
    }
}

运行结果:

学号加密后的结果为:vkXl8nCEIRwc0IFLYf2*****
将判断条件处的密文修改为学号的密文:

编辑方法,修改代码为学号的密文:

输入学号:

(2)把“大家好…net”这段话全部换成自己自定义的英文。

用dnSpy打开CrackMe1.exe,将中文修改成英文。

运行结果:

5. 运行login.exe,提示 “You Get It!” 代表成功。

先用dnSpy打开login.exe,找到主要的判断函数:

所以输入字符串的要求是:
1、输入的密码长度为9位,
2、所有位置上的数字的ASCII码之和为472,
3、所有位置上的数字的ASCII码最终异或的结果为66
这样的数看起来不只一个,那么我们就随便找个简单的,由于一共9位,所以我们分成4组,每组两个字符,并且令它们每组ASCII码相等,这样异或完之后就是0,最终第9位的ASCII为66,再令中间的4组8个的ASCII码和为472-66=406即可。所以我们可以使用如下密码:22334422B、22222255B、B22333333等等均可以。

之后按照老师的额外要求,正确的密码是之前学长或学姐的学号,也就是有额外的限制,根据每位数字的要求可以算出对应的两个学号,具体的学号涉及个人隐私就不写出来了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/908576.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号