在NTVDM下运行的16位应用程序

时间:2009-10-30 13:31:45

标签: c# process ntvdm

我正在捕获执行我们内部人员不应再使用的一些旧的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();
    }

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的实际路径?

感谢。

3 个答案:

答案 0 :(得分:5)

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

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

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

如果具有这些功能的内容未按计划进行,您可能在Vista上,可能需要此StackOverflow question中所述的修补程序,该修补程序指向downloading a hotfix,而described here依次为Intriguing read }:

  

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

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


“有点”工作代码

更新: 我使用以下代码进行了实验(其中包括),这些代码在C#中编译得很好(并且可以写得更简单,但我不想遇到元帅错误的风险)。添加这些函数时,可以调用Enum16BitProcesses,它将16位进程的EXE文件的文件名写入控制台。

我无法在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的

更新: {{3}}。记住句子,在接近结尾的地方:

  

“对于初学者来说,基于MS-DOS的程序   似乎总是在单独的NTVDM中运行   会话。我无法得到一个   基于MS-DOS的程序在运行   与基于Windows的16位相同的会话   程序。我也不能得到两个   独立启动基于MS-DOS   程序在同一个NTVDM中运行   会话。实际上,NTVDM会话   运行MS-DOS程序不会显示   在VDMEnumProcessWOW枚举中。“

似乎为了找出加载的进程,你需要在NTVDM中编写一个钩子,或者编写一个监视器来监视对该文件的访问。当试图读取某个DOS文件的应用程序是NTVDM.exe时,它就是宾果游戏。您可能想编写一个仅附加到NTVDM.exe的DLL,但现在我们已经领先于自己了。长话短说:这次进入NTVDM的小车已经显示出“可能性”,最终出现了真正的恶作剧。

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

答案 1 :(得分:2)

这对我有用:

现在,

  

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

(我从command.comcmd.exe得到了相同的结果 - 在Win XP下)

答案 2 :(得分:1)

this link about VDMDBG functions,您可以P / Invoke“VDMEnumProcessWOW()”,然后enumerate modules within the process using PSAPI

  

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

     

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

希望有帮助...