64位进程调用32位dll的解决方法 / 程序64位化带来的问题和思考

最近做在Windows XP X64,VS2005环境下做32位程序编译为64位程序的工作,遇到了一些64位编程中可能遇到的问题:如内联汇编(解决方法改为C/C++代码),long类型的变化,最关键的遇到了64位进程需要调用32位dll的问题。由于有一些32位dll没有源代码,无法重新编译为64位dll,所以只能想办法解决64位进程调用32位dll问题,这个问题让我很是挠头了几天。

相关资料:
微软公司的官方网站针对这个问题描述如下:
在64位的windows系统中,一个64位进程不能加载一个32位dll,同理一个32位进程也不能加载一个64位dll。但是,64位windows支持64位和32位进程(包括本机或跨机)间进程间通信(RPC)。在64位windows中,一个进程外32位COM服务器能够与64位客户端进行通信,同样一个进程外64位COM服务器也能与32位客户端进行通信。因此,如果你有一个32位COM无法识别的DLL,你可以将它封装到一个进程外COM服务器中并在一个64位进程中用COM配置调用DLL。(最后一句我也看不太懂!!哈哈哈)

验证:
工作流程:
1.创建一个进程外COM服务器(EXE)。
2.将32位dll的接口函数封装为COM服务器的相关接口。
3.注册COM服务器*.exe /regserver  (注销 *.exe /unregserver)。
4.64位进程调用32位COM服务器接口,成功。从而曲线实现了64位进程调用32位dll。

具体步骤:
我首先创建了一个简单的dll工程,只输出一个函数int c = add(int a,int b); 生成lib和dll
然后创建一个进程外COM(EXE类型),内部链接dll,添加方法Method: Add(long *c)
{ *c = add(1,2);}编译生成。
然后注册COM,*.exe /regserver
最创建一个64位WIN32工程验证64位环境下方法调用是否正确,经验证正确!!!

结论:以上方法可以解决64位进程调用32位dll的问题

32位进程调用64位dll应该也可以通过这种方法解决,原因64位windows系统下安装了32位和64位两套COM系统

 

程序64位化带来的问题和思考

1. 指针和long的转换
这是最基本的处理部分,由于32位系统下地址是32位,所以很多代码里都会存在这样的转换:
void* pData;
LONG lData;
lData = (LONG)pData;
现在地址是64位了,所以原来的这种转换就会导致地址高4Byte丢弃的问题.
这种转换向来被认为不安全,但还是大量出现,实际程序员代码的时候只要用一个指针保留就可以,完全没有必要用一个long保留,同样的问题也会出现在函数指针的保留.
思考:
所谓的存在即合理的思维在作怪吧,很多程序员认为这么做程序运行的好好的,所以不管是否有风险还照样这么写,实际上改成安全写法代码多不了多少.
当然不排除很多程序员对16位、32位、64位完全没有概念的.
还有一点可能是微软的windows消息给人以误导,消息的WPARAM和LPARAM中常会夹带数据指针,然后强制类型转换,但是看一下它们的定义,其中隔了一层,虽然只是语法上的小小手脚,但是绝对是有先见之明, 而很多人只会依样画葫芦而未明白期间的巧妙.

2. PE Import和Export Table的变化
程序中用到了PE hack,由于之前有人发现vista 32下原来所需要替换的一个import table中的函数找不到了,结果竟然去修改了export table, 姑且不谈修改export table的危险吧,结果vista 64的时候发现PE格式有了小小的变更.
MS将Import table中的地址转成了64bit,但是export table却还是32bit,估计其认为代码不可能大于4GB吧,这样就会发现更改export table变得很难,因为不同的dll会被load在不同的地址段,其间差距会大于4GB,所以只有将自己的dll强制定义在和目标dll相同的4GB范围内.
回头来说这种更改是极其危险的,因为对于export table OS会尽量保持全局唯一,所以一旦你的dll退出未能正确恢复原有的值,会造成其他所有使用到这个dll的程序crash.
最终我发现需要hack的那个函数放在了delayload import table中,简单的修改就解决了问题.
如果这个问题最终找不到而还是采用修改export table的方法,那么就会很惨,测试部门已经发现了经常性的系统crash.
思考:
这里出现了弯路,这个弯路项目的时间压迫难辞其咎,但是程序员未能自己去分析问题是更大的问题,vista在很多方面都保留了很好的兼容性,去分析下windows的目录就会发现基本和xp区别不大,所以很多基础dll的功能也没有变化(这一点让我感觉到vista并未如其发表的那样70%代码重写),在分析dll的时候只要稍微仔细点就会找到问题很简单.
另一个问题就是MS在定义PE格式的时候的确有很高的前瞻性,很好的保持了松耦合的能力,其实PE格式在2001年后就基本没有什么大的更改,那时大家还基本没有64位的概念,这一点是需要学习的,但是同样对于export table没有扩展到64bit我还是保留意见,难道真的认为4GB不会超过?当年盖茨大叔不是宣称640KB就够了吗^.^

