安全矩阵

 找回密码
 立即注册
搜索
查看: 722|回复: 0

拦截Windows关机消息

[复制链接]

252

主题

252

帖子

1309

积分

金牌会员

Rank: 6Rank: 6

积分
1309
发表于 2023-3-20 00:31:16 | 显示全部楼层 |阅读模式
原文链接:拦截Windows关机消息

写了一个小工具,可以通过Hook Winlogon进程主模块的导入表、延迟导入表来拦截对于User32!ExitWindowsEx函数的调用。
整个步骤如下:
1、启动一个进程,注入DLL到Winlogon进程。
(1) 因为winlogon进程是system级别进程,所以要让注入进程已管理员方式启动,并使能SeDebugPrivilege权限,这样才可以注入system进程。
(2) 通过进程枚举获取到winlogon进程的PID,这个过程还可以判断session会话,因为同一时刻,系统中可能存在多个winlogon进程。
(3) 打开此进程,可以使用PROCESS_ALL_ACCESS标志。
(4) 在目标进程winlogon进程中申请内存,并写入需要注入的DLL对应的路径名称,写入绝对路径名称。
(5) 通过远线程注入,启动函数为LoadLibraryA(W),传入参数为目标进程中需要注入DLL的路径地址。
2、在DLL的入口函数DllMain中HOOk 导入表,延迟导入表。
(1) 通过尝试,在windows 7 x86中,winlogon进程是在导入表中引用了User32模块的ExitWindowsEx函数。
(2) 而在 Windows 10 1809 x64中,winlogon进程是在延迟导入表中引用了User32模块的ExitWindowsEx函数。所以直接对这两个表都进行修复即可。
3、Hook函数指向自己写的函数,在自己写的函数中编写功能代码。
(1) 通过尝试,Winlogon进程会调用两次ExitWindowsEx函数,调用时使用的标志数据uFlags是不一样的,第一次调用ExitWindowsEx时会向桌面进程发送一些用户注销或者关机消息,用户进程可以在此时处理以便需要保存的内容。所有进程都处理完成后,第一次调用的ExitWindowsEx函数就返回TRUE了,否则返回FALSE。
(2) 如果第一次调用ExitWindowsEx成功,那么就说明桌面进程都同意关机或者注销了,会第二次调用ExitWindowsEx函数,需要意识到第二次调用前,桌面进程都被销毁了,此时如果要显示一个程序窗口,我们需要在桌面"WinSta0\winlogon"上进行显示,指定STARTUPINFOA(W)的lpDesktop指向此字符串。
备注:在windows 7上存在会话隔离,服务进程默认处于会话0(Session 0)下,所以窗口正常情况下都看不见,Windows启动后的第一个Winlogon进程为会话1,Winlogon下创建了工作站WinSta0,在此工作站下创建了桌面"WinSta0\winlogon",登录对话框就是显示在这个桌面上的,当用户成功登录后,显示的桌面为"WinSta0\Default",也就是说一般情况下我们看到的的程序都运行在WinSta0\Default桌面上,Winlogon进程还创建了桌面"WinSta0\Disconnect"。可以通过Process Explorer工具来查看某个进程中的句柄,可以看到类似的桌面都打开了WindowsStation对象,对应的名称为\Sessions\1\Windows\WindowStations\WinSta0 不同的会话对应的会话ID不同,所以WindowsStation对象对应的Name为\Sessions\XXX\Windows\WindowStations\WinSta0。SysinternalsSuite套件中提供了一个工具叫做Desktops.exe,这个工具除了当前默认的桌面外,还可以模拟出3个桌面,查看此进程可以看到共对应四个Desktop对象,分别为:【\Default】、【\Sysinternals Desktop 1】、【\Sysinternals Desktop 2】、【\Sysinternals Desktop 3】,通过Alt 1、2、3、4即可在四个桌面之间快速进行切换。
通过上面的知识,我们可以选择在Winlogon进程第二次调用ExitWindowsEx前,进行拦截,可以弹窗提示告知用户即将关机,或者显示其它消息。因为第一次ExitWindowsEx返回TRUE才会进行第二次调用,表明用户进程都已知晓,如果有未保存文件,此时用户都已知晓并处理过了。
当第一次调用ExitWindowsEx成功后,此时Windows桌面已经进入到了上面所说的WinSta0\winlogon下,所以调用CreateProcessAsUserA(W)时需要指定对应的桌面。
4、需要考虑到我们的DLL是运行在system进程中的,而提示时需要启动一个新的进程,最好不要让这个进程拥有过高权限,所以我们使用CreateProcessAsUserA(W)来创建进程。
通过尝试,在第二次调用ExitWindowsEx前,我们先打开当前进程,因为之后需要复制token,再修改token进程等级。但是在打开当前进程时程序执行失败,所以我们选择在DllMain函数中创建好一个Token进行保存,在调用CreateProcessAsUserA(W)使用这个token就可以了。注入进程代码如下:

  1. #include <Windows.h>
  2. #include <iostream>
  3. #include <Psapi.h>
  4. #include <Tlhelp32.h>
  5. #include <sddl.h>
  6. #include <Shlwapi.h>

  7. using namespace std;
  8. #pragma comment (lib,"advapi32.lib")
  9. #pragma comment (lib,"Shlwapi.lib")

  10. VOID InjectToWinLogon()
  11. {   
  12.     PROCESSENTRY32 entry;
  13.     HANDLE snapshot = NULL, proc = NULL;
  14.     entry.dwSize = sizeof(PROCESSENTRY32);
  15.     snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

  16.     INT pid = -1;
  17.     if (Process32First(snapshot, &entry))
  18.     {
  19.         while (Process32Next(snapshot, &entry))
  20.         {
  21.             if (wcscmp(entry.szExeFile, L"winlogon.exe") == 0)
  22.             {               
  23.                 pid = entry.th32ProcessID;
  24.                 break;
  25.             }
  26.         }
  27.     }
  28.     CloseHandle(snapshot);
  29.     if (pid < 0)
  30.     {
  31.         //puts("[-] Could not find winlogon.exe");
  32.         return;
  33.     }  
  34.     proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
  35.     if (proc == NULL)
  36.     {
  37.         DWORD error = GetLastError();
  38.         puts("[-] Failed to open process.");
  39.         printf("error %d\n", error);
  40.         return;
  41.     }
  42.     TCHAR buffDll[MAX_PATH] = { 0 };
  43.     GetModuleFileName(NULL, buffDll, _countof(buffDll));
  44.     PathRemoveFileSpec(buffDll);
  45.     _tcscat_s(buffDll, _countof(buffDll), L"\\DllHookExitWindowsEx.dll");   
  46.     LPVOID buffer = VirtualAllocEx(proc, NULL, sizeof(buffDll), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  47.     if (buffer == NULL)
  48.     {
  49.         printf("[-] Failed to allocate remote memory");
  50.     }
  51.     if (!WriteProcessMemory(proc, buffer, buffDll, sizeof(buffDll), 0))
  52.     {
  53.         puts("[-] Failed to write to remote memory");
  54.         return;
  55.     }      
  56.     LPTHREAD_START_ROUTINE start = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryW");
  57.     HANDLE hthread = CreateRemoteThread(proc, 0, 0, (LPTHREAD_START_ROUTINE)start, buffer, 0, 0);   

  58.     DWORD error = GetLastError();
  59.     if (hthread == INVALID_HANDLE_VALUE)
  60.     {
  61.         puts("[-] Failed to create remote thread");
  62.         return;
  63.     }
  64. }

  65. void EnableSeDebugPrivilegePrivilege()
  66. {
  67.     LUID luid;
  68.     HANDLE currentProc = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
  69.     if (currentProc)
  70.     {
  71.         HANDLE TokenHandle = NULL;
  72.         BOOL hProcessToken = OpenProcessToken(currentProc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle);
  73.         if (hProcessToken)
  74.         {
  75.             BOOL checkToken = LookupPrivilegeValue(NULL, L"SeDebugPrivilege", &luid);

  76.             if (!checkToken)
  77.             {
  78.                 //std::cout << "[+] Current process token already includes SeDebugPrivilege\n" << std::endl;
  79.             }
  80.             else
  81.             {
  82.                 TOKEN_PRIVILEGES tokenPrivs;

  83.                 tokenPrivs.PrivilegeCount = 1;
  84.                 tokenPrivs.Privileges[0].Luid = luid;
  85.                 tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  86.                 BOOL adjustToken = AdjustTokenPrivileges(TokenHandle, FALSE, &tokenPrivs, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL);

  87.                 if (adjustToken != 0)
  88.                 {
  89.                     //std::cout << "[+] Added SeDebugPrivilege to the current process token" << std::endl;
  90.                 }
  91.             }
  92.             CloseHandle(TokenHandle);
  93.         }
  94.     }
  95.     CloseHandle(currentProc);
  96. }

  97. int _tmain(int argc, _TCHAR* argv[])
  98. {               
  99.     //开启权限 使之可以注入到syetem进程
  100.     EnableSeDebugPrivilegePrivilege();
  101.     //注入dll
  102.     InjectToWinLogon();

  103.     getchar();
  104.     return 0;
  105. }
复制代码
被注入的DLL代码如下:
  1. #include <Windows.h>
  2. #include <Psapi.h>
  3. #include <Tlhelp32.h>

  4. #include "warningUser.h"

  5. #include <Shlwapi.h>
  6. #include <stdlib.h>
  7. #include <tchar.h>

  8. #pragma comment (lib,"Shlwapi.lib")

  9. LPVOID _copyNtShutdownSystem = NULL;
  10. LPVOID _ExitWindowsExAddTwoByte = NULL;
  11. HMODULE _gloDllModule = NULL;


  12. #pragma warning(disable:4996)

  13. /*__declspec(naked)*/ void MyExitWindowsEx()
  14. {
  15.     /*__asm
  16.     {
  17.         call testMsgBox;
  18.         jmp _ExitWindowsExAddTwoByte
  19.     }*/
  20. }

  21. typedef BOOL(WINAPI* FuncExitWindowsEx)(_In_ UINT uFlags, _In_ DWORD dwReason);
  22. FuncExitWindowsEx _OldExitWindowsEx = NULL;


  23. HANDLE gloCreateProcessHandle = NULL;

  24. BOOL WINAPI IATHookExitWindowsEx(_In_ UINT uFlags, _In_ DWORD dwReason)
  25. {   
  26.     BOOL bRet = FALSE;
  27.     static BOOL bNeedWarning = FALSE;
  28.     //__asm int 3
  29.     //DebugBreak();

  30.     /*if (uFlags & 0x200000)//win7 x86可以通过这句来判断是否是第二次调用 通过调试获得的   需要测试
  31.     {      
  32.     }*/
  33.     if (bNeedWarning)
  34.     {
  35.         TCHAR wszProcessName[MAX_PATH] = { 0 };
  36.         GetModuleFileName(_gloDllModule, wszProcessName, _countof(wszProcessName));
  37.         PathRemoveFileSpec(wszProcessName);
  38.         _tcscat_s(wszProcessName, _countof(wszProcessName), L"\\LogOffWillRun.exe");
  39.         useTokenCreateProcess(gloCreateProcessHandle, wszProcessName);
  40.     }

  41.     bRet = _OldExitWindowsEx(uFlags, dwReason);
  42.     if (bRet)
  43.     {      
  44.         bNeedWarning = TRUE;      
  45.     }   
  46.     return bRet;   
  47. }

  48. //这是 win7 x86上的 Iniline Hook
  49. void hook_ExitWindowsEx()
  50. {   
  51.     HMODULE hUser32 = GetModuleHandle(L"user32.dll");
  52.     char* pOldExitWindowsEx = (char*)GetProcAddress(hUser32, "ExitWindowsEx");
  53.     char* pOldAddr = pOldExitWindowsEx;   

  54.     //00540000 8bff            mov     edi, edi   
  55.     int iLengthCopy = 7;
  56.     if (NULL != pOldAddr)
  57.     {
  58.         _copyNtShutdownSystem = VirtualAlloc(0, 1024, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  59.         char* pNewAddr = (char*)_copyNtShutdownSystem;

  60.         char* pnop = pOldAddr - 5; //有5个字节的NOP
  61.         char aa = *pOldAddr;
  62.         char bb = *(pOldAddr+1);
  63.         if ((char)0x8b == *pOldAddr && (char)0xff == *(pOldAddr+1))
  64.         {
  65.             DWORD oldshutdownProtect = 0;
  66.             if (VirtualProtect(pOldAddr-5, iLengthCopy, PAGE_EXECUTE_READWRITE, &oldshutdownProtect))
  67.             {
  68.                 //*pOldNtShutdownSyetem = (char)0xe9;//jmp
  69.                 *pOldExitWindowsEx = (char)0xeB;//jmp 短跳转
  70.                 *(UCHAR*)(pOldExitWindowsEx + 1) = (USHORT)(-0x7); //addr

  71.                 *pnop = (char)0xe9;//jmp
  72.                 *(int*)(pnop + 1) = (int)MyExitWindowsEx-(int)(pnop + 5); //addr
  73.                 _ExitWindowsExAddTwoByte = pOldExitWindowsEx + 2;
  74.                 VirtualProtect(pOldAddr-5, iLengthCopy, oldshutdownProtect, NULL);
  75.             }
  76.         }      
  77.     }
  78.     return;
  79. }

  80. BYTE* getNtHdrs(BYTE* pe_buffer)
  81. {
  82.     if (pe_buffer == NULL) return NULL;

  83.     IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)pe_buffer;
  84.     if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
  85.         return NULL;
  86.     }
  87.     const LONG kMaxOffset = 1024;
  88.     LONG pe_offset = idh->e_lfanew;
  89.     if (pe_offset > kMaxOffset) return NULL;
  90.     IMAGE_NT_HEADERS32* inh = (IMAGE_NT_HEADERS32*)((BYTE*)pe_buffer + pe_offset);
  91.     if (inh->Signature != IMAGE_NT_SIGNATURE) return NULL;
  92.     return (BYTE*)inh;
  93. }

  94. IMAGE_DATA_DIRECTORY* getPeDir(PVOID pe_buffer, size_t dir_id)
  95. {
  96.     if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL;

  97.     BYTE* nt_headers = getNtHdrs((BYTE*)pe_buffer);
  98.     if (nt_headers == NULL) return NULL;

  99.     IMAGE_DATA_DIRECTORY* peDir = NULL;

  100.     IMAGE_NT_HEADERS* nt_header = (IMAGE_NT_HEADERS*)nt_headers;
  101.     peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]);

  102.     if (peDir->VirtualAddress == NULL) {
  103.         return NULL;
  104.     }
  105.     return peDir;

  106. }

  107. bool FixDelayIATHook(PVOID modulePtr)
  108. {   
  109.     IMAGE_DATA_DIRECTORY* importsDir = getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
  110.     if (importsDir == NULL) return false;

  111.     size_t maxSize = importsDir->Size;
  112.     size_t impAddr = importsDir->VirtualAddress;

  113.     IMAGE_DELAYLOAD_DESCRIPTOR* lib_desc = NULL;
  114.     size_t parsedSize = 0;
  115.     bool bFound = TRUE;

  116.     size_t addrExitWindowsEx = (size_t)GetProcAddress(GetModuleHandle(L"User32"), "ExitWindowsEx");   

  117.     for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_DELAYLOAD_DESCRIPTOR)) {

  118.         lib_desc = (IMAGE_DELAYLOAD_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);

  119.         if (lib_desc->ImportAddressTableRVA == NULL && lib_desc->ImportNameTableRVA == NULL) break;

  120.         LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->DllNameRVA);      

  121.         size_t call_via = lib_desc->ImportAddressTableRVA;
  122.         size_t thunk_addr = lib_desc->ImportNameTableRVA;
  123.         if (thunk_addr == NULL) thunk_addr = lib_desc->ImportAddressTableRVA;

  124.         size_t offsetField = 0;
  125.         size_t offsetThunk = 0;      
  126.         for (;; offsetField += sizeof(IMAGE_THUNK_DATA), offsetThunk += sizeof(IMAGE_THUNK_DATA))
  127.         {
  128.             IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
  129.             IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);

  130.             if (0 == fieldThunk->u1.Function && 0 == orginThunk->u1.Function)
  131.             {
  132.                 break;
  133.             }           
  134.             PIMAGE_IMPORT_BY_NAME by_name = NULL;
  135.             LPSTR func_name = NULL;
  136.             size_t addrOld = NULL;
  137.             if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
  138.             {
  139.                 addrOld = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));//通过序号也可以获取到  获取低两个字节 也可以获取到函数地址
  140.                 //printf("        [V] API %x at %x\n", orginThunk->u1.Ordinal, addr);
  141.                 //fieldThunk->u1.Function = addr;               
  142.                 continue;
  143.             }
  144.             else
  145.             {
  146.                 by_name = (PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr) + orginThunk->u1.AddressOfData);
  147.                 func_name = (LPSTR)by_name->Name;
  148.                 addrOld = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);
  149.             }                       
  150.             //printf("        [V] API %s at %x\n", func_name, addr);
  151.             OutputDebugStringA("\r\n");
  152.             OutputDebugStringA(func_name);
  153.             //HOOK           
  154.             if (strcmpi(func_name, "ExitWindowsEx") == 0)
  155.             {               
  156.                 //DebugBreak();
  157.                 DWORD dOldProtect = 0;
  158.                 size_t* pFuncAddr = (size_t*)&fieldThunk->u1.Function;
  159.                 if (VirtualProtect(pFuncAddr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dOldProtect))
  160.                 {
  161.                     fieldThunk->u1.Function = (size_t)IATHookExitWindowsEx;
  162.                     VirtualProtect(pFuncAddr, sizeof(size_t), dOldProtect, &dOldProtect);
  163.                     _OldExitWindowsEx = (FuncExitWindowsEx)addrExitWindowsEx;
  164.                     bFound = true;
  165.                     return bFound;
  166.                 }               
  167.                 break;
  168.             }

  169.         }

  170.     }   
  171.     return true;
  172. }

  173. bool FixIATHook(PVOID modulePtr)
  174. {
  175.     IMAGE_DATA_DIRECTORY* importsDir = getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
  176.     if (importsDir == NULL) return false;

  177.     size_t maxSize = importsDir->Size;
  178.     size_t impAddr = importsDir->VirtualAddress;

  179.     IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
  180.     size_t parsedSize = 0;
  181.     bool bFound = TRUE;   
  182.     size_t addrExitWindowsEx = (size_t)GetProcAddress(GetModuleHandle(L"User32"), "ExitWindowsEx");

  183.     for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR)) {
  184.         lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);
  185.         if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL)
  186.             break;

  187.         LPSTR lib_name = (LPSTR)((size_t)modulePtr + lib_desc->Name);

  188.         size_t call_via = lib_desc->FirstThunk;
  189.         size_t thunk_addr = lib_desc->OriginalFirstThunk;
  190.         if (thunk_addr == NULL)
  191.             thunk_addr = lib_desc->FirstThunk;

  192.         size_t offsetField = 0;
  193.         size_t offsetThunk = 0;

  194.         for (;; offsetField += sizeof(IMAGE_THUNK_DATA), offsetThunk += sizeof(IMAGE_THUNK_DATA))
  195.         {
  196.             IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetField + call_via);
  197.             IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)(size_t(modulePtr) + offsetThunk + thunk_addr);

  198.             if (0 == fieldThunk->u1.Function && 0 == orginThunk->u1.Function)
  199.             {
  200.                 break;
  201.             }

  202.             PIMAGE_IMPORT_BY_NAME by_name = NULL;
  203.             LPSTR func_name = NULL;
  204.             size_t addrOld = NULL;
  205.             if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64)
  206.             {
  207.                 addrOld = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF));//通过序号?
  208.                 //printf("        [V] API %x at %x\n", orginThunk->u1.Ordinal, addr);
  209.                 //fieldThunk->u1.Function = addr;   
  210.                 //DebugBreak();
  211.                 continue;
  212.             }
  213.             else
  214.             {
  215.                 by_name = (PIMAGE_IMPORT_BY_NAME)(size_t(modulePtr) + orginThunk->u1.AddressOfData);
  216.                 func_name = (LPSTR)by_name->Name;
  217.                 addrOld = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name);
  218.             }
  219.             //printf("        [V] API %s at %x\n", func_name, addr);
  220.             OutputDebugStringA("\r\n");
  221.             OutputDebugStringA(func_name);
  222.             //HOOK           
  223.             if (strcmpi(func_name, "ExitWindowsEx") == 0)
  224.             {               
  225.                 //DebugBreak();                                               
  226.                 DWORD dOldProtect = 0;
  227.                 size_t* pFuncAddr = (size_t*)&fieldThunk->u1.Function;
  228.                 if (VirtualProtect(pFuncAddr, sizeof(size_t), PAGE_EXECUTE_READWRITE, &dOldProtect))
  229.                 {
  230.                     fieldThunk->u1.Function = (size_t)IATHookExitWindowsEx;
  231.                     VirtualProtect(pFuncAddr, sizeof(size_t), dOldProtect, &dOldProtect);
  232.                     _OldExitWindowsEx = (FuncExitWindowsEx)addrExitWindowsEx;
  233.                     bFound = true;
  234.                     return bFound;
  235.                 }               
  236.             }
  237.         }               
  238.     }
  239.     return true;   
  240. }

  241. BOOL APIENTRY DllMain( HMODULE hModule,
  242.                        DWORD  ul_reason_for_call,
  243.                        LPVOID lpReserved
  244.                      )
  245. {

  246.     switch (ul_reason_for_call)
  247.     {
  248.     case DLL_PROCESS_ATTACH:
  249.     {      
  250.         //DebugBreak();
  251.         _gloDllModule = hModule;
  252.         gloCreateProcessHandle = getMediumProcessToken();      
  253.         HMODULE exeModule = GetModuleHandle(NULL);      
  254.         FixIATHook(exeModule);
  255.         FixDelayIATHook(exeModule);               
  256.         break;
  257.     }      
  258.     case DLL_THREAD_ATTACH:
  259.     case DLL_THREAD_DETACH:
  260.     case DLL_PROCESS_DETACH:
  261.     {
  262.         if (gloCreateProcessHandle != NULL)
  263.         {
  264.             CloseHandle(gloCreateProcessHandle);
  265.             gloCreateProcessHandle = NULL;
  266.         }
  267.     }
  268.         break;
  269.     }
  270.     return TRUE;
  271. }
