Sunshine的文章

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

    背景通常情况下,我们可以通过调用 NtShutdownSystem 未导出的内核 API 函数,来在内核层下实现计算机的关机和重启等操作。 这种实现方式虽然在内核层下实现,但是相对来说,还是太过于表层了。
    本文要介绍一种在内核层下实现的强制关机以及重启的方法,即通过汇编 OUT 指令,控制端口输出,从而实现计算机的关机和重启。这种方式,较为低层,而且更难防御和监控。现在,我就把程序实现过程和原理,整理成文档,分享给大家。
    实现原理我们通过汇编 OUT 指令,控制端口的输出,从而实现控制计算机关机或者重启。
    对于实现关机操作,则可以向 1004H 端口,写入 2001H,汇编代码为:
    mov ax, 2001h mov dx, 1004h out dx,ax ret
    则,对应的机器码为:
    {0x66,0xB8,0x01,0x20,0x66,0xBA,0x04,0x10,0x66,0xEF,0xC3}
    对于实现重启操作,则可以向 64H 端口,写入 0FEH,汇编代码为:
    mov al, 0FEh out 64h, al ret
    则,对应的机器码为:
    {0xB0,0xFE,0xE6,0x64,0xC3}
    所以,我们可以通过申请一块非分页内存,写入 Shellcode 数据,并声明函数指针,调用 Shellcode 数据并执行,从而实现相应的关机或者重启操作。当然,我们也可以直接写成汇编代码,但是,要注意 64 位系统下汇编代码要保存为 .asm 汇编文件,添加到工程中。
    编码实现强制关机// 强制关机BOOLEAN ShutdownForce(){// {0x66, 0xB8, 0x01, 0x20, 0x66, 0xBA, 0x04, 0x10, 0x66, 0xEF, 0xC3} UCHAR ShellCode[11] = { 0x66, 0xB8, 0x01, 0x20, 0x66, 0xBA, 0x04, 0x10, 0x66, 0xEF, 0xC3 }; ULONG ulShellcodeSize = 11;#ifdef _WIN64 // 64 位 typedef VOID(__fastcall *typedef_SHUTDOWNFUNC)();#else // 32 位 typedef VOID(__stdcall *typedef_SHUTDOWNFUNC)();#endif // 申请内存 typedef_SHUTDOWNFUNC ShutdownFunc = (typedef_SHUTDOWNFUNC)ExAllocatePool(NonPagedPool, ulShellcodeSize); if (NULL == ShutdownFunc) { DbgPrint("ExAllocatePool Error!\n"); return FALSE; } // 写入数据 RtlCopyMemory(ShutdownFunc, ShellCode, ulShellcodeSize); // 执行Shellcode代码实现关机 ShutdownFunc(); // 释放内存 ExFreePool(ShutdownFunc); return TRUE;}
    强制重启// 强制重启BOOLEAN RebootForce(){ // {0xB0, 0xFE, 0xE6, 0x64, 0xC3} UCHAR ShellCode[11] = { 0xB0, 0xFE, 0xE6, 0x64, 0xC3 }; ULONG ulShellcodeSize = 5;#ifdef _WIN64 // 64 位 typedef VOID(__fastcall *typedef_REBOOTFUNC)();#else // 32 位 typedef VOID(__stdcall *typedef_REBOOTFUNC)();#endif // 申请内存 typedef_REBOOTFUNC RebootFunc = (typedef_REBOOTFUNC)ExAllocatePool(NonPagedPool, ulShellcodeSize); if (NULL == RebootFunc) { DbgPrint("ExAllocatePool Error!\n"); return FALSE; } // 写入数据 RtlCopyMemory(RebootFunc, ShellCode, ulShellcodeSize); // 执行Shellcode代码实现重启 RebootFunc(); // 释放内存 ExFreePool(RebootFunc); return TRUE;}
    程序测试在 Win7 32 位系统下,驱动程序正常运行;在 Win10 64 位系统下,驱动程序正常运行。
    总结对于这个层序,虽然编码较为简单,但是要想兼容 32 位和 64 位计算机,则需要特别注意一个问题:
    在声明函数指针的时候,对于 32 位函数,调用约定为 __stdcall;对于 64 位函数,调用约定为 __fastcall。
    所以,一定要注意不同系统位数下,函数的调用约定声明。否则,会出现错误。
    1  留言 2019-04-15 16:25:24
  • 两种方法编程实现锁屏注销关机操作

    背景我们对计算机锁屏、注销、关机通常都是点击鼠标去操作,但是,大家有没有试过编程去实现。
    或许对命令行熟悉的读者,会想到可以使用CMD命令去实现注销、关机、重启等操作。是的,这个可以实现,而且使用命令行实现,是本文讲解的一种实现方法之一。另一种方法,就是使用Win32 API函数 ExitWindowsEx 去实现注销、关机等操作,使用 LockWorkStation 函数实现锁屏操作。
    现在,本文就把实现过程和原理写成文档,分享给大家。
    函数声明LockWorkStation 函数
    可以锁定工作站保护其免受未经授权者使用。
    函数声明
    BOOL WINAPI LockWorkStation(void);
    参数

    无参数
    返回值

    如果函数成功,则返回值为非零值。由于该函数以异步方式执行,返回非零值指示操作已启动。它并不表示是否已成功锁定工作站。

    ExitWindowsEx 函数
    用来退出、重启或注销系统。
    函数声明
    BOOL ExitWindowsEx( UINT uFlags, // 关闭参数 DWORD dwReserved // 系统保留,一般取0)
    参数

    uFlags指定关闭的类型。此参数必须有下列值的组合:



    VALUE
    MEANING




    EWX_FORCE
    强制终止进程。当此标志设置,Windows不会发送消息WM_QUERYENDSESSION和WM_ENDSESSION的消息给目前在系统中运行的程序。这可能会导致应用程序丢失数据。因此,你应该只在紧急情况下使用此标志。


    EWX_LOGOFF
    关闭所有进程,然后注销用户。


    EWX_POWEROFF
    关闭系统并关闭电源。该系统必须支持断电。Windows NT中调用进程必须有 SE_SHUTDOWN_NAME 特权。


    EWX_REBOOT
    关闭系统,然后重新启动系统。Windows NT中:调用进程必须有SE_SHUTDOWN_NAME特权。


    EWX_SHUTDOWN
    关闭系统,安全地关闭电源。所有文件缓冲区已经刷新到磁盘上,所有正在运行的进程已经停止。Windows NT中:调用进程必须有SE_SHUTDOWN_NAME特权。




    dwReserved系统保留,这参数被忽略。一般取0。
    返回值

    如果函数成功,返回值为非零。如果函数失败,返回值是零。想获得更多错误信息,请调用GetLastError函数。

    实现原理我们可以使用命令行 shutdown 指令来实现注销、关机和重启等功能,但这个指令支持实现锁屏功能。
    Win32 API 函数 LockWorkStation 支持实现锁屏功能。
    Win32 API 函数 ExitWindowsEx 支持实现注销、关机、重启、关电源等功能。而且,实现这些操作需要有权限才行。
    所以,对于使用命令行方式,我们可以使用函数 system 去实现CMD命令。
    关机的CMD指令是:
    system("shutdown -p"); // 或者system("shutdown -s");
    注销的CMD命令是:
    system("shutdown -l");
    重启的CMD命令是:
    system("shutdown -r");
    那么对于使用API函数来实现的话,我们直接调用相应的API。
    锁屏实现的核心代码是:
    ::LockWorkStation();
    系统注销实现的核心代码是:
    ::ExitWindowsEx(EWX_LOGOFF, 0)
    系统关机实现的核心代码是:
    ::ExitWindowsEx(EWX_SHUTDOWN, 0)
    系统关机实现的核心代码是:
    ::ExitWindowsEx(EWX_REBOOT, 0)需要注意的是,如果使用 ExitWindowsEx 函数来实现关机和重启的操作,需要进程必须要有 SE_SHUTDOWN_NAME 权限,如果没有,则必须先提权,才可以成功使用此函数。因为进程提权的知识,超出了本文讲解的内容范畴,所以在本文就不详细讲解进程提权的操作了,只给出实现代码。
    编码实现进程提权到 SE_SHUTDOWN_NAME 权限// 进程提权BOOL EnableProcessPrivilege(){ HANDLE hToken; if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { ShowError("OpenProcessToken"); return FALSE; } TOKEN_PRIVILEGES tkp; // 修改进程权限 if (!::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid)) { ShowError("LookupPrivilegeValue"); return FALSE; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 通知系统修改进程权限 if (!::AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) { ShowError("AdjustTokenPrivileges"); return FALSE; } return TRUE;}
    CMD方式实现 注销// CMD方式实现 注销BOOL CMD_LogOff(){ system("shutdown -l"); return TRUE;}
    CMD方式实现 关机// CMD方式实现 关机BOOL CMD_Shutdown(){ system("shutdown -p"); return TRUE;}
    CMD方式实现 重启// CMD方式实现 重启BOOL CMD_Reboot(){ system("shutdown -r"); return TRUE;}
    API方式实现 锁屏// API方式实现 锁屏BOOL API_Lock(){ BOOL bRet = ::LockWorkStation(); return bRet;}
    API方式实现 注销// API方式实现 注销BOOL API_LogOff(){ BOOL bRet = ::ExitWindowsEx(EWX_LOGOFF, 0); return bRet;}
    API方式实现 关机// API方式实现 关机BOOL API_Shutdown(){ BOOL bRet = ::ExitWindowsEx(EWX_SHUTDOWN, 0); return bRet;}
    API方式实现 重启// API方式实现 重启BOOL API_Reboot(){ BOOL bRet = ::ExitWindowsEx(EWX_REBOOT, 0); return bRet;}
    总结需要注意的是,如果使用 ExitWindowsEx 函数来实现关机和重启的操作,需要进程必须要有 SE_SHUTDOWN_NAME 权限,如果没有,则必须先提权,才可以成功使用此函数。
    注意,此程序的功能实现不需要管理员权限就可以实现,上面提到的提升进程权限,并不是指获取管理员权限,而是指将修改进程令牌,实现进程权限的提升。而本文,进程提升到 SE_SHUTDOWN_NAME 权限不需要程序以管理员权限运行就可以成功提升。而对于其它的进程权限提升,例如进程提升到 SE_DEBUG_NAME 权限,则需要程序以管理员权限运行方可成功提权。
    参考参考自《Windows黑客编程技术详解》一书
    3  留言 2018-12-20 12:33:41

发送私信

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

14
文章数
15
评论数
eject