c语言中无windows函数库,关于C语言, GCC/MSVC中,如何写出一个真正意义上的不依赖库的程序?...

在Windows平台上,如果要写一个有意义的用户态程序(以任何方式显示数据或将数据写入设备/文件等),是几乎无法不依赖库的。

如果要让数据显示到控制台、存储数据到文件、或者绘制图形界面,一定需要操作系统的支持。Windows API是通过若干用户态的动态链接库(DLL)调用的。这些动态链接库对系统服务进行了封装,向开发者暴露稳定的接口;而系统服务暴露的接口有可能发生变化。

如果需要调用Windows NT系统服务,需要将系统服务号放进EAX寄存器,然后通过int 2Eh中断或syscall指令转移到特权等级0执行系统服务处理程序。系统服务处理程序通过系统服务描述符表(System Service Descriptor Table,简称SSDT)跳转到对应的内核函数。内核函数会依照调用约定获取栈或寄存器中的参数进行处理。

这些系统服务调用被包装成C语言函数,通过ntdll.dll导出API。其中一部分可以通过Windows DDK在驱动程序中使用,接口相对稳定;而另一部分则是仅仅导出了符号,并没有收录在文档中,这些函数的使用方式有可能在未来被微软更改。

原则上只要知道了系统服务号,就可以不依赖任何库调用Windows NT的内核函数。问题在于,由于系统调用是不对开发者开放的,同一个内核函数对应的系统服务号会随着Windows版本更新而变化;而且在32位环境中,原生32位系统服务调用与WoW64下(在64位操作系统上运行32位应用)的系统服务调用是完全不同的,需要使用特殊的系统服务号,并额外借助WoW64的状态转移机制。

理论上如果要完美解决这个问题,需要记录Windows NT内核每个版本操作系统中各个内核函数对应的系统服务号,然后通过进程环境数据动态检测系统版本号和WoW64状态决定使用哪一个系统服务号以及是否使用WoW64状态转移,还要紧盯系统新版本可能带来的变化。

因此可以得出这样的结论:哪怕开发者可以绕开Windows API的kernel32.dll和user32.dll,ntdll.dll也是几乎不可逾越的障碍,绕开这个对于实际开发来说不现实。

如果抛弃版本兼容性,确实可以轻松写出不依赖任何库的程序,但是只能在特定版本的Windows中成功运行。下面举例说明做到这一点的方法。

Windows 10使用控制台驱动程序(Console Driver)与控制台进行数据交换,因此操作控制台的方式与操作其他Windows设备的方法相似。

一般情况下,用户态程序调用Windows API的DeviceIoControl函数操作设备的驱动程序进行I/O,然而这个函数依赖kernel32.dll。出于演示目的,这里绕过Windows API,直接通过底层的NtDeviceIoControlFile函数进行系统服务调用。

下面展示如何通过操作Windows 10控制台驱动程序把Brain Kernighan著名的Hello World程序改写成不依赖任何库的形式。以下是Brain Kernighan的C语言源代码:

首先需要把NtDeviceIoControlFile的系统服务调用从ntdll.dll中抽出,从而消除对ntdll.dll的依赖。下面是这个函数的MASM汇编代码:

接下来用C语言实现字符串的输出。源代码如下:

运行Visual Studio的x64本机工具命令提示,用下列命令行进行编译:

链接后生成一个非常小的可执行文件(Visual C++2019生成的文件大小为2.5 KB)。使用Dependency Walker查看这个文件,可以看到没有依赖任何库:

b0ff29385123aa5793a3f85bd8b538fd.png

在64位Windows 10(我使用的版本是Windows 1020H210.0.19042.685)的命令提示符或PowerShell中运行这个文件,即可在控制台上看到输出:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值