32位应用System32文件重定向问题

9 篇文章 0 订阅
5 篇文章 0 订阅

    近日,因为工作需要,在公司的一个现有的解决方案里开发一个工具并打包为.msi的安装包(c#),主输出项目为dll

1、基本痛点

    1)现有解决方案为32位工具集

    2)自己的工具需要使用一个64位独有的命令工具

2、尝试解决

    1)将c++的dll输出平台不变,依旧位win32,其他c#的输出平台全换为X64,失败,会报错,如下

    上网搜了下解决方案,说是64位应用程序调用32位dll或32位应用程序调用64位dll出现的问题,推荐解决方案是将文件的输出平台换位X86就可以,但是我要是换成X86那我怎么访问我的64位独有命令?转念一想,我可是有dll源码的啊,我把这些dll也用x64编译一遍不就得了,OK,我将所有的工程输出平台全部换成X64,测试,又失败了,这一步,懵逼。所有的dll我都用x64重新生成了一遍啊。

    又一次上网查看又没有类似的情况,有提到dll文件报这种错,不仅仅是dll文件是64位的,可能是因为调用他的应用程序是32位的,而我的.msi安装包是微软自带的安装格式,所以可能是对应的installUtil.exe版本不对,查阅MSDN,如下:

于是我更换了项目输出的程序集为MSIL(也就是目标平台位anyCPU),这次可以用了,但是,附上自己的代码,没反应。

    另一方面,我的代码是我自己之前单创的工程跑通了的,所以代码不会有问题,但是以防万一,我依旧是加上了Log代码,存到了文件里,根据log的表现,应该是我使用的命令没有找到。

    以上,代码没问题,目标平台没问题,但是在之前目标平台CPU架构设置时,使用X64生成的包无法正常使用,有可能仍然是有依赖的程序集为32位,但是我的解决方案中除了主输出以外所有的dll我都使用X64重新编译了,那么问题就可能出在了系统的库程序集,但是又不知道是哪一项,没辙,只能是用最笨的办法——穷举,根据同事推荐,我首先使用depend工具查看了下我的主输出项目的依赖属性,将所有依赖的dll列举出来,然后使用一个叫CFF-EXplorer的工具,将主输出项目的所有dll一一放进去观察其对应的位数,不出所料,真凶已然显出身影:

这三个程序集是32位下生成的,所以我的主项目只能是32位架构。

    综上分析,想要生成64位的项目怕是没戏了,换个思路吧,试试在32位机器中访问64的工具。

    查阅MSDN的时候,发现了一个好东西,叫做重定向管理器,

以上摘自MSDN,对应连接:https://docs.microsoft.com/zh-cn/windows/desktop/WinProg64/file-system-redirector

可以通过微软提供的API关闭,Wow64DisableWow64FsRedirection搭配Wow64RevertWow64FsRedirection这两个API分别用来关闭重定向和打开重定向。

    so,用呗!

IntPtr oldWOW64State = new IntPtr();
if(Wow64DisableWow64FsRedirection(ref oldWOW64State);)

{

    自己的代码;

    Wow64RevertWow64FsRedirection(oldWOW64State);

}

    现实是,又一次被捶翻了, 依旧访问不到,不信邪的我,改了一下代码

IntPtr oldWOW64State = new IntPtr();
if(Wow64DisableWow64FsRedirection(ref oldWOW64State);)

{

    WriteLog("Redirection succeed!")//自己实现的写log函数

    if (File.Exists(@"C:\Windows\System32\命令名))

    {

        WriteLog("find cmdlet succeed!")

        自己的代码;

    }

    Wow64RevertWow64FsRedirection(oldWOW64State);

}

    现实又一次将我按在地上,我的log文件中这两个日志都打印出来了,也就是说,文件重定向函数返回值为真,根据MSDN描述,重定向应该是关掉了,但是我的命令就是没执行,我又开始自我怀疑了,代码有问题?于是我打开了powershell,敲了自己的命令,有效果,又把自己的测试工程(64位的)打开,代码拷进去,也成功执行了。看来不是代码的问题,那就只能回过头来,这个重定向API未生效,那么为什么呢?是有什么特殊情况我没有注意到吗?我再次打开了MSDN,看到这个例子里的一行注释。

#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501

#ifdef NTDDI_VERSION
#undef NTDDI_VERSION
#endif
#define NTDDI_VERSION 0x05010000

#include <Windows.h>

void main()
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    PVOID OldValue = NULL;

    //  Disable redirection immediately prior to the native API
    //  function call.
    if( Wow64DisableWow64FsRedirection(&OldValue) ) 
    {
        //  Any function calls in this block of code should be as concise
        //  and as simple as possible to avoid unintended results.
        hFile = CreateFile(TEXT("C:\\Windows\\System32\\Notepad.exe"),
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

        //  Immediately re-enable redirection. Note that any resources
        //  associated with OldValue are cleaned up by this call.
        if ( FALSE == Wow64RevertWow64FsRedirection(OldValue) )
        {
            //  Failure to re-enable redirection should be considered
            //  a criticial failure and execution aborted.
            return;
        }
    }
    
    //  The handle, if valid, now can be used as usual, and without
    //  leaving redirection disabled. 
    if( INVALID_HANDLE_VALUE != hFile )  
    {
        // Use the file handle
    }
}

    如下图所示,Note that any resoutces associated with OldValue are cleaned up by this call.和OldValue有关的所有资源都会被清理掉,我这一寻思,不对啊,我的命令在执行过程中要持续挺久,要给我直接干掉了,那肯定没效果啊,所以我试着改了改代码:

   

我将自己之前的ps.Invoke();改成了下面这两行代码,将我的命令执行改成了异步进行,开启重定向后新建一个线程,这个线程里执行我的命令,这样关闭重定向时清理线程资源时也不会影响到我

IAsyncResult result = ps.BeginInvoke();
ps.EndInvoke(result);

   再运行一次,成了!就很舒服。

    但是上面的其实是我在写这篇文章从MSDN截图的时候才注意到然后测试成功的解决方案,我自己用的并不是这个,接下来我要将我用的方案。还是MSDN,有这样一句,

所以,我将原先的C:\Windows\System32\命令名改成了C:\Windows\Sysnative\命令名,执行代码,成功!

 

综上,32位应用程序想要访问System32文件,有两种方案

1、Wow64DisableWow64FsRedirection搭配Wow64RevertWow64FsRedirection这两个API使用,但是需要注意:

    1)一定要匹配使用,并且要及时。否则后果,就像野指针一样可怕,甚至犹有过之!

    2)如果是一些持续性的进程则需要异步使用,新开启一个线程,使得该进程地资源不会在重定向结束地时候被清理掉。例如如果是用powershell或者cmd命令,就需要BeginInvoke()搭配EndInvoke()使用,如果是process进程,则需要start()搭配WaitForExit()使用。

2、使用Sysnative别名来访问,但是需要注意,64位的应用程序无法使用这个别名

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值