Injecting Code Into Privileged Win32 Processes

For a while now, I've been searching for the optimal way to inject code into privileged Win32 processes like lsass.exe, csrss.exe, and winlogon.exe.

There are many functions such as the LSA and SAM exports that even users logged in with full administrative rights cannot execute
unless they do so under the context of one of these privileged processes.

There are a few tricks that I learned along the way.

First, it is necessary to adjust the token privileges of your program so that debugging (SE_PRIVILEGE_ENABLED) is allowed.

If you are injecting code into a lower privileged process, then this will not be needed.

Also, the target process will need to be opened with PROCESS_ALL_ACCESS rights.

Its all pretty easy on Windows 2000 and XP Service Pack 0 and 1.
On these systems, you can use the documented CreateRemoteThread() function, but first the code you want
to run in the security context of the remote process needs to exist in that process' virtual memory space.
You can put it there by using VirtualAllocEx() and WriteProcessMemory().

With XP SP2 and later (2003, Vista) some new security measures prevent the traditional CreateRemoteThread() function from working properly.
You should be able to open the process, allocate memory on its heap, and write data to the allocated region,
but when trying to invoke the remote thread, it will fail with ERROR_NOT_ENOUGH_MEMORY.

On Vista, I found that an author can substitute the CreateRemoteThread() call with NtCreateThreadEx() export from ntdll.dll
and it will allow for the thread to execute properly. This requires you to auto-detect the version of the operating system and
branch to this different call if on Vista.

Also, this is isn't really a universal solution, because NtCreateThreadEx() doesn't exist on pre-Vista sytsems.
So now we're stuck with using CreateRemoteThread() on 2000 and XP SP 0,1 and NtCreateThreadEx() on Vista.
This is already getting messy, and we still don't have a solution for XP SP2.

Also, the NtCreateThreadEx() function takes an undocumented structure, whose members can be initialized appropriately
by reversing other binaries that use the function, but it looks really ugly in source code since I don't really know what the members are for,
or why particular values are significant.

For XP SP2 I did a little debugging and found that inside CreateRemoteThread(), there is a call to ZwCreateThread() which is an export
from ntdll.dll. The call is made while specifying that the thread should start suspended, which it does properly,
however down the road still inside CreateRemoteThread() before ZwResumeThread() is called, there is a call to CsrClientCallServer()
which fails and eventually leads to the error message.

This behavior makes you wonder, if you can just call ZwCreateThread() directly, then the call to CsrClientCallServer() will be avoided
and the thread will execute. The problem is that ZwCreateThread() doesn't allow one to set the thread start address easily
(you have to configure the INITIAL_TEB members to set EIP to your start address using mostly undocumented structures and functions).

However, this all can be avoided by using the RtlCreateUserThread() function instead,
which configures and calls all the undocumented functions for you, and eventually invokes ZwCreateThread() with the result.
Although RtlCreateUserThread() is undocumented also, its hardly as complex as the rest and is pretty simple to use.

At this point, we can successfully execute remote threads into privileged processes across all target platforms,
but as mentioned before, its pretty messy.

We're using three different, largely undocumented functions and auto-detecting which one to use based on the OS version.

The better solution is to create a secondary program that adds a service object (your injector program)
to the service control manager database on the target system. Since you're administrator, which is required anyway,
you'll be able to add these entries and start the service. This will enable the injector program
to run with different access rights than normal code, and the traditional CreateRemoteThread()
will work properly on Windows 2000, all of XP, and 2003/Vista.

The API functions for adding and controlling the service are documented by MSDN and remain consistent across all of the platforms.

So, what is learned is that we can use a number of different functions to inject code into privileged remote processes,
including RtlCreateUserThread() on XP SP2, and NtCreateThreadEx() on Vista, but the optimal way is to install a temporary service
and allow CreateRemoteThread() to be the single API that accomplishes the task for all platforms.


PS:

Basically the needed access rights are identical to XP: In both OSs you need admin rights for system wide injection. However, in Vista when UAC is enabled even admin users don't have admin rights by default. So you need to right click your exe and choose "run as administrator" (as LeVuHoang has already said). Alternatively you can add a manifest to your exe which will tell Vista that your app needs admin rights. If you do that, you don't need to do the "run as admin" step, anymore. However, the end user will still have to confirm the operation. If you don't like all this you need to inject from a service (see HookProcessTermination demo).

One other thing to look for is that the hook dll needs enough NTFS rights or else it might not be injected into all processes successfully. Vista is a bit more strict there than XP was.

void  Inject(HWND hWnd,  char *  strDll)
{
    GetWindowThreadProcessId(hWnd, 
& pId);
    HANDLE hProcess 
=  OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId);
    LPVOID lpRemoteAddress 
