ring0下通过OUT指令实现强制关机和重启

Sunshine

发布日期: 2019-04-15 16:24:29 浏览量: 975
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

背景

通常情况下,我们可以通过调用 NtShutdownSystem 未导出的内核 API 函数,来在内核层下实现计算机的关机和重启等操作。 这种实现方式虽然在内核层下实现,但是相对来说,还是太过于表层了。

本文要介绍一种在内核层下实现的强制关机以及重启的方法,即通过汇编 OUT 指令,控制端口输出,从而实现计算机的关机和重启。这种方式,较为低层,而且更难防御和监控。现在,我就把程序实现过程和原理,整理成文档,分享给大家。

实现原理

我们通过汇编 OUT 指令,控制端口的输出,从而实现控制计算机关机或者重启。

对于实现关机操作,则可以向 1004H 端口,写入 2001H,汇编代码为:

  1. mov ax, 2001h
  2. mov dx, 1004h
  3. out dx,ax
  4. ret

则,对应的机器码为:

  1. {0x66,0xB8,0x01,0x20,0x66,0xBA,0x04,0x10,0x66,0xEF,0xC3}

对于实现重启操作,则可以向 64H 端口,写入 0FEH,汇编代码为:

  1. mov al, 0FEh
  2. out 64h, al
  3. ret

则,对应的机器码为:

  1. {0xB0,0xFE,0xE6,0x64,0xC3}

所以,我们可以通过申请一块非分页内存,写入 Shellcode 数据,并声明函数指针,调用 Shellcode 数据并执行,从而实现相应的关机或者重启操作。当然,我们也可以直接写成汇编代码,但是,要注意 64 位系统下汇编代码要保存为 .asm 汇编文件,添加到工程中。

编码实现

强制关机

  1. // 强制关机
  2. BOOLEAN ShutdownForce()
  3. {
  4. // {0x66, 0xB8, 0x01, 0x20, 0x66, 0xBA, 0x04, 0x10, 0x66, 0xEF, 0xC3}
  5. UCHAR ShellCode[11] = { 0x66, 0xB8, 0x01, 0x20, 0x66, 0xBA, 0x04, 0x10, 0x66, 0xEF, 0xC3 };
  6. ULONG ulShellcodeSize = 11;
  7. #ifdef _WIN64
  8. // 64 位
  9. typedef VOID(__fastcall *typedef_SHUTDOWNFUNC)();
  10. #else
  11. // 32 位
  12. typedef VOID(__stdcall *typedef_SHUTDOWNFUNC)();
  13. #endif
  14. // 申请内存
  15. typedef_SHUTDOWNFUNC ShutdownFunc = (typedef_SHUTDOWNFUNC)ExAllocatePool(NonPagedPool, ulShellcodeSize);
  16. if (NULL == ShutdownFunc)
  17. {
  18. DbgPrint("ExAllocatePool Error!\n");
  19. return FALSE;
  20. }
  21. // 写入数据
  22. RtlCopyMemory(ShutdownFunc, ShellCode, ulShellcodeSize);
  23. // 执行Shellcode代码实现关机
  24. ShutdownFunc();
  25. // 释放内存
  26. ExFreePool(ShutdownFunc);
  27. return TRUE;
  28. }

强制重启

  1. // 强制重启
  2. BOOLEAN RebootForce()
  3. {
  4. // {0xB0, 0xFE, 0xE6, 0x64, 0xC3}
  5. UCHAR ShellCode[11] = { 0xB0, 0xFE, 0xE6, 0x64, 0xC3 };
  6. ULONG ulShellcodeSize = 5;
  7. #ifdef _WIN64
  8. // 64 位
  9. typedef VOID(__fastcall *typedef_REBOOTFUNC)();
  10. #else
  11. // 32 位
  12. typedef VOID(__stdcall *typedef_REBOOTFUNC)();
  13. #endif
  14. // 申请内存
  15. typedef_REBOOTFUNC RebootFunc = (typedef_REBOOTFUNC)ExAllocatePool(NonPagedPool, ulShellcodeSize);
  16. if (NULL == RebootFunc)
  17. {
  18. DbgPrint("ExAllocatePool Error!\n");
  19. return FALSE;
  20. }
  21. // 写入数据
  22. RtlCopyMemory(RebootFunc, ShellCode, ulShellcodeSize);
  23. // 执行Shellcode代码实现重启
  24. RebootFunc();
  25. // 释放内存
  26. ExFreePool(RebootFunc);
  27. return TRUE;
  28. }

程序测试

在 Win7 32 位系统下,驱动程序正常运行;在 Win10 64 位系统下,驱动程序正常运行。

总结

对于这个层序,虽然编码较为简单,但是要想兼容 32 位和 64 位计算机,则需要特别注意一个问题:

在声明函数指针的时候,对于 32 位函数,调用约定为 __stdcall;对于 64 位函数,调用约定为 __fastcall。

所以,一定要注意不同系统位数下,函数的调用约定声明。否则,会出现错误。

上传的附件

发送私信

这个世界上我只相信两个人,一个是我,另一个不是你

13
文章数
15
评论数
最近文章
eject