使用Windows API绕过进程保护

VSole2021-11-18 07:19:54

前言

最近在研究某数字杀软的时候看到有个配置选项:

img

这个自我保护实际上是加载360SelfProtection.sys驱动(看这名字应该还有360SelfProtection_win10.sys文件),在0环通过hook等手段保护注册表项,重要进程进程等。

img

比如这里要结束某核心进程,会显示无法结束,拒绝访问。

img

img

这个并不是说权限不够的问题,即便是system权限也不行。而是由于在底层,杀死进程的API已经被hook了,这里应该是内核hook,当想要结束的进程为核心进程时,就直接返回一个无法终止进程的弹窗。本文就如何实现一个进程保护功能进行探究,驱动就不写了,就写一个用户层的。

实现原理

windows提供了一个可以杀死其他进程的API:TerminateProcess。如果是结束本身进程为:ExitProcess。

BOOL TerminateProcess(
[in] HANDLE hProcess,
[in] UINT   uExitCode
);

通过命令taskkill或者通过任务管理器等GUI工具去结束进程,本质上都是调用的TerminateProcess,有些可能是调用更为底层的ZwTerminateProcess,但是本质都差不多,只是看该进程用的什么API,这里为了方便就使用TerminateProcess进行演示。

通过hook TerminateProcess让执行该函数时直接返回没有权限。

获取API地址并创建新的hook函数

static BOOL(WINAPI* OldTerminateProcess)(
    HANDLE hProcess,
    UINT   uExitCode) = TerminateProcess;
BOOL WINAPI New_TerminateProcess(
_In_ HANDLE hProcess,
_In_ UINT uExitCode
) {
    unhookTerminateProcess();
MessageBox(NULL,L"该进程受保护!", L"Access Denied",NULL);
    hookTerminateProcess();
returnfalse;
}

这里是64位的进程,要改变的硬编码是12个字节,而且不需要去算偏移。

48 b8 _dwNewAddress(0x1122334455667788)
ff e0
mov rax, _dwNewAddress(0x1122334455667788)
jmp rax

将原来函数的调转地址改为我们自己函数的跳转地址

void hookTerminateProcess() {
    DWORD dwTerminateProcessOldProtect;
    BYTE pData[12] = { 0x48,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xE0};
    ULONGLONG ullNewFuncAddr = (ULONGLONG)New_TerminateProcess;
//保存本来的字节
::RtlCopyMemory(g_OldTerminateProcess, OldTerminateProcess, sizeof(pData));
//更改权限
VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwTerminateProcessOldProtect);
//更改原来的机器码,改为我们自己函数的地址
::RtlCopyMemory(&pData[2], &ullNewFuncAddr, sizeof(ullNewFuncAddr));
::RtlCopyMemory(OldTerminateProcess, pData, sizeof(pData));
//恢复属性
VirtualProtect(OldTerminateProcess, 12, dwTerminateProcessOldProtect, &dwTerminateProcessOldProtect);
}

恢复硬编码,还原调转地址。

void unhookTerminateProcess() {
    DWORD dwOldProtect = NULL;
//改为可写属性
VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//还原原来的硬编码
RtlCopyMemory(OldTerminateProcess, g_OldTerminateProcess, sizeof(g_OldTerminateProcess));
//还原属性
VirtualProtect(OldTerminateProcess, 12, dwOldProtect, &dwOldProtect);
}

最方便的就是写成一个dll,这样注入到任务管理器并起一个线程跑就行了。

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
        hookTerminateProcess();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
        unhookTerminateProcess();
break;
}
return TRUE;
}

注入这里也是为了练手,稍微写了一个注入程序,为了以后的方法写成了突破session 0的注入。

