7

我正准备执行一些我们内部人员不应再使用的旧 16 位应用程序。它们是 1985 年的 DOS 应用程序,因此捕获它们很容易……捕获在 NTVDM.exe 下启动的任何进程

现在,问题是找出 NTVDM 实际在后台运行的程序。显然,应该允许它们运行一些 1985 年的程序,所以我需要查看隐藏在 NTVDM 下的实际 EXE 名称。

        WqlEventQuery query =
            new WqlEventQuery("__InstanceCreationEvent",
            new TimeSpan(0, 0, 1),
            "TargetInstance isa \"Win32_Process\"");

        ManagementEventWatcher watcher = new ManagementEventWatcher(query);

        watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);

        watcher.Start();


...


    static void watcher_EventArrived(object sender, EventArrivedEventArgs e)
    {
        ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];

        ProcessInfo PI = new ProcessInfo();
        PI.ProcessID = int.Parse(instance["ProcessID"].ToString());
        PI.ProcessName = instance["Name"].ToString();
        PI.ProcessPath = instance["ExecutablePath"].ToString();

        // Here's the part I need...
        PI.ActualEXE = ???;

        // ... do the magic on the PI class ...

        instance.Dispose();
    }

当我捕获实例信息时,我可以得到命令行,但参数是“-f -i10”......命令行上没有EXE名称。我应该查看任何其他方法/属性来确定实际运行的 16 位应用程序的 EXE 名称吗?

更新:让我细化这个问题:如果我能找到 NTVDM 进程,我如何——以编程方式——知道正在下面执行的 EXE 的实际路径?

谢谢。

4

3 回答 3

6

诀窍不是使用VDMEnumProcessWOW(它提供 VDM),而是使用VDMEnumTasksWOW。将针对指定 VDM 中的每个 16 位任务调用传递给此函数的枚举器函数。

我自己没有检查过,但是根据文档,如果您传入 PROC16 枚举值,这个CodeProject 库正是这样做的。它是 C++,如果您需要帮助编译该代码并从 C# 调用它,请告诉我,我会给您一个示例。

使用这种技术的程序是Process Master,它带有完整的源代码。我建议你运行它来看看它是否提供了你需要的信息,如果是这样,你可以将此方法应用于你自己的应用程序(它不能在 Windows Vista 或 7 上运行,它使用旧的 VB5 代码,显然不是兼容。它应该在 XP 上运行)。

如果具有这些功能的事情没有按计划进行,您可能在 Vista 上并且可能需要此StackOverflow 问题中描述的修补程序,它指向下载修补程序,这又在此处描述

“使用 VDMEnumProcessWOW 函数枚举虚拟 DOS 机器的应用程序在运行 32 位版本的 Windows Vista 的计算机上不返回任何输出或不正确的输出”

更新:虽然这看起来很有希望,但我应用了补丁,运行了几个版本的代码,包括微软的,虽然它们都在 XP 上工作,但在 Vista 上它们会默默地失败(没有错误或错误的返回值)。


“有点”的工作代码

更新:我(除其他外)使用以下代码进行了试验,该代码在 C# 中编译得很好(并且可以更简单地编写,但我不想冒元帅错误的风险)。添加这些函数时,可以调用Enum16BitProcesses,它将16位进程的EXE文件的文件名写入Console。

我无法在 Vista 32 位上运行它。但也许其他人可以尝试编译它,或者在代码中找到错误。很高兴知道它是否适用于其他系统:

public class YourEnumerateClass
{
    public static void Enum16BitProcesses()
    {
        // create a delegate for the callback function
        ProcessTasksExDelegate procTasksDlgt = 
             new ProcessTasksExDelegate(YourEnumerateClass.ProcessTasksEx);

        // this part is the easy way of getting NTVDM procs
        foreach (var ntvdm in Process.GetProcessesByName("ntvdm"))
        {
            Console.WriteLine("ntvdm id = {0}", ntvdm.Id);
            int apiRet = VDMEnumTaskWOWEx(ntvdm.Id, procTasksDlgt, IntPtr.Zero);
            Console.WriteLine("EnumTaskWOW returns {0}", apiRet);
        }
    
    }
    