3. DelayLoad的问题
DelayLoad这个特性在VC6开始出现,一般大家不会去接触,包括我自己,要不是这次机会也不会去看.
推荐一篇文章
http://www.microsoft.com/msj/0200/hood/hood0200.aspx
有详细的阐述,有兴趣可以自己看一下,其实其原理很简单,主要是在使用到某个dll的接口的时候再去load这个dll,这样可以节约空间,因为有些dll中的接口可能在程序周期中永远不会用到, 而且如果某个dll没有,如果没有用到,那么整个程序也能继续执行.
这真的是一个巧妙的设定, 虽然在第一次呼叫的时候会有小小的性能损失,并且代码也会稍微大一点,但是可能会带来更大的空间节省.
但是随即带来的思考, 为什么这么好的一个东西会那么生疏呢?个人考虑有以下原因:
1. 这种技术显然对于很多人来说过于底层了,有多少人在意编译出来的程序是怎么样存放,又是如何被加载运行的呢?
2. DelayLoad带来的好处对很多人不可见,只要我的程序编译通过,并且可以正确执行就可以了,节省了那么些空间或者扩展兼容有必要考虑吗?
3. 现在的硬件太好了,何必要去做这种优化呢?内存消耗多了,没什么嘛去买1GB内存插上不久解决了?
4. 编译系统不够智能,可能Visual Studio做的更加智能些,自动去分析然后产生delay load会更好吧?