#include
#include
#include"tchar.h"
#include
usingnamespace std;
BOOL EnbalePrivileges(HANDLE hProcess, LPCWSTR pszPrivilegesName)
{
    HANDLE hToken = NULL;
    LUID luidValue = { 0};
    TOKEN_PRIVILEGES tokenPrivileges = { 0};
    BOOL bRet = FALSE;
    DWORD dwRet = 0;
// 打开进程令牌并获取进程令牌句柄
    bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
if(FALSE == bRet)
{
        printf("[!] Open CurrentProcessToken Error,Error is:%d",GetLastError());
return FALSE;
}
else
{
        printf("[*] Open CurrentProcessToken Successfully!,TokenHandle is:%d", hToken);
}
// 获取本地系统的 pszPrivilegesName 特权的LUID值
    bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
if(FALSE == bRet)
{
        printf("[!] LookupPrivilegeValue Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
        printf("[*] LookupPrivilegeValue Successfully!");
}
// 设置提升权限信息
    tokenPrivileges.PrivilegeCount= 1;
    tokenPrivileges.Privileges[0].Luid= luidValue;
    tokenPrivileges.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;
// 提升进程令牌访问权限
    bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
if(FALSE == bRet)
{
        printf("[!] AdjustTokenPrivileges Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
// 根据错误码判断是否特权都设置成功
        dwRet = ::GetLastError();
if(ERROR_SUCCESS == dwRet)
{
            printf("[√] ALL_ASSIGNED!");
return TRUE;
}
elseif(ERROR_NOT_ALL_ASSIGNED == dwRet)
{
            printf("[!] ERROR:NOT_ALL_ASSIGNED,Error is %d", dwRet);
return FALSE;
}
}
return FALSE;
}
DWORD EnumModules(DWORD hPid, LPCSTR hMoudlePath)
{
    WCHAR szBuffer[MAX_PATH] = { 0};
    mbstowcs(szBuffer, hMoudlePath, MAX_PATH);
//通过pid列出所有的Modules
    HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32    me32;
//给进程所引用的模块信息设定一个快照
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, hPid);
if(hModuleSnap == INVALID_HANDLE_VALUE)
{
        printf("[!] Error:Enum modules failed to detect if there is an injected DLL module,error is %d",GetLastError());
}
    me32.dwSize = sizeof(MODULEENTRY32);
if(!Module32First(hModuleSnap, &me32))
{
        printf("[!] Enum Error!");
CloseHandle(hModuleSnap);
}
do
{
if(!memcmp(me32.szExePath, szBuffer,MAX_PATH))
return1;           
} while(Module32Next(hModuleSnap, &me32));
CloseHandle(hModuleSnap);
return0;
}
DWORD _InjectThread(DWORD _Pid, LPCSTR psDllPath)
{
    FILE* fp;
    fp = fopen(psDllPath, "r");
if(!fp)
{
        printf("[!] Error:DLL path not foundPlease check that your path is correct or absolute");
return FALSE;
}
    fclose(fp);
    printf("****************************************************************************");
    HANDLE hprocess = NULL;
    HANDLE hThread = NULL;
    DWORD _SIZE = 0;
    LPVOID pAlloc = NULL;
    FARPROC pThreadFunction = NULL;
    DWORD ZwRet= 0;
    hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
if(hprocess == NULL)
{
printf("[!] OpenProcess Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] OpenProcess Successfully!");
}
_SIZE = strlen(psDllPath)+1;
pAlloc = ::VirtualAllocEx(hprocess, NULL, _SIZE, MEM_COMMIT, PAGE_READWRITE);
if(pAlloc == NULL)
{
printf("[!] VirtualAllocEx Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] VirtualAllocEx Successfully!");
}
BOOL x = ::WriteProcessMemory(hprocess, pAlloc, psDllPath, _SIZE, NULL);
if(FALSE == x)
{
printf("[!] WriteMemory Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] WriteMemory Successfully!");
}
HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
if(hNtdll == NULL)
{
printf("[!] LoadNTdll Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] Load ntdll.dll Successfully!");
}
pThreadFunction = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
if(pThreadFunction == NULL)
{
printf("[!] Get LoadLibraryA Address Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] Get LoadLibraryA Address Successfully! Address is %x", pThreadFunction);
}
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown
);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown
);
#endif
typedef_ZwCreateThreadEx ZwCreateThreadEx= NULL;
ZwCreateThreadEx= (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
if(ZwCreateThreadEx== NULL)
{
printf("[!] Get ZwCreateThreadEx Address Error,Error is:%d", GetLastError());
return FALSE;
}
else
{
printf("[*] Get ZwCreateThreadEx Address Successfully! Address is %x", ZwCreateThreadEx);
}
HANDLE hRemoteThread;
ZwRet= ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hprocess,
(LPTHREAD_START_ROUTINE)pThreadFunction, pAlloc, 0, 0, 0, 0, NULL);
if(hRemoteThread == NULL)
{
printf("[!] Creat RemoteThread Error,Error is:%d", GetLastError());
CloseHandle(hprocess);
return FALSE;
}
printf("[*] Please wait for a moment in the process of injection:");
for(int m = 0;m<5;m++)
{
if(EnumModules(_Pid, psDllPath))
{
printf("[√] Creat RemoteThread Successfully! RemoteThread Id is %x", hRemoteThread);
VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hprocess);
FreeLibrary(hNtdll);
return TRUE;
}
Sleep(2000);
}
printf("[!] DLL injection failed!Notice:Please check that your path is absolute or correct");
VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
CloseHandle(hRemoteThread);
CloseHandle(hprocess);
FreeLibrary(hNtdll);
return FALSE;
}
int main(int argc, char* argv[])
{
if(argc == 3)
{
EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);
DWORD dwPid;
sscanf(argv[1],"%d", &dwPid);
_InjectThread(dwPid, argv[2]);
return1;
}
else
{
printf("[!] You passed in the wrong number of parameters!Please pass in two parameters.");
printf("[!] Notice:[!] The first parameter is the PID of the target process[!] The second parameter is the location of the injected DLL,Please enter the absolute path!");
return0;
}
}

直接注入,看到线程创建成功就表示已经成功了。

img

用procexp也可以看一眼。

img

比如现在我们想要结束QQ进程。

img

会发现无法结束。

img

这里由于还没有写判断规则,这个任务管理器目前无法结束任何进程,也就是所有进程都无法结束,这显然用户体验是不好的。

那么如果要选择性保护进程,又应该怎么做呢。注意TerminateProcess的第一个参数,传入的是一个句柄,这个句柄需要从openprocess的返回值获得,所以我们还需要知道打开进程的句柄。

