问题已解决。谢谢斑竹帮忙!:
前段时间看到linxer兄的《一段仿真PE加载器行为的程序》的帖子,
http://bbs.pediy.com/showthread.php?t=27134 发现自己也收藏了一段类似的代码;
拿出来给大家讨论。希望有人出来把他完善了,给需要的人分享一下。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
在内存中运行可执行程序
在内存中运行可执行程序,好处是可以给程序加壳,加密源程序,静态反汇编无法获得PE输入节,但是因为运行后仍然是独立的进程,所以没办法防止远程线程注入,挂接API钩子。
本人收集的一段代码,请给位大哥给与批评指教。
typedef IMAGE_SECTION_HEADER ( * PIMAGE_SECTION_HEADERS)[ 1 ];
// 计算对齐后的大小
unsigned long GetAlignedSize(unsigned long Origin, unsigned long Alignment)
{
return (Origin + Alignment - 1 ) / Alignment * Alignment;
}
// 计算加载pe并对齐需要占用多少内存
// 未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0
unsigned long CalcTotalImageSize(PIMAGE_DOS_HEADER MzH
, unsigned long FileLen
, PIMAGE_NT_HEADERS peH
, PIMAGE_SECTION_HEADERS peSecH)
{
unsigned long res;
// 计算pe头的大小
res = GetAlignedSize( peH -> OptionalHeader.SizeOfHeaders , peH -> OptionalHeader.SectionAlignment);
// 计算所有节的大小
for ( int i = 0 ; i < peH -> FileHeader.NumberOfSections; ++ i)
{
// 超出文件范围
if (peSecH[i] -> PointerToRawData + peSecH[i] -> SizeOfRawData > FileLen)
{
return 0 ;
}
else if (peSecH[i] -> VirtualAddress) // 计算对齐后某节的大小
{
if (peSecH[i] -> Misc.VirtualSize)
{
res = GetAlignedSize( peSecH[i] -> VirtualAddress + peSecH[i] -> Misc.VirtualSize
, peH -> OptionalHeader.SectionAlignment);
}
else
{
res = GetAlignedSize( peSecH[i] -> VirtualAddress + peSecH[i] -> SizeOfRawData
, peH -> OptionalHeader.SectionAlignment);
}
}
else if ( peSecH[i] -> Misc.VirtualSize < peSecH[i] -> SizeOfRawData )
{
res += GetAlignedSize( peSecH[i] -> SizeOfRawData
, peH -> OptionalHeader.SectionAlignment);
}
else
{
res += GetAlignedSize( peSecH[i] -> Misc.VirtualSize
, peH -> OptionalHeader.SectionAlignment);
} // if_else
} // for
return res;
}
// 加载pe到内存并对齐所有节
BOOL AlignPEToMem( void * Buf
, long Len
, PIMAGE_NT_HEADERS & peH
, PIMAGE_SECTION_HEADERS & peSecH
, void *& Mem
, unsigned long & ImageSize)
{
PIMAGE_DOS_HEADER SrcMz; // DOS头
PIMAGE_NT_HEADERS SrcPeH; // PE头
PIMAGE_SECTION_HEADERS SrcPeSecH; // 节表
SrcMz = (PIMAGE_DOS_HEADER)Buf;
if ( Len < sizeof (IMAGE_DOS_HEADER) )
return FALSE;
if ( SrcMz -> e_magic != IMAGE_DOS_SIGNATURE )
return FALSE;
if ( Len < SrcMz -> e_lfanew + ( long ) sizeof (IMAGE_NT_HEADERS) )
return FALSE;
SrcPeH = (PIMAGE_NT_HEADERS)(( int )SrcMz + SrcMz -> e_lfanew);
if ( SrcPeH -> Signature != IMAGE_NT_SIGNATURE )
return FALSE;
if ( (SrcPeH -> FileHeader.Characteristics & IMAGE_FILE_DLL) ||
(SrcPeH -> FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE == 0 ) ||
(SrcPeH -> FileHeader.SizeOfOptionalHeader != sizeof (IMAGE_OPTIONAL_HEADER)) )
{
return FALSE;
}
SrcPeSecH = (PIMAGE_SECTION_HEADERS)(( int )SrcPeH + sizeof (IMAGE_NT_HEADERS));
ImageSize = CalcTotalImageSize( SrcMz, Len, SrcPeH, SrcPeSecH);
if ( ImageSize == 0 )
return FALSE;
Mem = VirtualAlloc( NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配内存
if ( Mem != NULL )
{
// 计算需要复制的PE头字节数
unsigned long l = SrcPeH -> OptionalHeader.SizeOfHeaders;
for ( int i = 0 ; i < SrcPeH -> FileHeader.NumberOfSections; ++ i)
{
if ( (SrcPeSecH[i] -> PointerToRawData) &&
(SrcPeSecH[i] -> PointerToRawData < l) )
{
l = SrcPeSecH[i] -> PointerToRawData;
}
}
memmove( Mem, SrcMz, l);
peH = (PIMAGE_NT_HEADERS)(( int )Mem + ((PIMAGE_DOS_HEADER)Mem) -> e_lfanew);
peSecH = (PIMAGE_SECTION_HEADERS)(( int )peH + sizeof (IMAGE_NT_HEADERS));
void * Pt = ( void * )((unsigned long )Mem
+ GetAlignedSize( peH -> OptionalHeader.SizeOfHeaders
, peH -> OptionalHeader.SectionAlignment)
);
for ( i = 0 ; i < peH -> FileHeader.NumberOfSections; ++ i)
{
// 定位该节在内存中的位置
if (peSecH[i] -> VirtualAddress)
Pt = ( void * )((unsigned long )Mem + peSecH[i] -> VirtualAddress);
if (peSecH[i] -> SizeOfRawData)
{
// 复制数据到内存
memmove(Pt, ( const void * )((unsigned long )(SrcMz) + peSecH[i] -> PointerToRawData), peSecH[i] -> SizeOfRawData);
if (peSecH[i] -> Misc.VirtualSize < peSecH[i] -> SizeOfRawData)
Pt = ( void * )((unsigned long )Pt + GetAlignedSize(peSecH[i] -> SizeOfRawData, peH -> OptionalHeader.SectionAlignment));
else // pt 定位到下一节开始位置
Pt = ( void * )((unsigned long )Pt + GetAlignedSize(peSecH[i] -> Misc.VirtualSize, peH -> OptionalHeader.SectionAlignment));
}
else
{
Pt = ( void * )((unsigned long )Pt + GetAlignedSize(peSecH[i] -> Misc.VirtualSize, peH -> OptionalHeader.SectionAlignment));
}
}
}
return TRUE;
}
typedef void * (__stdcall * pfVirtualAllocEx)(unsigned long , void * , unsigned long , unsigned long , unsigned long );
pfVirtualAllocEx MyVirtualAllocEx = NULL;
BOOL IsNT()
{
return MyVirtualAllocEx != NULL;
}
// 生成外壳程序命令行
char * PrepareShellExe( char * CmdParam, unsigned long BaseAddr, unsigned long ImageSize)
{
if (IsNT())
{
char * Buf = new char [ 256 ];
memset(Buf, 0 , 256 );
GetModuleFileName( 0 , Buf, 256 );
strcat(Buf, CmdParam);
return Buf; // 请记得释放内存;-)
}
else
{
// Win98下的处理请参考原文;-)
// http://community.csdn.net/Expert/topic/4416/4416252.xml?temp=8.709133E-03
return NULL;
}
}
// 是否包含可重定向列表
BOOL HasRelocationTable(PIMAGE_NT_HEADERS peH)
{
return (peH -> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
&& (peH -> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
}
#pragma pack(push, 1 )
typedef struct {
unsigned long VirtualAddress;
unsigned long SizeOfBlock;
} * PImageBaseRelocation;
#pragma pack(pop)
// 重定向PE用到的地址
void DoRelocation(PIMAGE_NT_HEADERS peH, void * OldBase, void * NewBase)
{
unsigned long Delta = (unsigned long )NewBase - peH -> OptionalHeader.ImageBase;
PImageBaseRelocation p = (PImageBaseRelocation)((unsigned long )OldBase
+ peH -> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (p -> VirtualAddress + p -> SizeOfBlock)
{
unsigned short * pw = (unsigned short * )(( int )p + sizeof ( * p));
for (unsigned int i = 1 ; i <= (p -> SizeOfBlock - sizeof ( * p)) / 2 ; ++ i)
{
if (( * pw) & 0xF000 == 0x3000 ) {
unsigned long * t = (unsigned long * )((unsigned long )(OldBase) + p -> VirtualAddress + (( * pw) & 0x0FFF ));
* t += Delta;
}
++ pw;
}
p = (PImageBaseRelocation)pw;
}
}
// 卸载原外壳占用内存
BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr)
{
typedef unsigned long (__stdcall * pfZwUnmapViewOfSection)(unsigned long , unsigned long );
pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
BOOL res = FALSE;
HMODULE m = LoadLibrary( " ntdll.dll " );
if (m) {
ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(m, " ZwUnmapViewOfSection " );
if (ZwUnmapViewOfSection)
res = (ZwUnmapViewOfSection((unsigned long )ProcHnd, BaseAddr) == 0 );
FreeLibrary(m);
}
return res;
}
// 创建外壳进程并获取其基址、大小和当前运行状态
BOOL CreateChild( char * Cmd, CONTEXT & Ctx, HANDLE & ProcHnd, HANDLE & ThrdHnd,
unsigned long & ProcId, unsigned long & BaseAddr, unsigned long & ImageSize)
{
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned long old;
MEMORY_BASIC_INFORMATION MemInfo;
memset( & si, 0 , sizeof (si));
memset( & pi, 0 , sizeof (pi));
si.cb = sizeof (si);
BOOL res = CreateProcess(NULL, Cmd, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, & si, & pi); // 以挂起方式运行进程;
if (res) {
ProcHnd = pi.hProcess;
ThrdHnd = pi.hThread;
ProcId = pi.dwProcessId;
// 获取外壳进程运行状态,[ctx.Ebx+8]内存处存的是外壳进程的加载基址,ctx.Eax存放有外壳进程的入口地址
Ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(ThrdHnd, & Ctx);
ReadProcessMemory(ProcHnd, ( void * )(Ctx.Ebx + 8 ), & BaseAddr, sizeof (unsigned long ), & old); // 读取加载基址
void * p = ( void * )BaseAddr;
// 计算外壳进程占有的内存
while (VirtualQueryEx(ProcHnd, p, & MemInfo, sizeof (MemInfo)))
{
if (MemInfo.State = MEM_FREE) break ;
p = ( void * )((unsigned long )p + MemInfo.RegionSize);
}
ImageSize = (unsigned long )p - (unsigned long )BaseAddr;
}
return res;
}
// 创建外壳进程并用目标进程替换它然后执行
HANDLE AttachPE( char * CmdParam, PIMAGE_NT_HEADERS peH, PIMAGE_SECTION_HEADERS peSecH,
void * Ptr, unsigned long ImageSize, unsigned long & ProcId)
{
HANDLE res = INVALID_HANDLE_VALUE;
CONTEXT Ctx;
HANDLE Thrd;
unsigned long Addr, Size;
char * s = PrepareShellExe(CmdParam, peH -> OptionalHeader.ImageBase, ImageSize);
if (s == NULL) return res;
if (CreateChild(s, Ctx, res, Thrd, ProcId, Addr, Size)) {
void * p = NULL;
unsigned long old;
if ((peH -> OptionalHeader.ImageBase == Addr) && (Size >= ImageSize)) { // 外壳进程可以容纳目标进程并且加载地址一致
p = ( void * )Addr;
VirtualProtectEx(res, p, Size, PAGE_EXECUTE_READWRITE, & old);
}
else if (IsNT()) {
if (UnloadShell(res, Addr)) { // 卸载外壳进程占有内存
p = MyVirtualAllocEx((unsigned long )res, ( void * )peH -> OptionalHeader.ImageBase, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
if ((p == NULL) && HasRelocationTable(peH)) { // 分配内存失败并且目标进程支持重定向
p = MyVirtualAllocEx((unsigned long )res, NULL, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (p) DoRelocation(peH, Ptr, p); // 重定向
}
}
if (p) {
WriteProcessMemory(res, ( void * )(Ctx.Ebx + 8 ), & p, sizeof (DWORD), & old); // 重置目标进程运行环境中的基址
peH -> OptionalHeader.ImageBase = (unsigned long )p;
if (WriteProcessMemory(res, p, Ptr, ImageSize, & old)) { // 复制PE数据到目标进程
Ctx.ContextFlags = CONTEXT_FULL;
if ((unsigned long )p == Addr)
Ctx.Eax = peH -> OptionalHeader.ImageBase + peH -> OptionalHeader.AddressOfEntryPoint; // 重置运行环境中的入口地址
else
Ctx.Eax = (unsigned long )p + peH -> OptionalHeader.AddressOfEntryPoint;
SetThreadContext(Thrd, & Ctx); // 更新运行环境
ResumeThread(Thrd); // 执行
CloseHandle(Thrd);
}
else { // 加载失败,杀掉外壳进程
TerminateProcess(res, 0 );
CloseHandle(Thrd);
CloseHandle(res);
res = INVALID_HANDLE_VALUE;
}
}
else { // 加载失败,杀掉外壳进程
TerminateProcess(res, 0 );
CloseHandle(Thrd);
CloseHandle(res);
res = INVALID_HANDLE_VALUE;
}
}
delete[] s;
return res;
}
/**/ /**/ /**/ /* ******************************************************\
{ ******************************************************* }
{ * 从内存中加载并运行exe * }
{ ******************************************************* }
{ * 参数: }
{ * Buffer: 内存中的exe地址 }
{ * Len: 内存中exe占用长度 }
{ * CmdParam: 命令行参数(不包含exe文件名的剩余命令行参数)}
{ * ProcessId: 返回的进程Id }
{ * 返回值: 如果成功则返回进程的Handle(ProcessHandle), }
{ 如果失败则返回INVALID_HANDLE_VALUE }
{ ******************************************************* }
\****************************************************** */
HANDLE MemExecute( void * ABuffer, long Len, char * CmdParam, unsigned long * ProcessId)
{
HANDLE res = INVALID_HANDLE_VALUE;
PIMAGE_NT_HEADERS peH;
PIMAGE_SECTION_HEADERS peSecH;
void * Ptr;
unsigned long peSz;
if (AlignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz))
{
res = AttachPE(CmdParam, peH, peSecH, Ptr, peSz, * ProcessId);
VirtualFree(Ptr, peSz, MEM_DECOMMIT);
}
return res;
}
// 初始化
class CInit
{
public :
CInit()
{
MyVirtualAllocEx = (pfVirtualAllocEx)GetProcAddress(GetModuleHandle( " Kernel32.dll " ), " VirtualAllocEx " );
}
} Init;
int main( int argc, char * argv[])
{
FILE * fp;
fp = fopen( " E:\\CProject\\DBGVIEW.EXE " , " rb " );
if ( fp )
{
fseek(fp, 0l ,SEEK_END);
int file_size = ftell(fp); /**/ /* 获取文件长度 */
fseek(fp, 0l ,SEEK_SET); /**/ /* 回到文件头部 */
LPBYTE pBuf = new BYTE[file_size];
memset( pBuf, 0 , file_size);
fread(pBuf,file_size, 1 ,fp);
DWORD id = GetCurrentProcessId();
unsigned long ulProcessId = 0 ;
MemExecute( pBuf, file_size, "" , & ulProcessId);
delete[] pBuf;
}
return 0 ;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)