如何使用C#调用U8的COM组件之二 利器与初探

前一章(点击可以看前一章内容),我们讲解了Interop的三大问题,本章将引入利器,对这三个问题进行初探,并加以解决

善功必先利器

工具一 SpyStudio

这是我最近发现的一个特别牛逼的软件,主要用来HOOK程序,实时分析程序调用了那些api,读取了那些文件,用到了那些COM组件,读取了那些注册表。特别适合进行软件绿化,因为它能直接导出vmware thinapp,简直是神一样的存在,可惜不能在Win10下正常使用。但是绿化后的程序可以在各种系统下运行。

工具二 ILMergeGUI_Portable和ILMerge

用于将多个NET的DLL组件合并到一起,本来是一个命令行工具,后来我找到一个GUI的小程序,可以托拉拽了,特别强大和方便。

工具三 DotNETHelper

这也是一个杀手级别的利器,主要用来将NET反编译成IL文件,然后修改IL后,再次编译成DLL。我主要用它解决引用错误问题。因为Interop文件不能很完美反编译成C#语言,所以很多工作我都是在IL级别操作的。

工具四 TotalCmd

因为后期主要是对各种文件的操作,所以最好准备一个TC工具,可以非常方便的管理文件。对于这个软件可以说也是神级别的了,我就不多说了。自己百度吧。

工具五 Mono.Cecil

准确的说,这不是一个工具了,而是一个NET的类库,由Mono提供,可以修改Net程序的DLL文件,功能比DotHelper还要强大。因为需要对Interop文件进行批量的处理,而IL操作实在是太麻烦了,所以直接用这个类库,通过编程的方式,把我想修改的内容写成代码,然后执行,全部搞定!!

工具使用

以上的5个工具,除了SpyStudio暂时用不到,其他的工具,是下面将会用到的主要工具,所以希望大家提前去百度学习一下如何使用,这里不展开。毕竟也不是非常复杂,过程中我会尽量截图,让大家看的清除一些。

Interop文件引用修复初探

反编译观察

有了工具,我们就可以分析Interop文件了,首先拿出DotNetHelper,把Interop.U8Login.dll反编译后,看一下IL文件的开始部分信息。
DOTNETHELPER
文件的内容很多,但是我们主要关注开头部分,因为开头部分是引用部分,相当于我们开发C#时,引用程序集那个部分的功能,具体的格式如下:

// Metadata version: v2.0.50727
.assembly extern mscorlib
{
 .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
 .ver 2:0:0:0
}
.assembly extern ADODB
{
 .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
 .ver 7:0:3300:0
}

我们这里发现Interop.U8Login.dll 引用了两个组件,一个是2.0的mscorlib,也就是framewor 2.0的基本环境。一个是ADODB,版本7.0。并且我们发现ADDOB有两个特点。

1、ADODB的这个引用带有强命名,.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
2、ADODB这个引用前面没有Interop单词,.assembly extern ADODB,这个特征很重要,有时候我们在处理Interop.VBA.dll的时候你就会发现,有的引用居然是VBA.dll,有的是Interop.VBA.dll

分析正确文件的特点

我们再看一下,正确引用了Interop.U8Login.dll的其他Interop文件,比如下面就是我自己编译的Interop.USERPCO.dll,该类库引用了U8Login组件,这个类库也是非常重要的一个类,主要完成库存单据的保存功能。是API里面最核心的调用类。

// Metadata version: v2.0.50727
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
//这里的引用,publickey和U8login的ADODB引用是完全一样的
.assembly extern ADODB
{
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )                         // .?_....:
  .ver 7:0:3300:0
}
.assembly extern Interop.VBA
{
  .ver 6:0:0:0
}
//这里的引用,没有publickey
.assembly extern Interop.USCOMMON
{
  .ver 2:1:0:0
}
//注意这里的引用,有publickey
.assembly extern Interop.U8Login
{
  .publickeytoken = (79 A4 E7 AD 54 EE AB CA )                         // y...T...
  .ver 1:14:0:0
}
.assembly extern Interop.MSXML2
{
  .ver 3:0:0:0
}