<think>好的,我现在需要帮助用户了解如何查看C代码中哪部分依赖libcrypto-3-x64.dll文件。首先,我得回忆一下相关知识,可能涉及编译链接过程中的依赖分析,或者运行依赖检测。 首先,用户的问题是关于C代码动态链接库的依赖关系。libcrypto通常是OpenSSL的一部分,提供加密相关函数。所以,用户可能在代码中使用了OpenSSL的函数,比如SHA256、AES加密等。当代码调用这些函数,编译后的可执行文件会动态链接到libcrypto的DLL,比如libcrypto-3-x64.dll。 接下来,我需要思考如何确定代码中哪些部分依赖这个DLL。可能的步骤包括: 1. **检查头文件包含**:在C代码中,如果包含了OpenSSL的头文件,比如#include <openssl/evp.h>,那么可能使用了相关函数。但头文件可能被包含而不直接使用,所以这只是一个线索。 2. **查看函数调用**:在代码中搜索OpenSSL特有的函数,比如EVP_EncryptInit_ex、SHA256_Init等。这些函数的调用意味着需要链接libcrypto。 3. **编译的链接参数**:检查编译命令是否有-lcrypto或指定libcrypto的库路径。如果链接了该库,说明代码中有依赖。 4. **使用工具分析依赖**: - **静态分析**:用nm或objdump查看目标文件或可执行文件的符号,寻找未定义的符号,这些符号可能在libcrypto中定义。 - **动态分析**:使用Dependency Walker(Windows)或ldd(Linux)查看运行依赖,但用户提到的是Windows的DLL,所以Dependency Walker可能更合适。或者使用Windows的工具如Process Explorer查看加载DLL。 5. **动态链接库的符号解析**:在Windows下,可以使用dumpbin工具(Visual Studio自带)来查看导入的符号。例如,dumpbin /imports executable.exe,然后查找libcrypto-3-x64.dll中的函数。 不过,用户可能需要的是在代码级别确定哪部分代码调用了该DLL中的函数,而不仅仅是确认存在依赖。所以可能需要结合代码审查工具分析。 步骤可能如下: 1. **代码审查**:在代码中搜索所有调用OpenSSL API的地方,比如以EVP_、RSA_、SHA256_等开头的函数。 2. **编译中间文件分析**:编译生成的目标文件(.o或.obj)中,可以通过工具查看引用了哪些外部符号。例如,在Linux下使用nm查看未定义的符号,Windows下使用dumpbin /symbols查看。 3. **链接阶段检查**:链接如果出现找不到符号的错误,可能需要链接libcrypto。如果成功链接,可执行文件就会依赖该DLL。 4. **运行监控**:使用调试器或DLL依赖跟踪工具,在运行程序查看哪些函数被调用,从而定代码置。 可能的混淆点:用户可能将动态链接库的依赖静态库混淆,需要明确是动态链接。此外,不同平台工具不同,用户提到的是libcrypto-3-x64.dll,说明是Windows 64系统,因此应推荐Windows工具,如dumpbin、Dependency Walker(可能已过,但可能有替代品如Dependencies)或Process Monitor来监控文件访问。 总结步骤: - 使用代码搜索找出OpenSSL函数调用。 - 使用dumpbin检查可执行文件的导入表,确认是否有来自libcrypto-3-x64.dll的函数。 - 编译检查链接器参数,确认是否链接了libcrypto。 - 运行使用工具监控DLL加载情况,并定调用的函数,从而回溯到代码置。 需要注意的潜在问题:如果代码动态加载DLL(比如通过LoadLibraryGetProcAddress),这种情况下编译不会直接链接,需要检查代码中显式加载的函数。这静态分析代码中的字符串(如"DLL名称"或函数名)会更有效。 最后,需要将这些步骤以清晰的方式呈现给用户,分步骤解释,确保用户能逐步操作。</think>要查看C代码中哪部分依赖`libcrypto-3-x64.dll`,可通过以下步骤分析: --- ### **1. 代码审查:查找OpenSSL函数调用** - **方法**:在代码中搜索所有OpenSSL相关函数,这些函数通常以`EVP_`、`RSA_`、`SHA256_`等前缀开头。 - **示例**: ```c #include <openssl/evp.h> // 包含此头文件说明可能依赖libcrypto void encrypt_data() { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); // 调用OpenSSL函数 // ... } ``` - **工具**:使用文本搜索工具(如`grep`、IDE全局搜索)查找以下关键词: ```bash grep -rnw 'EVP_\|RSA_\|SHA256_\|HMAC_' /path/to/code ``` --- ### **2. 检查编译的链接参数** - **查看编译命令**:确认编译命令中是否包含链接`libcrypto`的选项。 - 在Linux/macOS中,通常为`-lcrypto`。 - 在Windows中,可能直接指定库文件(如`libcrypto.lib`)。 - **示例**: ```bash gcc code.c -o output.exe -lssl -lcrypto # 链接了libcrypto ``` --- ### **3. 静态分析目标文件/可执行文件** - **Windows工具(Visual Studio Command Prompt)**: 1. 使用`dumpbin`查看可执行文件的导入表: ```bash dumpbin /imports your_program.exe | find "libcrypto-3-x64.dll" ``` 输出会列出所有从该DLL导入的函数(如`EVP_EncryptInit_ex`)。 2. 查看目标文件(`.obj`)的未解析符号: ```bash dumpbin /symbols your_object.obj | find "UNDEF" ``` 若存在`UNDEF`符号(如`EVP_EncryptInit_ex`),说明依赖外部库。 - **Linux工具(交叉编译环境)**: - 使用`nm`查看未定义符号: ```bash nm -u your_program | grep EVP_ ``` --- ### **4. 动态分析运行依赖** - **Dependency Walker(旧版Windows工具)**: 1. 运行Dependency Walker并打开可执行文件。 2. 查看`libcrypto-3-x64.dll`是否被加载。 3. 展开该DLL,查看具体调用了哪些函数。 - **Process Monitor(实监控DLL加载)**: 1. 运行Process Monitor,过滤进程名称为你的程序。 2. 监控文件系统操作,观察是否加载`libcrypto-3-x64.dll`。 --- ### **5. 处理动态加载(LoadLibrary/GetProcAddress)** 如果代码显式加载DLL: ```c HMODULE hLib = LoadLibrary("libcrypto-3-x64.dll"); FARPROC func = GetProcAddress(hLib, "EVP_EncryptInit_ex"); ``` - **检查代码中所有`GetProcAddress`调用**,确认是否引用了该DLL的函数。 --- ### **总结步骤** 1. **代码层面**:搜索OpenSSL函数调用。 2. **编译层面**:检查是否链接`libcrypto`。 3. **静态分析**:用`dumpbin`确认导入函数。 4. **动态分析**:用工具监控运行DLL加载。 通过以上方法,可精准定代码中依赖`libcrypto-3-x64.dll`的部分。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值