【VC】内存注入
本帖最后由 舒默哦 于 2020-5-23 22:52 编辑https://static.52pojie.cn/static/image/hrline/4.gif
内存注入的步骤
1、以二进制的方式把dll读到内存
2、拉伸dll
3、在要注入的目标进程申请内存
4、修复重定位和IAT表
5、得到dll在目标进程的入口
6、构造shellcode
7、创建远程线程
注:构造shellcode的目的是为了传参,因为CreateRemoteThread这个函数传参用的是一个地址,而dll的入口要传三个参数,所以只能构造shellcode来跳转
https://static.52pojie.cn/static/image/hrline/1.gif
实验用的test_dll.dll只有一个MessageBox(NULL, "注入成功", "恭喜", MB_OK);
要注入的程序是ipmsg.exe,其他程序也一样,只要该程序的IAT表有MessageBoxA就可以,不然,注入成功了信息框不能弹出
编译用的是vs2017字集是多字符集
test_dll.dll
BOOL APIENTRY DllMain(HMODULE hModule,
DWORDul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
MessageBox(NULL, "注入成功", "恭喜", MB_OK);
}
case DLL_THREAD_ATTACH:
//MessageBox(NULL, "DLL_THREAD_ATTACH", "", MB_OK);
case DLL_THREAD_DETACH:
//MessageBox(NULL, "DLL_THREAD_DETACH", "", MB_OK);
case DLL_PROCESS_DETACH:
//MessageBox(NULL, "DLL_PROCESS_DETACH", "", MB_OK);
break;
}
return TRUE;
}
main.cpp
#include <iostream>
#include <Windows.h>
#include "resource.h"
#define path_dll "C:\\Users\\daddy\\Desktop\\软件代码区\\dll库\\test_dll\\Release\\test_dll.dll"
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory
#ifdef _WIN64
#define POINTER_TYPE ULONGLONG
#else
#define POINTER_TYPE DWORD
#endif
//获取文件的在内存的地址和大小
int GetBuffAddrAndSizeofFile(char* FilePath, _Out_DWORD* buff, _Out_ DWORD* FileSize, _Out_ DWORD* FileSize1);
//修复重定位表
void PerformBaseRelocation(char* buff, DWORD Value);
//重建IAT表
BOOL RebuildImportTable(char* buff);
//拉伸文件
DWORD StretchFile(DWORD pFileBuff, DWORD FileSize);
//内存注入
void MemoryInject(DWORD dwPID)
{
//1.把dll文件加载到内存
DWORD pFileBuf_dll = 0;//dll在内存中的地址
DWORD FileSize_dll = 0;//dll的内存大小
DWORD FileSize_dll_1 = 0;//dll的文件大小
if (GetBuffAddrAndSizeofFile((char*)path_dll, &pFileBuf_dll, &FileSize_dll, &FileSize_dll_1) == 0)
{
return;
}
//2.拉伸dll
DWORD NewBuff = StretchFile(pFileBuf_dll, FileSize_dll);
//释放pFileBuf_dll
delete[](char*)pFileBuf_dll;
//获取PE头和NT头
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)NewBuff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)NewBuff + pDosHeader->e_lfanew);
//3.0打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID);
//3.1在目标进程申请内存
LPBYTE lpbPeAddress = (LPBYTE)VirtualAllocEx(hProcess, NULL, FileSize_dll, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == lpbPeAddress)
{
printf("在目标进程申请内存失败!");
return;
}
//4.0 修复重定位表
DWORD locationDelta = (SIZE_T)((DWORD)lpbPeAddress - pNtHeader->OptionalHeader.ImageBase);
if (0 != locationDelta)
{
PerformBaseRelocation((char*)NewBuff, locationDelta);
}
//4.1 修复IAT表
if (!RebuildImportTable((char*)NewBuff))
{
printf("修复失败!");
return;
}
//把修复后的dll数据拷贝到目标进程内存
BOOL res = WriteProcessMemory(hProcess, lpbPeAddress, (LPCVOID)NewBuff, FileSize_dll, 0);
if (0 == res)
{
printf("在目标进程写入失败!");
VirtualFreeEx(hProcess, lpbPeAddress, FileSize_dll, MEM_RELEASE);
CloseHandle(hProcess);
return;
}
//5.找到dll在目标进程的入口
DWORD EnterPointer = (DWORD)lpbPeAddress + pNtHeader->OptionalHeader.AddressOfEntryPoint;
//dll入口参数结构体
typedef struct MyStruct
{
HMODULE hModule;
DWORDul_reason_for_call;
LPVOID lpReserved;
}ParamCall;
ParamCall paramcall;
paramcall.hModule = (HMODULE)lpbPeAddress;//DLL模块在进程虚拟空间中的起始地址
paramcall.ul_reason_for_call = DLL_PROCESS_ATTACH;
paramcall.lpReserved = 0;
//在目标进程申请内存
LPBYTE shlcodeaddr = (LPBYTE)VirtualAllocEx(hProcess, NULL, 0x40, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//E8 XXXXXXXX = 要跳转的地址 - E8指令所在的地址 - 5
DWORD jmp_addr = EnterPointer - ((DWORD)shlcodeaddr + 15) - 5;
//6.构造shellcode
char shellcode[] = {
0x68,
((DWORD)paramcall.lpReserved)&0xFF, (((DWORD)paramcall.lpReserved) & 0xFF00) >> 8,(((DWORD)paramcall.lpReserved) & 0xFF0000) >> 16,((DWORD)paramcall.lpReserved) >> 24,
0x68,
(paramcall.ul_reason_for_call) & 0xFF, ((paramcall.ul_reason_for_call) & 0xFF00) >> 8,((paramcall.ul_reason_for_call) & 0xFF0000) >> 16,(paramcall.ul_reason_for_call) >> 24,
//0x6A,0x0,0x6A,0x1,
0x68,
((DWORD)paramcall.hModule) & 0xFF, (((DWORD)paramcall.hModule) & 0xFF00) >> 8,(((DWORD)paramcall.hModule) & 0xFF0000) >> 16,((DWORD)paramcall.hModule) >> 24,
0xE8,
(jmp_addr) & 0xFF, ((jmp_addr) & 0xFF00) >> 8,((jmp_addr) & 0xFF0000) >> 16,(jmp_addr) >> 24,
0xC3
};
if (!WriteProcessMemory(hProcess, shlcodeaddr, (LPCVOID)shellcode, sizeof(shellcode), NULL))
{
MessageBox(NULL,"Write trueAddress failed","",MB_OK);
return;
}
//7.开启远程线程
DWORD threadId;
HANDLE hThead;
hThead = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)shlcodeaddr, 0, 0, &threadId);
WaitForSingleObject(hThead, -1);
CloseHandle(hThead);
//释放NewBuff
delete[](char*)NewBuff;
}
int main()
{
DWORD pid=0;
printf("请输入进程ID: ");
scanf_s("%d", &pid);
MemoryInject(pid);
std::cout << "Hello World!\n";
}
//修复重定位表
void PerformBaseRelocation(char* buff, DWORD Value)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(buff + pDosHeader->e_lfanew);
//获取目录表头指针
PIMAGE_DATA_DIRECTORY pDataDirectory = pNtHeader->OptionalHeader.DataDirectory;
if (pDataDirectory.Size > 0)
{
PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)((DWORD)buff + pDataDirectory.VirtualAddress);
while (relocation->VirtualAddress > 0)
{
BYTE* dest = (PBYTE)((DWORD)buff + relocation->VirtualAddress);
WORD* relInfo = (PWORD)((DWORD)relocation + sizeof(IMAGE_BASE_RELOCATION));
for (int i = 0; i < ((relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2); ++i, ++relInfo)
{
DWORD *patchAddrHL;
int type, offset;
//the upper 4 bits define the type of relocation
type = *relInfo >> 12;
//the lower 12 bits define the offset
offset = *relInfo & 0xFFF;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
//skip relocation
break;
case IMAGE_REL_BASED_HIGHLOW:
//change comlete 32 bit address
patchAddrHL = (PDWORD)(dest + offset);
*patchAddrHL += Value;
break;
default:
break;
}
}
//advance to next relocation block
relocation = PIMAGE_BASE_RELOCATION((DWORD)relocation + relocation->SizeOfBlock);
}
}
}
//重建IAT表
BOOL RebuildImportTable(char* buff)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(buff + pDosHeader->e_lfanew);
int result = 1;
//获取目录表头指针
PIMAGE_DATA_DIRECTORY pDataDirectory = pNtHeader->OptionalHeader.DataDirectory;
if (pDataDirectory.Size > 0)
{
//获取导入表地址
PIMAGE_IMPORT_DESCRIPTOR ImportAddr = PIMAGE_IMPORT_DESCRIPTOR(pDataDirectory.VirtualAddress + (DWORD)buff);
for (; !IsBadReadPtr(ImportAddr, sizeof(PIMAGE_IMPORT_DESCRIPTOR)) && ImportAddr->Name; ++ImportAddr)
{
POINTER_TYPE *thunkRef;
FARPROC *funcRef;
HMODULE hModule = LoadLibrary((LPCSTR)(buff + ImportAddr->Name));
if (ImportAddr->OriginalFirstThunk)
{
thunkRef = (POINTER_TYPE *)(buff + ImportAddr->OriginalFirstThunk);
funcRef = (FARPROC*)(buff + ImportAddr->FirstThunk);
}
else
{
//no hint table
thunkRef = (POINTER_TYPE *)(buff + ImportAddr->FirstThunk);
funcRef = (FARPROC*)(buff + ImportAddr->FirstThunk);
}
for (; *thunkRef; ++thunkRef, ++funcRef)
{
if (IMAGE_SNAP_BY_ORDINAL(*thunkRef))
{
*funcRef = GetProcAddress(hModule, (LPCSTR)IMAGE_ORDINAL(*thunkRef));
}
else
{
PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)(*thunkRef + (DWORD)buff);
*funcRef = GetProcAddress(hModule, (LPCSTR)&pFuncName->Name);
}
if (*funcRef == 0)
{
result = 0;
break;
}
}
}
}
return result;
}
//获取文件的在内存的地址和大小
int GetBuffAddrAndSizeofFile(char* FilePath, _Out_ DWORD* buff, _Out_ DWORD* FileSize, _Out_ DWORD* FileSize1)
{
//1.1 获取文件句柄
HANDLE hFile = CreateFile(
FilePath,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//1.2 获取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
CHAR *pFileBuf = new CHAR;
memset(pFileBuf, 0, dwFileSize);
//1.3 将文件读取到内存
DWORD ReadSize = 0;
ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL);
//1.4 关闭句柄
CloseHandle(hFile);
//2.1 判断是否为PE文件
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("不是MZ开头\n");
return 0;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是PE文件\n");
return 0;
}
//2.2 把文件的在内存中的地址和大小分别赋值给参数二和参数三
*buff = (DWORD)pFileBuf;
*FileSize = pNtHeader->OptionalHeader.SizeOfImage;
*FileSize1 = dwFileSize;
return 1;
}
//拉伸文件
DWORD StretchFile(DWORD pFileBuff, DWORD FileSize)
{
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuff;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuff + pDosHeader->e_lfanew);
//1.1 根据内存大小 申请内存
char* NewFileBuff = new char;
if (NewFileBuff == NULL)
{
printf("内存申请失败!");
return 0;
}
memset(NewFileBuff, 0, FileSize);
//1.2 拉伸文件
// 拷贝DOS头 + DOS STUB + PE头到headers地址处
memcpy(NewFileBuff, pDosHeader, pNtHeader->OptionalHeader.SizeOfHeaders);
// 从dll文件内容中拷贝每个section(节)的数据到新的内存区域
PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuff + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)OptionalHeader + pNtHeader->FileHeader.SizeOfOptionalHeader);
for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++, pSectionHeader++)
{
char* x = (char*)NewFileBuff + pSectionHeader->VirtualAddress;
char* y = (char*)pFileBuff + pSectionHeader->PointerToRawData;
memcpy(x, y, pSectionHeader->SizeOfRawData);
}
return (DWORD)NewFileBuff;
}
舒默哦 发表于 2020-11-24 22:55
可能游戏有检测,用OD跟一下
不是哦自己写的程序 内存注入都卡死然后闪退 不知道什么问题 {:301_1001:}. 感觉可以用作外挂~ 好东西,谢谢分享 不懂 太难了{:1_909:}
舒默哦 发表于 2020-5-19 23:30
我服了,东西都到嘴边了还不会,手把手我过段时间再写详细点吧
{:301_971:}原谅我是个菜鸡。刚刚在把我另一个求助帖子看了看,我自己解决了我自己的问题。{:301_998:} 希望有个手把手教程,哈哈,热心和评分都给楼主了~ 最后在抹掉 PE头, 然后暴力搜索内存也不行了!
还可以用在反破解, OD载入进去,你也会发现无法保存文件,对于新手,那就是无解!!!!!!!!!!!!!!! hj170520 发表于 2020-5-19 23:19
. 感觉可以用作外挂~
给我投币老铁,:$qqq明天更新文件永久注入 舒默哦 发表于 2020-5-19 23:23
给我投币老铁,明天更新文件永久注入
{:301_999:} 老哥,今天我币投了~明天吧!
我也不会用这玩意~,有没有手把手教的教程{:301_998:} hj170520 发表于 2020-5-19 23:28
老哥,今天我币投了~明天吧!
我也不会用这玩意~,有没有手把手教的教程
我服了,东西都到嘴边了还不会,手把手我过段时间再写详细点吧 学到了,来打卡 shellcode应该如何写呢 看不太懂 boy7928 发表于 2020-5-20 00:02
shellcode应该如何写呢 看不太懂
push xxxxx
push xxxxx
push xxxxx
call xxxxx
ret