注意U8Login的强命名还有版本号 ,.publickeytoken = (79 A4 E7 AD 54 EE AB CA ) .ver 1:14:0:0
注意ADODB的强命名和版本号,这两个值是(.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) .ver 7:0:3300:0 ),且和U8Login使用的ADODB完全一样,这样的文件,就是正确的,因为如果U8Login有返回值是ADODB中的类,那么这个Interop文件可以正常操作,不会出现类型转换失败的问题,因为他们引用的是同一个ADODB文件。

错误文件的特征

再看一个我们自己生成的Interop后最容易遇到的问题,往往我们自己生成Interop 文件以后,很有可能是这样的

//前面的我就忽略不贴出来了,直接看关键的U8Login的引用
.assembly extern Interop.U8Login
{
  .ver 1:29:0:0
}

正确的应该如下

.assembly extern Interop.U8Login
{
  .publickeytoken = (79 A4 E7 AD 54 EE AB CA )                         // y...T...
  .ver 1:14:0:0
}

这里之所以不一致,就是前文说的拖家带口问题导致的,其实你仔细看,就会发现在你生成的Interop文件对应的目录里面,居然还有一个Interop.U8Login.dll,而这个文件就是版本1.29那个文件。而NET程序是绝对不能引用两个同名文件的。所以必须将公用引用文件保持一致。
到此我们发现了正确文件的基本特征:

1、公用文件,比如ADODB,他们引用的名称,版本号,强命名都是完全一样的
2、彼此关联的文件,比如某DLL引用了Interop.U8Login.dll,必须保证某DLL中U8login的版本和强命名还有名称和原版的Interop.U8Login一致。

Interop引用错误的问题修改

毛爷爷教导我们,知错不改,不是好同志!既然我们知道了错误的根本原因,那么下面就开始做一名真正的好同志。

修改方法1 反编译法

之前说过,Interop.U8Login.dll不是能完美反编译成C#的,但是如果你愿意手工干预,通过后期的修改,这个项目可以完美的变成一个标准C#类库项目,然后通过修改引用,再次编译就好了。可惜,我不认为有人会这么干,因为Interop文件实在太多了,修改工作量巨大,错曾经尝试过几次,最后都放弃了。

修改方法2 命令行参数法

正统的解决方案微软其实是给出来的,就是通过在使用Tlbimp.exe这个命令行的时候,使用/reference 参数

Tlbimp.exe C:\U8Soft\Ufcomsql\U8Login.dll  /reference:"C:\U8Soft\Interop\MSXML2.dll"
Tlbimp.exe C:\U8Soft\Ufcomsql\USERPCO.dll  /reference:"C:\U8Soft\Interop\Interop.U8Login.dll" /reference:"C:\U8Soft\Interop\MSXML2.dll" 

上面的代码,展示了如何使用多个已有类库文件,解决引用错误。这个方法真的非常不错,我以前也使用,后来觉得还是改IL比较有成就感,所以就不怎么用这个方法。但是我还是强力推荐该方法。

修改方法3 IL修改法

之前的整个研究过程,都是基于IL进行,所以我最早使用的就是DotNetHelper工具进行IL修改去解决这个问题的。速度也比较快,反编译,打开IL文件,找到错误点,修改,重新编译,问题解决。(这里补充一下,因为ocx控件使用AxImp工具生成AxInterop文件,但是这个命令行工具没有引用参数reference,所以只能使用IL修复法。)修改的过程如下:

//前面的我就忽略不贴出来了,直接看关键的U8Login的引用
.assembly extern Interop.U8Login
{
  .ver 1:29:0:0
}

正确的应该如下

.assembly extern Interop.U8Login
{
  .publickeytoken = (79 A4 E7 AD 54 EE AB CA )                         // y...T...
  .ver 1:14:0:0
}

本章完
下一章,开始讲解在面对繁多的U8版本和繁多的Interop时,面临的新问题

如何使用C#调用U8的COM组件之二 利器与初探

上一篇:windows下 为不同虚拟环境配置不同的cuda


下一篇:如何使用C#调用U8的COM组件之 一前言