复制代码
其中引用了warningUser.h文件内容如下:


  1. HANDLE getMediumProcessToken();
  2. void useTokenCreateProcess(HANDLE hToken, TCHAR* szProcessName);
复制代码
源文件warningUser.cpp文件内容为:

  1. #include "warningUser.h"
  2. #include <Windows.h>
  3. #include <sddl.h>
  4. #include <tchar.h>
  5. #include <Shlwapi.h>
  6. #include <stdlib.h>

  7. #pragma comment(lib, "Advapi32.lib")
  8. #pragma comment (lib,"Shlwapi.lib")

  9. HANDLE getMediumProcessToken()
  10. {
  11.     WCHAR* wszIntegritySid = L"S-1-16-8192";
  12.     //CreateIntegritySidProcess(L"S-1-16-4096");//low权限进程
  13.     //CreateIntegritySidProcess(L"S-1-16-8192");//medium权限进程
  14.     //CreateIntegritySidProcess(L"S-1-16-12288");//high权限进程
  15.     //CreateIntegritySidProcess(L"S-1-16-16384");//system权限进程
  16.     HANDLE mediumToken = NULL;   
  17.     HANDLE hToken = NULL;
  18.     HANDLE hNewToken = NULL;

  19.     PSID pIntegritySid = NULL;
  20.     TOKEN_MANDATORY_LABEL TIL = { 0 };   
  21.     __try
  22.     {
  23.         if (FALSE == OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hToken))
  24.         {
  25.             __leave;
  26.         }
  27.         if (FALSE == DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
  28.             SecurityImpersonation, TokenPrimary, &hNewToken))
  29.         {
  30.             __leave;
  31.         }
  32.         if (FALSE == ConvertStringSidToSid(wszIntegritySid, &pIntegritySid))
  33.         {
  34.             __leave;
  35.         }
  36.         TIL.Label.Attributes = SE_GROUP_INTEGRITY;
  37.         TIL.Label.Sid = pIntegritySid;

  38.         // Set the process integrity level
  39.         if (FALSE == SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL,
  40.             sizeof(TOKEN_MANDATORY_LABEL)+GetLengthSid(pIntegritySid)))
  41.         {
  42.             __leave;
  43.         }
  44.         mediumToken = hNewToken;
  45.     }
  46.     __finally
  47.     {
  48.         if (NULL != pIntegritySid)
  49.         {
  50.             LocalFree(pIntegritySid);
  51.             pIntegritySid = NULL;
  52.         }
  53.         if (NULL != hToken)
  54.         {
  55.             CloseHandle(hToken);
  56.             hToken = NULL;
  57.         }

  58.     }
  59.     return mediumToken;
  60. }

  61. void useTokenCreateProcess(HANDLE hToken, TCHAR* szProcessName)
  62. {
  63.     //LogOffWillRun

  64.     //WCHAR wszProcessName[MAX_PATH] = L"C:\\Windows\\System32\\CMD.exe";
  65.     PROCESS_INFORMATION ProcInfo = { 0 };
  66.     STARTUPINFO StartupInfo = { 0 };
  67.     StartupInfo.cb = sizeof(STARTUPINFO);
  68.     //si.dwXSize = 120;
  69.     //StartupInfo.lpDesktop = L"WinSta0\\Default";
  70.     StartupInfo.lpDesktop = L"WinSta0\\winlogon";
  71.     StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  72.     StartupInfo.wShowWindow = SW_SHOWNORMAL;
  73.     BOOL bRet = CreateProcessAsUser(hToken, NULL,
  74.         szProcessName, NULL, NULL, FALSE,
  75.         0, NULL, NULL, &StartupInfo, &ProcInfo);
  76.     if (bRet)
  77.     {
  78.         WaitForSingleObject(ProcInfo.hProcess, INFINITE);
  79.     }
  80.     if (ProcInfo.hProcess)
  81.     {
  82.         CloseHandle(ProcInfo.hProcess);
  83.         ProcInfo.hProcess = NULL;
  84.     }
  85.     if (ProcInfo.hThread)
  86.     {
  87.         CloseHandle(ProcInfo.hThread);
  88.         ProcInfo.hThread = NULL;
  89.     }
  90. }