=  VirtualAllocEx(hProcess, NULL, strlen(strDll), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, lpRemoteAddress, (LPVOID)strDll, strlen(strDll), NULL);
    CreateRemoteThread(hProcess, NULL, 
0 ,
        (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(
" Kernel32 " ),  " LoadLibraryA " ),
        lpRemoteAddress, 
0 , NULL);


The API does not create threads in other sessions (this behavior is documented in MSDN).

One way to load a library into a process of another session is: Create a suspended thread (ntdll!RtlCreateUserThread) at kernel32!ExitThread, schedule an asynchronous procedure call (ntdll!NtQueueApcThread) at kernel32!LoadLibraryEx, resume the thread (kernel32!ResumeThread - this executes the pending APC), and wait for the end of the thread (kernel32!WaitForSingleObject). APCs do not return a value - therefore the return value of kernel32!LoadLibraryEx is lost. There is much more work required to use this method in the exact same manner as CreateRemoteThread(LoadLibrary) (includes reading the PEB’s loader structures).

Other hints:

    * Never ever use CreateRemoteThread on a target process that differs in 'bitness' (kernel32!IsWow64Process). On some Windows versions this freezes your calling thread.
    * Dynamically determine the kernel32’s image base (might not be loaded at all).

 


对于 RtlCreateUserThread 函数的线程函数, 以下是个示例:

#define  LoadLibraryA_ADDR       0xDDDDDDDD 
#define  RtlExitUserThread_ADDR  0xEEEEEEEE 

static __declspec(naked) DWORD WINAPI ThreadDummy(LPVOID lpParam) 
{
    __asm { 
        push    dword ptr [esp+4]           ; // 将传进来的线程函数的参数压栈 
        mov     eax, LoadLibraryA_ADDR      ; // LoadLibraryA 或 FreeLibrary 函数的地址 
        call    eax                         ; // 调用 LoadLibraryA 函数
        push    eax                         ; // 将 RtlExitUserThread 函数的参数压栈
        mov     eax, RtlExitUserThread_ADDR ; // RtlExitUserThread 函数的地址 
        call    eax                         ; // 调用 RtlExitUserThread 函数
        ret     4                           ; // 返回 
    } 
}

static  __declspec(naked) DWORD WINAPI ThreadDummy_end(LPVOID lpParam) 

    __asm { 
        ret     4                            ; 
    } 
}

PUCHAR FindDWordFromBuffer(PUCHAR lpBuffer, UINT cchMax, DWORD dwValue) 

    PUCHAR pResult 
=  NULL; 
    UINT nIter 
=   0
    
