引言
在 Windows 中,我们的进程通常都运行在一个叫 Windows 的子系统上。在 Windows 子系统上分为 console、GUI 等子系统。进程如果想要调用系统 API,通常都需要先调用子系统的 API,来做一次转换。简单结构如下:
而这里我们说的 Native App 呢,它是不依赖任何子系统,可以直接通过 ntdll.dll 来调用系统 API 的。简单结构如下:
Native App 的优势
对比普通的 Win32 或者说其他子系统应用,主要有以下几个优势
- 性能好。使用 native 接口,可以绕过标准 Windows 应用程序接口,从而去掉一个软件层,可以加快运行速度。
- 功能强大。标准 Windows Api 无法提供的功能,也可能通过 Native Api 来实现。
- 依赖少。不依赖子系统的 dll,减少很多依赖。
- 灵活性好。此应用不依赖子系统,所以不用等子系统启动后才能运行。
开发 Hello Native
创建项目
VS 中是没有 native 应用的模板的,所以这里我们先创建一个 console app,然后基于 cosnole app 来进行修改部分配置即可。
创建完成后,选择项目,鼠标右键弹出菜单,选择【属性】,如下图:
开始修改配置
按如下图配置,依次修改:
然后我们要处理一下默认依赖了:
首先将 【Ignore All Default Libraries】选择【Yes】
然后,添加其他的依赖。
这里需要注意的是,由于在标准的 ntdll.lib 中没有列出 ntdll.dll 中实现的 CRT 函数,因此链接可能出现问题。所以这里我们采用一个三方库:
Clone 下来,编译下就好了:
cd ntdll
nmake msvc
编译后,会生成 ntdll64.lib
和 ntdll86.lib
。在 Windows 64 位系统上,选择 ntdll64.lib
即可。然后再 额外依赖中添加对应的 lib 文件即可。
$(CoreLibraryDependencies)
%(AdditionalDependencies)
"D:libntdll64.lib"
总体配置如下:
💡 这里也可以通过 LdrGetDllHandle
和 LdrGetProcedureAddress
来做。但相对比较麻烦。如果不需要用到 swprintf_s
是不需要这一步的,用普通的 ntdll.dll 就可以
开始写代码
不同于普通应用,Native App 的入口函数是 NtProcessStartup
,这里我们给出简单的示例:
#include <phnt_windows.h>
#include <phnt.h>
extern "C" int swprintf_s(
wchar_t* _Buffer, USHORT size,
wchar_t const* _Format, ...);
NTSTATUS NtProcessStartup(PPEB peb) {
RTL_OSVERSIONINFOEXW osvi = { sizeof(osvi) };
RtlGetVersion(&osvi);
WCHAR text[256];
swprintf_s(text, ARRAYSIZE(text), L"Windows version: %d.%d.%dn",
osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
UNICODE_STRING str;
RtlInitUnicodeString(&str, text);
NtDrawText(&str);
LARGE_INTEGER li;
li.QuadPart = -10000000 * 10;
NtDelayExecution(FALSE, &li);
return STATUS_SUCCESS;
}
这里逻辑是先获取系统版本号,然后将它打印在屏幕上。
有读者可能会好奇,phnt_windows 和 phnt 是什么?
由于 Windows 中各种头文件的依赖,所以直接采用一个三方的头文件库。
通过 vcpkg.exe 安装 phnt
- clone 源代码
git clone https://github.com/Microsoft/vcpkg.git
- 运行安装脚本
cd vcpkg
.bootstrap-vcpkg.bat
- 集成到 VS
.vcpkg.exe integrate install
- 安装 phnt
.vcpkg.exe install phnt
编译
然后就可以编译通过啦。会得到一个 exe 文件。如下图:
怎么跑起来
当你双击这个 exe 尝试运行的时候,会发现根本无法运行。
这是因为 Native app 没法在子系统模式下运行。这里我们可以参考 autochk 的运行方式。
将 exe 文件拷贝到 system32 文件夹中,然后再打开注册表编辑器,切换到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager
,在 BootExecute
key 上添加一个 HelloNative。如下图:
然后重启,就可以看到在用户登录之前,显示 Windows 版本的提示了。
也就是我们的第一个 native app。
0