复制代码
在提示进程LogOffWillRun中,代码很简单,进行提示,如下:

  1. #include <Windows.h>

  2. #pragma comment(lib, "User32.lib")
  3. int _tmain(int argc, _TCHAR* argv[])
  4. {
  5.     MessageBox(GetConsoleWindow(), _TEXT("调用了ExitWindowsEx进行关机或者注销"), _TEXT("提示"), MB_OK);
  6.     return 0;
  7. }
复制代码

代码基本到此结束,运行注入DLL的进程时需要以管理员权限运行。
winlogon调用两次ExitWindowsEx后,wininit进程会去调用ntdll!NtShutDownSystem进程进行关机。在win 10 1809 x64上,发现wininit会进行第三次调用ExitWindowsEx函数。本来想远线程注入wininit进程,但是失败,错误代码为8,含义为内存资源不足,无法处理此命令。
还有一个想法是Hook wininit进程,具体如下:
1、打开wininit进程。
2、远程修改导入表,Hook NtShutDownSystem。
(1) 可以通过在本进程中调用LoadLibraryExA(W)展开wininit的PE文件,定位到NtShutDownSystem函数对应的地址,通过此地址与内存中的wininit映像计算一个偏移。
(2)枚举wininit进程模块,获取主进程基地址,然后通过上一步的偏移量来修改导入表或者延迟导入表。
(3)修改地址为一段shellcode的起始地址,并保留原来wininit中导入表的内容。
a、远程申请一段空间,可读可写可执行,这段空间包括两部分内容,第一部分是数据,第二部分是代码。
b、数据是原来导入表的内容,也可以写入其它数据。
c、代码可以使用PIC_Bindshell项目,然后自己编写C语言函数,编写一段shellcode,引用之前的数据(可以通过call pop重定位或者生成shellcode后,通过每次修改shellcode硬编码来引用数据)。
3、修改导入表后,Hook函数指针指向我们自己的shellcode(如果数据与代码在连续内存,记得加上偏移,使指针指向shellcode起始地址),当wininit调用NtShutDownSystem时会先调用我们自己的函数,使用PIC_Bindshell编写代码可以加载我们自己的动态库,调用动态库导出函数来启动一个进程提示用户关机了。
备注:winlogon进程调用两次ExitWindowsEx后程序就退出了,如果我们选择注销当前用户,当前winlogon结束,会启动新的winlogon,此时没有我们注入的动态库,即可以将注入器写成服务进程,后台枚举winlogon进程,注入DLL。或者HOOk Wininit进程。也可以在桌面进程中监听窗口消息WM_QUERYENDSESSION。或者在服务进程中通过RegisterServiceCtrlHandlerA(W)注册一个回调函数,在回调函数中处理SERVICE_CONTROL_SHUTDOWN请求。
2022-12-28添加:在Hook的ExitWindowsEx函数中也可以通过MessageBox来提示用户,如下:



  1. if (bNeedWarning)
  2. {
  3.     MessageBox(NULL, _TEXT("弹框提示"), _TEXT("提示"), MB_OK);
  4. }
复制代码
效果如下:


如果是通过启动一个新进程的方式,优点是更加灵活,自己编写新进程,不需要修改DLL。






回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 21:52 , Processed in 0.022328 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表