简介
代码注入在分析程序或制作外挂外挂时优劣常爱使用的一种本领, 通常用于调用程序功能, 好比调用程序中的call;其基本原理和DLL注入的原理一样, 目前已经有许多工具可以直接实当代码注入或DLL注入了, 本文只是学习一下原理....
思绪
思绪很简朴, 基本就两大步:
- OpenProcess打开需要注入的程序, 获取句柄;
- 通过CreateRemoteThread函数将我们需要注入的代码以新的线程的方式进行运行, 到达注入的效果;
通过PEB获取模块基址
通常我们注入的代码一般都是汇编, 假如注入是很复杂的代码, 我们通常将代码写到DLL中, 直接用DLL注入了;
所以为了使我们的汇编根据有健壮性, 这里说一下如何用汇编获取程序模块的基地址, 注入的代码就相当于是写shellcode;
PEB
在fs:[0x30]地址处保存着一个指针, 指向了PEB布局, 布局基本如下:
typedef struct _PEB { // Size: 0x1D8/*000*/ UCHAR InheritedAddressSpace;/*001*/ UCHAR ReadImageFileExecOptions;/*002*/ UCHAR BeingDebugged;/*003*/ UCHAR SpareBool; // Allocation size/*004*/ HANDLE Mutant;/*008*/ HINSTANCE ImageBaseAddress; // Instance/*00C*/ VOID *DllList; //_PEB_LDR_DATA ;进程加载的模块链表/*010*/ PPROCESS_PARAMETERS *ProcessParameters;/*014*/ ULONG SubSystemData;/*018*/ HANDLE DefaultHeap;/*01C*/ KSPIN_LOCK FastPebLock;/*020*/ ULONG FastPebLockRoutine;/*024*/ ULONG FastPebUnlockRoutine;/*028*/ ULONG EnvironmentUpdateCount;/*02C*/ ULONG KernelCallbackTable;/*030*/ LARGE_INTEGER SystemReserved;/*038*/ ULONG FreeList;/*03C*/ ULONG TlsExpansionCounter;/*040*/ ULONG TlsBitmap;/*044*/ LARGE_INTEGER TlsBitmapBits;/*04C*/ ULONG ReadOnlySharedMemoryBase;/*050*/ ULONG ReadOnlySharedMemoryHeap;/*054*/ ULONG ReadOnlyStaticServerData;/*058*/ ULONG AnsiCodePageData;/*05C*/ ULONG OemCodePageData;/*060*/ ULONG UnicodeCaseTableData;/*064*/ ULONG NumberOfProcessors;/*068*/ LARGE_INTEGER NtGlobalFlag;/*070*/ LARGE_INTEGER CriticalSectionTimeout;/*078*/ ULONG HeapSegmentReserve;/*07C*/ ULONG HeapSegmentCommit;/*080*/ ULONG HeapDeCommitTotalFreeThreshold;/*084*/ ULONG HeapDeCommitFreeBlockThreshold;/*088*/ ULONG NumberOfHeaps;/*08C*/ ULONG MaximumNumberOfHeaps;/*090*/ ULONG ProcessHeaps;/*094*/ ULONG GdiSharedHandleTable;/*098*/ ULONG ProcessStarterHelper;/*09C*/ ULONG GdiDCAttributeList;/*0A0*/ KSPIN_LOCK LoaderLock;/*0A4*/ ULONG OSMajorVersion;/*0A8*/ ULONG OSMinorVersion;/*0AC*/ USHORT OSBuildNumber;/*0AE*/ USHORT OSCSDVersion;/*0B0*/ ULONG OSPlatformId;/*0B4*/ ULONG ImageSubsystem;/*0B8*/ ULONG ImageSubsystemMajorVersion;/*0BC*/ ULONG ImageSubsystemMinorVersion;/*0C0*/ ULONG ImageProcessAffinityMask;/*0C4*/ ULONG GdiHandleBuffer[0x22];/*14C*/ ULONG PostProcessInitRoutine;/*150*/ ULONG TlsExpansionBitmap;/*154*/ UCHAR TlsExpansionBitmapBits[0x80];/*1D4*/ ULONG SessionId;} PEB, *PPEB;PEB布局的偏移0xc处保存着另外一个指针ldr,该指针为PEB_LDR_DATA:
PEB_LDR_DATA
typedef struct _PEB_LDR_DATA{ ULONG Length; // +0x00 BOOLEAN Initialized; // +0x04 PVOID SsHandle; // +0x08 LIST_ENTRY InLoadOrderModuleList; // +0x0c LIST_ENTRY InMemoryOrderModuleList; // +0x14 LIST_ENTRY InInitializationOrderModuleList;// +0x1c} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24PEB_LDR_DATA布局的后三个成员是指向LDR_MODULE链表布局中相应三条双向链表头的指针, 分别是按照加载序次, 在内存中地址序次和初始化序次分列的模块信息布局的指针, 其中LDR_MODULE布局就是_LDR_DATA_TABLE_ENTRY布局; 而链表的第一个就保存了当前程序的基地址;
_LDR_DATA_TABLE_ENTRY布局
typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; // +0x00 LIST_ENTRY InMemoryOrderLinks; // +0x04 LIST_ENTRY InInitializationOrderLinks; // +0x08 PVOID DllBase; // +0x0c PVOID EntryPoint; // +0x10 DWORD SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; DWORD Flags; WORD LoadCount; WORD TlsIndex; LIST_ENTRY HashLinks; PVOID SectionPointer; DWORD CheckSum; DWORD TimeDateStamp; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; }LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY; 其中偏移为0x10的位置的EntryPoint就保存了模块的基地址;
综上所述, 我们可以利用一下这段汇编代码获取程序的基地址:
mov eax, fs:[0x30]; // PEB mov ebx, [eax + 0xc]; // PEB_LDR_DATA mov eax, [ebx + 0x14]; // InMemoryOrderModuleList mov ebx, [eax + 0x10]; // ebx = EntryPoint(基址)假如在DLL当中获取程序基地址, 可以使用下面的代码:
void Get_addr(DWORD pro_id){ HANDLE hpro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pro_id); if (hpro == 0){ printf("无法获取进程句柄"); } printf("进程句柄id: %d\n",hpro); // 获取每一个模块加载基址 DWORD pro_base = NULL; HMODULE hModule[100] = {0}; DWORD dwRet = 0; int num = 0; int bRet = EnumProcessModulesEx(hpro, (HMODULE *)(hModule), sizeof(hModule),&dwRet,NULL); if (bRet == 0){ printf("EnumProcessModules"); } // 总模块个数 num = dwRet/sizeof(HMODULE); printf("总模块个数: %d\n",num); // 打印每一个模块加载基址 char lpBaseName[100]; for(int i = 0;i |