    // declaration of API function callback
    public delegate bool ProcessTasksExDelegate(
        int ThreadId,
        IntPtr hMod16,
        IntPtr hTask16,
        IntPtr ptrModName,
        IntPtr ptrFileName,
        IntPtr UserDefined
        );
    
    // the actual function that fails on Vista so far
    [DllImport("VdmDbg.dll", SetLastError = false, CharSet = CharSet.Auto)]
    public static extern int VDMEnumTaskWOWEx(
        int processId, 
        ProcessTasksExDelegate TaskEnumProc, 
        IntPtr lparam);
    
    // the actual callback function, on Vista never gets called
    public static bool ProcessTasksEx(
        int ThreadId,
        IntPtr hMod16,
        IntPtr hTask16,
        IntPtr ptrModName,
        IntPtr ptrFileName,
        IntPtr UserDefined
        )
    {
        // using PtrToStringAnsi, based on Matt's comment, if it fails, try PtrToStringAuto
        string filename = Marshal.PtrToStringAnsi(ptrFileName);
        Console.WriteLine("Filename of WOW16 process: {0}", filename);
        return false;       // false continues enumeration
    }

}

更新: 著名的 Matt Pietrek读到的有趣的书。请注意这句话,靠近结尾的某个地方:

“对于初学者来说,基于 MS-DOS 的程序似乎总是在单独的 NTVDM 会话中运行。我永远无法让基于 MS-DOS 的程序与基于 16 位 Windows 的程序在同一会话中运行。也不是“我能够让两个独立启动的基于 MS-DOS 的程序在同一个 NTVDM 会话中运行。事实上,运行 MS-DOS 程序的 NTVDM 会话不会出现在 VDMEnumProcessWOW 枚举中。”

似乎,要找出加载了哪些进程,您需要在 NTVDM 中编写一个挂钩或编写一个监听器来监视对文件的访问。当试图读取某个 DOS 文件的应用程序是 NTVDM.exe 时,它​​就是宾果游戏。您可能想编写一个仅附加到 NTVDM.exe 的 DLL,但现在我们有点超前了。长话短说:这次进入 NTVDM 的小插曲显示了最终出现真正恶作剧的“可能性”。

还有另一种方法,但时间太短,无法创建示例。您可以在 DOS 内存段中四处寻找,而 EXE 通常加载在同一段中。但我不确定这是否最终会导致相同的结果以及是否值得付出努力。

于 2009-11-09T16:45:42.967 回答
2

这对我有用:

  • 按照Windows XP 中的软件限制策略说明中的说明打开本地或域策略编辑器。

  • 在 Software Restriction Policies -> Additional Rules 下,右键单击并选择 New Hash Rule。

  • 浏览到(例如)edit.com。确保安全级别设置为不允许。单击确定。

现在,

C:\>edit
The system cannot execute the specified program.

command.com(在 Win XP 下,我得到了相同的结果cmd.exe

于 2009-11-09T05:02:42.443 回答
1

这个关于 VDMDBG 函数的链接,您可以 P/Invoke “VDMEnumProcessWOW()”,然后使用 PSAPI 枚举进程中的模块

关于 16 位 DOS 应用程序的注意事项:

VDMDBG 功能均不适用于 16 位 DOS 应用程序。要枚举 DOS VDM,您需要使用另一种方法。首先,您可以使用 VDMEnumProcessWOW() 列出所有 Win16 VDM,然后使用其他方案(例如 PSAPI)枚举 NTVDM.exe 的所有实例。完整枚举中不在 Win16 列表中的任何 NTVDM.exe 都是 DOS VDM。您可以使用 CreateProcess() 和 TerminateProcess() 创建和终止 16 位 DOS 应用程序。

希望有帮助...

于 2009-11-02T22:47:12.063 回答