同一进程多次使用openprocess获取的句柄是不一样的。

inline hook的稳定性还是差了点,很容易让进程崩溃。根据实验:任务管理器会不断地调用openprocess这个api,不管有没有操作都会一直调用。这里就用微软更加稳定的detours库。

voidHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
// 参数一是原函数地址,参数二是新函数地址
DetourAttach((PVOID*)&OldOpenProcess, New_OpenProcess);
DetourAttach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
DetourTransactionCommit();
}
voidUnHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
// 和Hook完全一样,不同的只是将DetourAttach换成DetourDetach
DetourDetach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
DetourDetach((PVOID*)&OldOpenProcess, New_OpenProcess);
DetourTransactionCommit();
}

我们自己的函数如下编写,就是做一些简单的判断,比如这里要保护的进程id为5568 :

HANDLE g_handle = NULL;
HANDLE WINAPI New_OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId) {
if(dwProcessId == 5568) {
        g_handle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
return g_handle;
}
else
{
        HANDLE hHandle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
return hHandle;
}
}
BOOL WINAPI New_TerminateProcess(
_In_ HANDLE hProcess,
_In_ UINT uExitCode
) {
if(g_handle == hProcess)
{
MessageBox(NULL, L"该进程受保护!", L"Access Denied", NULL);
        g_handle = NULL;
returnfalse;
}
else
{
OldTerminateProcess(hProcess, uExitCode);
returntrue;
}
}

printfdword
本作品采用《CC 协议》,转载必须注明作者和本文链接
羊城杯 re wp
2021-10-07 16:01:00
题的质量还是可以,有些题因为遇到一些问题,没解出来。花时间来写wp一是巩固一些知识点,还有让更多的人可以互相学习。
HEVD学习笔记——UAF
2021-11-28 16:07:36
这里选择x86的驱动来进行黑盒测试学习内核漏洞,作为学习笔记记录下来。
EXP编写学习之绕过GS
2023-02-20 09:58:16
栈中的守护天使 :GSGS原理向栈内压入一个随机的DWORD值,这个随机数被称为canary ,IDA称为 Security Cookie。Security Cookie 放入 ebp前,并且data节中存放一个 Security Cookie的副本。栈中发生溢出时,Security Cookie首先被淹没,之后才是ebp和返回地址。函数返回之前,会添加一个Security Cookie验证操作,称为Security Check。检测到溢出时,系统将进入异常处理流程,函数不会正常返回,ret也不会被执行。函数使用无保护的关键字标记。缓冲区不是8字节类型 且 大小不大于4个字节。可以为函数强制启用GS。
0x01 进程遍历因为进程是在随时进行变动的所以我们需要获取一张快照1.1 CreateToolhelp32
前言在PE文件中,存在iat导入表,记录了PE文件使用的API以及相关的dll模块。可以看到使用了MessageBox这个API杀软会对导入表进行查杀,如果发现存在恶意的API,比如VirtualAlloc,CreateThread等,就会认为文件是一个恶意文件。自定义API函数FARPROC GetProcAddress;定义:typedef int ();HMODULE LoadLibraryA; // 成功返回句柄 失败返回NULL. 这里GetModuleHandle和LoadLibrary作用是一样的,获取dll文件。HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );printf; pMessageBox MyMessageBox = GetProcAddress; MyMessageBox; return 0;}. 程序可以正常运行:查看其导入表:User32.dll和MessageBox都不存在。实战测试用创建进程的方式加载shellcode。
最近粗浅的研究了一下Windows应用层相关调试API和对应调试原理,以达到实现反附加的功能。本文内容主要参考《软件调试》和网络上相关优秀文章,并且主要侧重在应用层调试附加方面,关于内核层面因为水平有限本文没有详细展现。
可在其中找受影响的版本复现,在受影响版本的系统中找到win32k.sys导入IDA。漏洞函数位于win32k.sys的SetImeInfoEx()函数,该函数在使用一个内核对象的字段之前并没有进行是否为空的判断,当该值为空时,函数直接读取零地址内存。如果在当前进程环境中没有映射零页面,该函数将触发页面错误异常,导致系统蓝屏发生。tagWINDOWSTATIONspklList对象的结构为:漏洞触发验证查看SSDT表dd KeServiceDescriptorTabledds Address L11C 显示地址里面值指向的地址. 以4个字节显示。
全局钩子注入-获取QQ密码实现 全局钩子注入-获取QQ密码实现 水一篇✨✨✨✨✨ SetWindowsHookExA 将应用程序定义的挂钩过程安装到挂钩链中。您将安装一个挂钩程序来监视系统中某些类型的事件。这些事件与特定线程或与调用线程相同的桌面中的所有线程相关联。
一篇静态免杀的文章
2021安洵杯PWN WP详解
2021-12-29 16:41:08
做了2021安洵杯线上赛题目,总体来说题目有简单有难的,难易程度合适,这次就做了pwn,把四道pwn题思路总结一下,重点是没几个人做出来的最后一道pwnsky,赛后做了复现。
VSole
网络安全专家