公司为一个web应用程序写了一个注册机,基本原理是用户运行这个软件后,得到一个申请 码,然后公司根据这个申请码给出相应注册码,匹配后方可正常使用web软件。在别人机子上没有问题,但是我机子上运行软件后死活就是没有申请码产生,也没 报错。开发此程序的人员早不知道是谁了,也没有源码,只好自己分析是什么问题导致的,如果是程序的问题,希望能给程序打个“补丁”,准确的说是采用比较初 级的.NET逆向工程来注入需要的补丁代码。以下是思路和主要操作(代码中略去了不需要的代码部分)。
1.
用reflector打开后,发现是.NET程序,且没混淆,这就好办了。因为程序的代码比较少,在reflector中看就那么几个按钮的事件函数,读一下就差不多了。基本思路是,既然申请码那个文本框没有产生申请码,那么就要找到为那个申请框赋值的语句,看看那个语句有没有问题,那么要想找到这个语句,就要先找到这个申请框在程序中的ID号。
2.
从程序中填写完注册码后点击的那个注册按钮入手,那个按钮点击后提示了“注册码错误”,根据这几个字,找到了:
private void registerBut_Click(object sender, EventArgs e)
{
if (this.sAnswerCode(this.requestCode.Text) == this.answerCode.Text)
{
this.setRegStr(this.answerCode.Text);
MessageBox.Show("注册成功。");
}
else
{
MessageBox.Show("注册码错误!");
}
}
显然,requestCode就是申请码那个TextBox,answerCode就是注册码那个TextBox,完成找ID号的工作。
3.
然后去找为requestCode赋值的语句:
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SystemSet());
}
public SystemSet()
{
………
this.requestCode.Text = this.CpuID();
………
}
private string CpuID()
{
string text2 = "";
try
{
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Processor");
ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
using (ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectCollection.GetEnumerator())
{
.....
}
.....
}
catch
{
}
return text2;
}
可以看出来,是CpuID方法用于产生申请码的,应该是根据cpu等硬件信息产生的,这就可以为每台计算机产生不同的申请码。最终经过处理返回text2就是申请码,用try-catch代码块,把所有可能错误都忽略了,难怪没申请码但是也没报错~~~
4.
将CpuID这个方法在我本机的VS中执行了一下(需要引用System.Management),果然报错了,查看catch抛出的异常,是在调用searcher.Get()时抛出的异常,信息如下:
{System.Runtime.InteropServices.COMException (0x80070422): 无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动。 (异常来自 HRESULT:0x80070422)
在 System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
在 System.Management.ManagementScope.InitializeGuts(Object o)
在 System.Management.ManagementScope.Initialize()
在 System.Management.ManagementObjectSearcher.Initialize()
在 System.Management.ManagementObjectSearcher.Get()
可能我机子的某个服务没有启动造成的,而Get方法却使用了此服务。Get的智能提示是:
“异步调用 WMI 查询,并绑定到一个观察程序以传递结果。”
这就明白了,去服务里看了一下,我的Windows Management Instrumentation确实没有启动,这是我之前优化系统时关上的,没想到在这里用到了。启动此服务后,程序运行正常了,可以产生申请码。
5.
虽然我知道了是这个问题,不过以后要是再使用这个软件的机子发生同样问题呢?总不能还配一个专门的说明书,所以如果能给打个小小的补丁,那是最好。不过因为没有源码,原则上是修改越少越好,而不是一味追求完美的修补。
为了方便起见,我这里只想在CPUID方法的catch中加一个提示语句:
1 |
MessageBox.Show( "无法生成申请码,请确认系统WMI服务启动正常!" ); |
这肯定是不够严谨的,不会所有的异常都是因为WMI未启动导致的,因为修改IL比较麻烦,这里就为了最小化修改的原则,以后发现别的问题再说。
操作思路如下:
(1)
首先,利用ILDASM(.NET的IL反汇编程序,Framework自带的),打开这个程序,然后转储它的IL及其相关文件(这里我转储为programIL,一共生成4个文件,一个IL后缀,两个resources后缀,还一个res后缀)。
(2)
将其中以IL后缀名的文件用文本编辑器打开,并找到CpuID的IL代码段,可以看到如下:
code
可以看到IL的catch中,确实什么都没干,就直接跳转出去了。我这里需要在里面加上上面说的提示信息,修改完毕的代码是:
code
对加入的IL稍做说明:
经过上面压栈,调用,出栈,我们的自定义代码注入进去了,并且又恢复了修改前栈的状态,因此不影响栈的平衡。这里想强调一下,上面的IL写法不是唯一的,比如我们完全可以将那个字符串“无法生成申请码,请确认系统WMI服务启动正常!”用如下代码来代替:
bytearray (E0 65 D5 6C 1F 75 10 62 33 75 F7 8B 01 78 0C FF F7 8B 6E 78 A4 8B FB 7C DF 7E 57 00 4D 00 49 00 0D 67 A1 52 2F 54 A8 52 63 6B 38 5E 01 FF )
其中bytearray里面的代码为汉字的unicode码。
还需要注意的是:
(3)
利用Ilasm((.NET的IL汇编程序,Framework自带的),将修改好的IL汇编成为应用程序。这个只提供了命令行版本的。
以下是命令,将这个il编译到newProgram.exe,还有许多参数,可以参照ilasm的连接;
执行过程略去,以下是部分统计信息:
可以看出,没有出现任何错误,提示编译成功。
(4)
运行一下新程序,当WMI服务停止时,确实提示了信息:
经过其他测试,没有影响之前程序的功能,修改就算成功了。
总结:
参考: