SE_DEBUG_NAME调试级别,可以用来关机等
ring3下不能提升到SYSTEM级别的。要想获得SYSTEM权限,只能是做成service等
原来X86的设想是:
ring 0 系统内核
ring 1 系统应用服务
ring 2 应用内核服务
ring 3 应用程序
但为了兼容,所以Windows只采用了ring 0和ring 3
先搞清楚: ring 是CPU的概念!不是操作系统概念!
研究这个不是为了做什么木马之类的,主要是积累知识。
一、如何创建系统进程?
1.方法一、服务进程
可以说,这是最简单的创建系统进程的方法,但是,写出来的程序一般是不能有界面的(也可以有界面)。大致方法又有两种:ATL服务进程和一般的服务进程。
2.方法二、注入法
注入到系统进程,再由系统进程创建进程,这时候创建的进程就是具有系统权限的。注入的方法很多了,只要是能被系统进程加载的,都可以用来注入,例如:Hook API、Hook message、远程线程注入、LSP,等等。
3、方法三、提权
创建高权限进程http://forum.eviloctal.com/thread-19527-1-1.html
二、如何创建系统关键进程?
系统关键进程——在任务管理器中无法结束的进程,强行结束的话,系统会提示“系统关键进程......”等。
其实,要想不让别人关闭进程,有很多方法。比如,进程隐藏(SSDT),HOOK API函数(ring3、ring0 HOOK),多进程互锁(互相复活),等等。这里不管这些,主要还是看看如何创建系统关键进程。
windows的每个用户登录系统后,系统会产生一个访问令牌(access token) ,其中关联了当前用户的权限信息,用户登录后创建的每一个进程都含有用户access token的拷贝,当进程试图执行某些需要特殊权限的操作或是访问受保护的内核对象时,系统会检查其acess token中的权限信息以决定是否授权操作。Administrator组成员的access token中会含有一些可以执行系统级操作的特权(privileges) ,如终止任意进程、关闭/重启系统、加载设备驱动和更改系统时间等,不过这些特权默认是被禁用的,当Administrator组成员创建的进程中包含一些需要特权的操作时,进程必须首先打开这些禁用的特权以提升自己的权限,否则系统将拒绝进程的操作。注意,非Administrator组成员创建的进程无法提升自身的权限,因此下面提到的进程均指Administrator组成员创建的进程。
Windows以字符串的形式表示系统特权,如“SeCreatePagefilePrivilege”表示该特权用于创建页面文件,“SeDebugPrivilege”表示该特权可用于调试及更改其它进程的内存,为了便于在代码中引用这些字符串,微软在winnt.h中定义了一组宏,如 #define SE_DEBUG_NAME TEXT("SeDebugPrivilege")。完整的特权列表可以查阅msdn的security一章。虽然Windows使用字符串表示特权,但查询或更改特权的API需要LUID来引用相应的特权,LUID表示local unique identifier,它是一个64位值,在当前系统中是唯一的。为了提升进程权限到指定的特权,我们必须先找到该特权对应的LUID,这时要调用LookupPrivilegeValue函数。
获得特权对应的LUID之后,我们要打开该特权。此时要用到LUID_AND_ATTRIBUTES结构,其定义如下:
typedef struct _LUID_AND_ATTRIBUTES { LUID Luid; DWORD Attributes; } LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;
Attributes取SE_PRIVILEGE_ENABLED时将打开Luid对应的特权。设置完成后,我们需要调用AdjustTokenPrivileges函数通知操作系统将指定的access token权限中的特权置为打开状态,前面我们说过,进程执行需要特列权限的操作时系统将检查其access token,因此更改了进程的access token特权设置,也就是更改了所属进程的特权设置。AdjustTokenPrivileges函数的原型如下:
BOOL WINAPI AdjustTokenPrivileges( __in HANDLE TokenHandle, __in BOOL DisableAllPrivileges, __in_opt PTOKEN_PRIVILEGES NewState, __in DWORD BufferLength, __out_opt PTOKEN_PRIVILEGES PreviousState, __out_opt PDWORD ReturnLength );
TokenHandle是要更改特权设置的acess token的句柄,DisableAllPrivileges表示是否禁用该access token的所有特权,NewState用来传递要新的特权设置,注意它的类型是PTOKEN_PRIVILEGES,PTOKEN_PRIVILEGES是TOKEN_PRIVILEGES结构的指针,定义如下:
typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
其中ANYSIZE_ARRAY被定义为1,可以看到TOKEN_PRIVILEGES中包含了用于设置特权信息的LUID_AND_ATTRIBUTES结构,在使用时,只需要将PrivilegeCount赋为1,然后把Privileges数组的第1个元素(Privileges[0])的Luid域设置为指定特权的Luid,再将其Attributes域设置为SE_PRIVILEGE_ENABLED,就可以完成TokenHandle表示的access token权限的提升了。
下面是一个实际的例子,用来将执行promoteProcessPrivilege的当前进程的指定特权打开,函数参数为指定的特权名,可以传递其宏定义,也可以是完整的字符串表示:
BOOL promoteProcessPrivileges(const TCHAR* newPrivileges) { HANDLE tokenHandle; //获得当前进程的access token句柄 if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle) == FALSE) return FALSE; TOKEN_PRIVILEGES structTkp; //查找newPrivileges参数对应的Luid,并将结果写入structTkp.Privileges[0]的Luid域中 if(::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE){ CloseHandle(tokenHandle); return FASLE; } //设置structTkp结构 structTkp.PrivilegeCount = 1; structTkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //通知操作系统更改权限 if(::AdjustTokenPrivileges(tokenHandle, FALSE, &structTkp, sizeof(structTkp), NULL, NULL) == FALSE){ CloseHandle(tokenHandle); return FALSE; } CloseHandle(tokenHandle); return TRUE; }
我们来用一个简单的例子验证promoteProcessPrivileges函数。下面的traceSystemProcess用于遍历当前系统进程,如果调用traceSystemProcess函数的进程以默认权限运行,对于如csrss.exe之类的进程,函数将没有足够的权限获得其模块名。如果在traceSystemProcess之前调用了promoteProcessPrivileges将进程权限提升至SE_DEBUG_NAME级别,traceSystemProcess函数将能正确打印出如csrss.exe等关键进程的路径:
BOOL traceSystemProcess() { PROCESSENTRY32 processEntry; processEntry.dwSize = sizeof(processEntry); HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hProcessSnap == INVALID_HANDLE_VALUE){ _tprintf(_T("CreateToolhelp32Snapshot 调用失败!/n")); return FALSE; } BOOL bMore = ::Process32First(hProcessSnap, &processEntry); while(bMore){ _tprintf(_T("进程名称:%s /n"), processEntry.szExeFile); _tprintf(_T("进程ID号:%u /n"), processEntry.th32ProcessID); HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processEntry.th32ProcessID); if(hProcess != NULL){ TCHAR szBuffer[MAX_PATH] = {_T('/0')}; if(::GetModuleFileNameEx(hProcess, NULL, szBuffer, MAX_PATH)){ _tprintf(_T("进程路径:%s /n"), szBuffer); } ::CloseHandle(hProcess); } _tprintf(_T("/n")); bMore = ::Process32Next(hProcessSnap,&processEntry); } ::CloseHandle(hProcessSnap); return TRUE; }
测试结果不再列出,有兴趣的朋友可以自己试一试。