笔记:ShellCode 远程线程注入

本文介绍了ShellCode远程线程注入的概念和原理,包括HOOK、代码特征码、模块注入及其优缺点。通过实践部分讲解了LoadLibrary、CreateRemoteThread等关键函数的使用,阐述了如何在目标进程中创建并执行代码。

理论:

 什么是进程,什么是线程

进程就是4GB的空间,线程就是EIP

想在别的进程中执行自己的代码

  • 进程空间中必须要有我的代码
  • 进程空间中必须有一个线程执行我的代码
  • 方法:(原来的线程JMP到我的代码,或者开辟一个新的线程)

原来的线程Jmp到我的代码,称之为HOOK,改变原来线程的执行轨迹

如何将代码放入目标进程空间:

  1. 代码整理成一个模块,直接插入到目标的进程空间中(容易被发现)
  2. 只把函数体放入对方的进程空间【硬编码】

如果直接使用硬编码的方式,会造成问题:

  1. 使用全局变量的地址会发生改变
  2. 使用API函数会导致IAT的地址找不到

使用ShellCode - 一种可变的硬编码

ShellCode是研究病毒必须要掌握的一种技能:将硬编码ShellCode化 

ShellCode(代码注入)

  1. 不使用全局变量
  2. 需要什么API函数自己去获取
  • 缺点:比较麻烦 - 如果代码量过多

  • 优点:无法被检测到

特征码就是程序核心的硬编码

在程序的核心部分做记号,根据这个记号去找到对应的程序或者基址

无痕HOOK 就是Jmp

Inline HOOK 就是 Call

模块注入

优点:写起来非常方便

缺点:特征明显

实践:

LoadLibrary

将指定的模块加载到调用进程的地址空间中。 指定的模块可能会导致加载其他模块。

HMODULE LoadLibraryA(
  [in] LPCSTR lpLibFileName
);

[in] lpLibFileName

模块的名称。 可以是库模块 (.dll 文件) ,也可以是可执行模块 (.exe 文件) 。 如果指定的模块是可执行模块,则不会加载静态导入;

LoadLibrary 可用于将库模块加载到进程的地址空间中,并返回一个句柄,该句柄可用于 在 GetProcAddress 中 获取 DLL 函数的地址。

如果指定的模块是尚未为调用进程加载的 DLL,则系统会使用DLL_PROCESS_ATTACH值调用 DLL 的 DllMain 函数。 如果 DllMain 返回 TRUE, 则 LoadLibrary 将返回模块的句柄。 如果 DllMain 返回 FALSE,则系统会从进程地址空间中卸载 DLL, 而 LoadLibrary 返回 NULL。 从 DllMain 调用 LoadLibrary 是不安全的。

返回值

如果函数成功,则返回值是模块的句柄。

如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

CreateRemoteThread 

创建在另一个进程的虚拟地址空间中运行的线程。

HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

[in] hProcess

要在其中创建线程的进程句柄。

[in] lpThreadAttributes

指向 SECURITY_ATTRIBUTES 结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。

[in] dwStackSize

堆栈的初始大小(以字节为单位)。  如果此参数为 0 (零) ,则新线程将使用可执行文件的默认大小。 

[in] lpStartAddress

线程执行的应用程序定义函数的指针,表示远程进程中线程的起始地址。 函数必须存在于远程进程中。 有关详细信息,请参阅 ThreadProc

[in] lpParameter

指向要传递给线程函数的变量的指针。

[in] dwCreationFlags

控制线程创建的标志。

含义

0

线程在创建后立即运行。

CREATE_SUSPENDED

0x00000004

线程以挂起状态创建,在调用 ResumeThread 函数之前不会运行。

STACK_SIZE_PARAM_IS_A_RESERVATION

0x00010000

dwStackSize 参数指定堆栈的初始保留大小。 如果未指定此标志, dwStackSize 将指定提交大小。

[out] lpThreadId

指向接收线程标识符的变量的指针。

如果此参数为 NULL,则不返回线程标识符。

返回值

如果函数成功,则返回值是新线程的句柄。

如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。

CreateToolhelp32Snapshot 

获取指定进程以及这些进程使用的堆、模块和线程的快照。

HANDLE CreateToolhelp32Snapshot(
  [in] DWORD dwFlags,
  [in] DWORD th32ProcessID
);

Process32First 

检索有关系统快照中遇到的第一个进程的信息。

BOOL Process32First(
  [in]      HANDLE           hSnapshot,
  [in, out] LPPROCESSENTRY32 lppe
);

Process32Next 

检索有关系统快照中记录的下一个进程的信息。

BOOL Process32Next(
  [in]  HANDLE           hSnapshot,
  [out] LPPROCESSENTRY32 lppe
);

VirtualAllocEx 

在指定进程的虚拟地址空间中保留、提交或更改内存区域的状态。 函数将它分配的内存初始化为零。

LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

WriteProcessMemory

将数据写入指定进程中的内存区域。要写入的整个区域必须可访问,否则操作将失败。

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

WaitForSingleObject 

等待指定的对象处于信号状态或超时间隔已过。

DWORD WaitForSingleObject(
  [in] HANDLE hHandle,
  [in] DWORD  dwMilliseconds
);

GetExitCodeThread 

检索指定线程的终止状态。

BOOL GetExitCodeThread(
  [in]  HANDLE  hThread,
  [out] LPDWORD lpExitCode
);

VirtualFreeEx 

释放、取消提交或释放和取消提交指定进程的虚拟地址空间中的内存区域。

