65.9K
CodeProject 正在变化。 阅读更多。
Home

VGA文本模式:地狱之旅与归来 (第二部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2018年5月19日

CPOL

2分钟阅读

viewsIcon

10131

本文展示了如何在Windows上进入VGA文本模式并从中返回。

引言

在 Windows Vista 和 Windows 7 上,显示驱动程序可以遵循 XPDM 或 WDDM。

之前的文章涵盖了 Windows 7 上的 XPDM 情况。 在 Windows 8 及更高版本上,显示驱动程序的唯一选择是遵循 WDDM。 在 WDDM 中,不再有单独的视频和显示驱动程序,我们只有显示端口和显示迷你端口。

根据这篇文章,引入了三种类型的显示迷你端口驱动程序

  1. 完整图形驱动程序
  2. 仅显示驱动程序
  3. 仅渲染驱动程序

在 Windows 8 上(未在 Windows 10 上检查),我们有默认的BasicDisplay.sysBasicRender.sys 驱动程序。 VirtualBox 提供了完整的图形驱动程序VBoxVideoW8.sys,可以替换默认配对。

这意味着我们不再可以调用视频驱动程序,因为没有涉及 EngDeviceIoControl。 因此,我们必须手动获取视频模式并设置它。 为了弄清楚如何做到这一点,我进入了 IOCTL_VIDEO_SET_CURRENT_MODE 调用

VgaDeviceIoControl(pDeviceObject, IOCTL_VIDEO_SET_CURRENT_MODE, 
                                   &VideoMode, sizeof(VideoMode), NULL, 0, &BytesReturned, FALSE);

并发现视频驱动程序使用 x86BiosCall 函数来设置视频模式。 牢记这一点,我进入 Windows 8 并设置了 x86BiosCall 上的断点。 我在 BasicDisplay!BiosSetDisplayMode 函数中找到了两个 x86BiosCall 调用。 第一个调用设置指定的视频模式,第二个调用检查当前的视频模式(模式是否设置成功)。

Using the Code

让我们看看代码。 用户模式部分保持不变,所以我不会在这里重复它。 我们需要禁用 GPU,运行驱动程序,刷新桌面,启用 GPU。 牢记这一点,让我们看看驱动程序本身。

#include <wdm.h>

#define DELAY_SECOND                        -10000000

struct X86_BIOS_REGISTERS
{
    ULONG Eax;
    ULONG Ecx;
    ULONG Edx;
    ULONG Ebx;
    ULONG Ebp;
    ULONG Esi;
    ULONG Edi;
    USHORT SegDs;
    USHORT SegEs;
};

extern "C"
{
    NTKERNELAPI BOOLEAN x86BiosCall
        (ULONG InterruptNumber, X86_BIOS_REGISTERS *pRegisters);        // import from HAL.DLL

    NTKERNELAPI VOID VidResetDisplay(BOOLEAN HalReset);                 // import from BOOTVID.DLL

    NTKERNELAPI VOID VidDisplayString(PUCHAR String);                   // import from BOOTVID.DLL
}

NTSTATUS VgaGetMode(ULONG *pMode)
{
    BOOLEAN b;
    NTSTATUS Status;
    X86_BIOS_REGISTERS Registers;

    memset(&Registers, 0, sizeof(Registers));

    Registers.Eax = 0x4F03;

    b = x86BiosCall(0x10, &Registers);

    if ((b) && (Registers.Eax == 0x4F))
    {
        *pMode = Registers.Ebx;
        Status = STATUS_SUCCESS;
    }
    else Status = STATUS_UNSUCCESSFUL;

    return Status;
}

NTSTATUS VgaSetMode(ULONG Mode)
{
    BOOLEAN b;
    NTSTATUS Status;
    X86_BIOS_REGISTERS Registers;

    memset(&Registers, 0, sizeof(Registers));

    Registers.Eax = 0x4F02;
    Registers.Ebx = 0x4000 | Mode;

    b = x86BiosCall(0x10, &Registers);

    if ((b) && (Registers.Eax == 0x4F))
    {
        Status = STATUS_SUCCESS;
    }
    else Status = STATUS_UNSUCCESSFUL;

    return Status;
}

void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
}

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
    ULONG Mode;
    NTSTATUS Status;
    LARGE_INTEGER Interval;

    Status = VgaGetMode(&Mode);

    if (NT_SUCCESS(Status))
    {
        VidResetDisplay(TRUE);
        VidDisplayString((PUCHAR)"HELLO\n");

        Interval.QuadPart = DELAY_SECOND * 10;
        KeDelayExecutionThread(KernelMode, FALSE, &Interval);

        VgaSetMode(Mode);
    }

    pDriverObject->DriverUnload = DriverUnload;

    return STATUS_SUCCESS;
}

有关 VESA BIOS Extensions 命令,请参阅此链接

有关启动视频函数原型,请参阅 ReactOS 源代码仓库。

如果我们尝试编译我们的驱动程序,我们将收到以下链接器错误

x86 构建

1>Source.obj : error LNK2019: 
unresolved external symbol __imp__VidResetDisplay@4 referenced in function _DriverEntry@8
1>Source.obj : error LNK2019: 
unresolved external symbol __imp__VidDisplayString@4 referenced in function _DriverEntry@8

x64 构建

1>Source.obj : error LNK2019: 
unresolved external symbol __imp_VidResetDisplay referenced in function DriverEntry
1>Source.obj : error LNK2019: 
unresolved external symbol __imp_VidDisplayString referenced in function DriverEntry

要从 BOOTVID.DLL 导入,我们需要创建导入库并将其添加到驱动程序项目的链接器输入中。 为此,我们需要 dumpbinlib 工具。 在我的系统上,它们位于

x86

D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin

x64

D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64

BOOTVID.DLLC:\Windows\System32 复制到适当的目录(取决于您的 Windows 是 64 位还是 32 位),然后运行以下 cmd 命令

dumpbin /exports BOOTVID.DLL

它将为您提供导出的函数名称及其序号

创建模块定义文件并将其复制到适当的目录(取决于您是为 x86 还是 x64 编译驱动程序)。 模块定义文件 BOOTVID.def 内容

x86

EXPORTS

VidDisplayString@4 @5

VidResetDisplay@4 @8

x64

EXPORTS

VidDisplayString

VidResetDisplay

请注意,对于 x86 构建,我们需要在函数名称后加上后缀并指定序号,而对于 x64 构建,我们可以使用纯函数名称。

运行以下 cmd 命令

lib /def:BOOTVID.def /out:BOOTVID.lib

将生成导入库

现在将导入库复制到您的项目目录,并将其添加到链接器输入(项目属性 -> 链接器 -> 输入 -> 附加依赖项)。

直接从 BOOTVID.DLL: Inbv* 函数导入的原因在于,它们在 Windows 7 上的工作方式不同。

此后,驱动程序将可以正常编译。

© . All rights reserved.