for  (nIter = 0 ; nIter < cchMax; nIter ++
    { 
        
if  (  * (DWORD  * )(lpBuffer  +  nIter)  ==  dwValue ) { 
            pResult 
=  lpBuffer  +  nIter; 
            
break
        } 
    } 
    
return  pResult; 


BOOL BuildRemoteThreadCode(OUT PUCHAR lpCode, UINT cchMax, BOOL bInject) 

    UINT nCodeLen 
=   0
    PUCHAR pIter 
=  NULL; 
    DWORD dwFnAddr 
=   0
    
    
if  (NULL == lpCode  ||   0 == cchMax) { 
        
return  FALSE; 
    } 
    
    nCodeLen 
=  (PUCHAR)  & ThreadDummy_end  -  (PUCHAR)  & ThreadDummy; 
    
if  (nCodeLen  >  cchMax) { 
        
return  FALSE; 
    } 
    
    memcpy((
void   * )lpCode, ( void   * & ThreadDummy, nCodeLen); 
    
    {
        pIter 
=  FindDWordFromBuffer(lpCode, nCodeLen, LoadLibraryA_ADDR); 
        
if  (NULL  ==  pIter) { 
            
return  FALSE; 
        } 
        
        
if  (bInject) { 
            dwFnAddr 
=  (DWORD) GetProcAddress(GetModuleHandle(_T( " kernel32.dll " )),  " LoadLibraryA " ); 
        } 
else  { 
            dwFnAddr 
=  (DWORD) GetProcAddress(GetModuleHandle(_T( " kernel32.dll " )),  " FreeLibrary " ); 
        } 
        
        
if  ( 0   ==  dwFnAddr) { 
            
return  FALSE; 
        } 
        
* (DWORD  * )pIter  =  dwFnAddr; 
    } 
    
    {
        pIter 
=  FindDWordFromBuffer(lpCode, nCodeLen, RtlExitUserThread_ADDR); 
        
if  (NULL  ==  pIter) { 
            
return  FALSE; 
        } 
        
        dwFnAddr 
=  (DWORD) GetProcAddress(GetModuleHandle(_T( " ntdll.dll " )),  " RtlExitUserThread " ); 
        
if  ( 0   ==  dwFnAddr) { 
            
return  FALSE; 
        } 
        
* (DWORD  * )pIter  =  dwFnAddr; 
    } 
    
    
return  TRUE; 
}

自己分配一块足够大的内存, 以这块内存的指针作为参数调用 BuildRemoteThreadCode 函数后, 这块内存就可以写到目标进程里面, 并作为 RtlCreateUserThread 函数的线程函数执行了.

当然, 线程函数的参数, 还是得自己准备了, 也就是一个字符串指针或一个模块的 HMODULE. 相信大家都会, 不用我废话了.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ven.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (ORA-28040: No matching authentication protocol) 这个错误是由于数据库连接池无法创建可池化的连接工厂,原因是数据库中没有匹配的身份验证协议。可能是数据库版本过低或者连接字符串中的身份验证协议不正确。需要检查数据库版本和连接字符串,确保它们匹配并正确配置。 ### 回答2: Error injecting: org.apache.ma这个错误一般是因为应用程序在启动或运行过程中,无法将所需的依赖项注入到相应的类中导致的。这个错误有多种可能性,下面列出其中几种常见的原因: 1. Maven 依赖库版本不兼容:可能是因为你的项目依赖了 Maven 库的不兼容版本,这会导致类库无法正常加载和注入依赖项。这时需要检查你项目中的依赖库,尝试使用较新的版本或尝试去掉不必要的依赖库。 2. 项目代码缺少依赖注入注释:依赖注入是一种通过注释和配置文件来完成的技术,如果你的项目中缺少相关的注释或配置文件,就会出现无法注入依赖项的错误。需要检查你的代码中是否存在缺失的注释或配置文件。 3. Spring 配置文件错误:Spring 是常用的 Java 开发框架之一,其 IOC 和 AOP 特性依赖于配置文件的正确性。如果你的 Spring 配置文件出现错误,就会导致无法正常注入依赖项。需要检查你的 Spring 配置文件是否存在错误,并尝试修复。 4. 类路径错误:类路径是指 JVM 加载类文件的路径,如果类路径设置错误,就会导致无法加载和注入依赖项的错误。需要检查你的类路径设置是否正确,并尝试修复。 5. 编译器版本兼容性问题:如果你的项目代码使用了不兼容的编译器版本,就会导致无法加载和注入依赖项的错误。需要检查你的项目使用的编译器版本是否适合目标运行环境,并尝试更换编译器版本。 综上所述,Error injecting: org.apache.ma错误可能是由多种原因导致的。需要排查具体原因并根据实际情况来进行相应的修复。 ### 回答3: 首先,让我们来了解一下“error injecting: org.apache.ma”的意思。当您尝试运行或部署一个应用程序或项目时,如果系统报错“error injecting: org.apache.ma”,这意味着其中的注入过程出现了某些错误。 注入是指将一个类的实例或字段插入到另一个类中。在Java编程中,注入通常用于依赖注入(DI)框架,如Spring等,以处理不同的服务或组件之间的依赖关系。此外,在Java EE中,注入常用于管理不同类型的资源,例如数据源、EJB等。 现在,我们来看一下可能导致“error injecting: org.apache.ma”的原因和解决方法: 1.依赖项不正确:当应用程序的依赖项无法满足注入的类型时,就会出现注入错误。在这种情况下,您需要检查依赖项是否正确设置和导入。 2.未正确配置注入器:另一个可能导致注入错误的原因是未正确配置注入器。这包括配置文件中的设置和注入器的参数配置。在这种情况下,您需要仔细查看配置文件和注入器设置,并确保它们符合正确的规范。 3.重复的注入:有时,可能会存在重复的注入,这会导致注入失败。在这种情况下,您需要检查是否存在多个相同类型的注入,并将其删除。 4.冲突的注入:有时,当您尝试注入两个有相同名称或相同类型的类时,也会出现注入错误。解决方法是在注入时使用一个唯一的名称或类型。 5.版本不匹配:当注入的类和使用的框架或库之间的版本不兼容时,也可能会出现注入错误。在这种情况下,您需要确保所有的库和框架都与注入的类兼容。 总之,“error injecting: org.apache.ma”可能会发生在应用程序或项目中的注入过程中,主要是由于依赖项不正确、注入器未正确配置、重复或冲突注入、版本不匹等原因。解决方法是检查这些问题,并根据实际情况进行更改和修复。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值