BOOL VirtualFreeEx(
  [in] HANDLE hProcess,
  [in] LPVOID lpAddress,
  [in] SIZE_T dwSize,
  [in] DWORD  dwFreeType
);

CloseHandle 

关闭打开的对象句柄。

BOOL CloseHandle(
  [in] HANDLE hObject
);

靶机线程代码:

#include <iostream>
#include <Windows.h>

int main()
{
	int i = 0;

	while (true)
	{
		Sleep(1000);
		i++;
		std::cout << "这是一个不会被关闭的主线程 运行时间:" << i << "秒" << std::endl;
	}

	return 0;
}

创建DLL

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBox(0, TEXT("DLL_PROCESS_ATTACH : dll被加载"), 0, 0);
    case DLL_THREAD_ATTACH:
        MessageBox(0, TEXT("DLL_PROCESS_ATTACH : dll被卸载"), 0, 0);
    case DLL_THREAD_DETACH:
        MessageBox(0, TEXT("DLL_THREAD_ATTACH : 线程附加到 DLL"), 0, 0);
    case DLL_PROCESS_DETACH:
        MessageBox(0, TEXT("DLL_THREAD_ATTACH : 线程从 DLL 分离"), 0, 0);
        break;
    }
    return TRUE;
}

将生成的DLL文件放置在文件目录下:

应用程序 与 DLL文件都准备好后,开始编写注入代码


 

// 6-3 InjectCode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <Windows.h>
#include <iostream>
#include <Tlhelp32.h>

int main()
{
    // 设置本地化环境为默认的宽字符本地化
    _wsetlocale(LC_ALL, L"");
    std::cout << "注入代码开始运行!!!\n";

    // 定义目标进程
    WCHAR wc_TargetProcessName[] = L"远程线程注入.exe";
    DWORD dw_TargetProcessPid = 0;
    HANDLE h_TargetProcessHandle = NULL;

    // 枚举所有进程 找到要注入的进程
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot != INVALID_HANDLE_VALUE) {
        if (Process32First(hSnapshot, &pe32)) {
            do {
                if (!wcscmp(pe32.szExeFile, wc_TargetProcessName))
                {
                    printf("成功:进程ID - %d ,进程名称 - %ws \n", pe32.th32ProcessID, pe32.szExeFile);
                    dw_TargetProcessPid = pe32.th32ProcessID;
                    break;
                }
                
            } while (Process32Next(hSnapshot, &pe32));
        }
        CloseHandle(hSnapshot);
    }

    if (!dw_TargetProcessPid)
    {
        printf("ERROR : 未找到对应的进程\n");
        return -1;
    }

    h_TargetProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dw_TargetProcessPid);

    if (!h_TargetProcessHandle)
    {
        printf("ERROR : 未能成功获取进程句柄\n");
        return -1;
    }
    else {
        printf("成功:进程句柄 %x \n", h_TargetProcessHandle);
    }

    

    //- - - - - 找到LoadLibrary的地址
    HMODULE hModule = LoadLibrary(L"kernel32.dll");
    FARPROC LoadLibraryAddress = 0;
    LoadLibraryAddress = GetProcAddress(hModule, "LoadLibraryW"); // 请将"MyFunction"替换为要获取地址的函数名称
    if (!LoadLibraryAddress)
    {
        printf("ERROR : 未能成功获取 LoadLibraryA 的函数地址\n");
    }
    else {
        printf("成功:LoadLibraryA 的函数地址 0x%x \n", LoadLibraryAddress);
    }

    //- - - - - 在目标进程的空间内开辟内存,存放LoadLibrary的参数
    WCHAR wc_DllName[] = L"Inject.dll";
    LPVOID MemoryBase = VirtualAllocEx(h_TargetProcessHandle, NULL, sizeof(wc_DllName), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!MemoryBase)
    {
        printf("ERROR : 开辟内存空间失败\n");
    }
    else {
        printf("成功:在目标进程开辟的内存空间基址 0x%x \n", MemoryBase);
    }

    SIZE_T WriteByte = 0;
    BOOL ReturnWritebool = WriteProcessMemory(h_TargetProcessHandle, MemoryBase, wc_DllName, sizeof(wc_DllName), &WriteByte);
    if (!ReturnWritebool)
    {
        printf("ERROR : 往进程写入数据失败\n");
    }
    else {
        printf("成功:往进程中写入数据成功 写入字节数%d \n", WriteByte);
    }

    //- - - - - 创建远程线程,执行LoadLibrary加载我们的Inject.dll
    HANDLE InJectThread = CreateRemoteThread(h_TargetProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, MemoryBase, 0, NULL);
    WaitForSingleObject(InJectThread, 1000);

    //- - - - - 释放远程线程
    DWORD ExitCode = NULL;  //获取目标进程的模块句柄
    GetExitCodeThread(InJectThread, &ExitCode);
    if (!ReturnWritebool)
    {
        printf("ERROR : 获取目标进程模块失败\n");
    }
    else {
        printf("成功:目标进程模块地址 %x \n", ExitCode);
    }


    //- - - - - 释放远程进程中写入的空间
    if (!VirtualFreeEx(h_TargetProcessHandle, MemoryBase, sizeof(wc_DllName), MEM_DECOMMIT)) {
        printf("ERROR : 释放空间失败\n");
    }
    else {
        printf("成功:进程空间已释放\n");
    }

    //CloseHandle(InJectThread);
    //CloseHandle(h_TargetProcessHandle);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值