查看: 87|回复: 0

[转载图文] C/C++ 内存转储与获取DLL加载

[复制链接]
发表于 2020-6-15 20:54:33 | 显示全部楼层 |阅读模式
  CREATE_PROCESS_DEBUG_EVENT 创建进程的调试事件。CREATE_PROCESS_DEBUG_INFO结构体描述了该类调试事件的详细信息
OUTPUT_DEBUG_STRING_EVENT  该事件,当被调试进程调用OutputDebugString时就会引发该类调试事件,OUTPUT_DEBUG_STRING_INFO结构体描述了关于该事件的详细信息
LOAD_DLL_DEBUG_EVENT    当DLL被加载时,会调用该回调,LOAD_DLL_DEBUG_INFO结构体描述了它的详细信息,dll的路径被放在了,hfile字段,该字段默认是句柄方式存储的,需要手工转换,
  实现简易调试器: 通过调试API实现建议调试器,可以加以改进,变成内存dump工具,等,也可以获取实际OEP作为查壳工具来用。
#include <stdio.h>
#include <Windows.h>
#include <Tlhelp32.h>
#include <imagehlp.h>
#pragma comment (lib, "Dbghelp")

BYTE bCC = '\xCC';

// 这是调试进程第一次被断下后执行操作
void OnException(DEBUG_EVENT *pDebug, BYTE *bCode)
{
CONTEXT context;
DWORD dwNum;
BYTE bTmp;

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pDebug->dwProcessId);
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pDebug->dwThreadId);

SuspendThread(hThread);
// 读取出异常首地址
ReadProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, &bTmp, sizeof(BYTE), &dwNum);

context.ContextFlags = CONTEXT_FULL;
GetThreadContext(hThread, &context);

printf("EAX = %x  EIP = %x \n", context.Eax, context.Eip);
// 将刚才的CC断点取消,也就是会写原始指令集
WriteProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, bCode, sizeof(BYTE), &dwNum);
context.Eip--;
SetThreadContext(hThread, &context);

printf("EAX = %x  EIP = %x \n", context.Eax, context.Eip);
printf("入口点: %x \n", pDebug->u.CreateProcessInfo.lpBaseOfImage);

//MemDump(pDebug, context.Eip,"c://dump.exe");   // 转储内存镜像

ResumeThread(hThread);
CloseHandle(hThread);
CloseHandle(hProcess);
}

int main(int argc, char * argv[])
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
DEBUG_EVENT de = { 0 };

// 创建调试进程
BOOL bRet = CreateProcess("c://123.exe", 0, 0, 0, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, 0, 0, &si, &pi);

if (bRet == FALSE)
return bRet;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

BYTE bCode;
DWORD dwNum;
int dwCC_Count = 0;

// 开始调试循环
while (WaitForDebugEvent(&de, INFINITE))
{
switch (de.dwDebugEventCode)
{
// 当进程创建成功后自动执行的部分
case CREATE_PROCESS_DEBUG_EVENT:
{
// 获取入口地址 0x0 可以增加偏移到入口后任意位置
DWORD dwAddr = 0x0 + (DWORD)de.u.CreateProcessInfo.lpStartAddress;
// 暂停线程
SuspendThread(de.u.CreateProcessInfo.hThread);
// 读取入口地址处的字节码
ReadProcessMemory(de.u.CreateProcessInfo.hProcess, (const void *)dwAddr, &bCode, sizeof(BYTE), &dwNum);
// 在入口地址处写入0xCC 即写入INT 3
WriteProcessMemory(de.u.CreateProcessInfo.hProcess, (void *)dwAddr, &bCC, sizeof(BYTE), &dwNum);
// 恢复线程
ResumeThread(de.u.CreateProcessInfo.hThread);
break;
}
// 当进程产生异常时自动执行这里
case EXCEPTION_DEBUG_EVENT:
{
switch (dwCC_Count)
{
// 第0次是系统断点,这里我们直接跳过
case 0: 
dwCC_Count++; break;
// 第一次断点,我们让他执行下面的函数
case 1:
OnException(&de, &bCode); dwCC_Count++; break;
}
}
}
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
}
system("pause");
return 0;
}
  内存转储 dump: memDump 配合前面的调试器附加,传入dump文件名,即可完成转储。
VOID MemDump(DEBUG_EVENT *pDe, DWORD dwEntryPoint, char *DumpFileName)
{
DWORD dwPid = pDe->dwProcessId;

MODULEENTRY32 me32;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);

me32.dwSize = sizeof(MODULEENTRY32);
BOOL bRet = Module32First(hSnap, &me32);

HANDLE hFile = CreateFile(me32.szExePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

// 判断PE文件的有效性
IMAGE_DOS_HEADER imgDos = { 0 };
IMAGE_NT_HEADERS imgNt = { 0 };

DWORD dwReadNum = 0;
ReadFile(hFile, &imgDos, sizeof(IMAGE_DOS_HEADER), &dwReadNum, NULL);

if (imgDos.e_magic != IMAGE_DOS_SIGNATURE)
return;

SetFilePointer(hFile, imgDos.e_lfanew, 0, FILE_BEGIN);
ReadFile(hFile, &imgNt, sizeof(IMAGE_NT_HEADERS), &dwReadNum, NULL);
if (imgNt.Signature != IMAGE_NT_SIGNATURE)
return;

// 得到EXE文件的大小
DWORD BaseSize = me32.modBaseSize;
if (imgNt.OptionalHeader.SizeOfImage > BaseSize)
{
BaseSize = imgNt.OptionalHeader.SizeOfImage;
}
// 分配内存并打开进程
LPVOID pBase = VirtualAlloc(NULL, BaseSize, MEM_COMMIT, PAGE_READWRITE);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);

// 读取文件的数据
bRet = ReadProcessMemory(hProcess, me32.modBaseAddr, pBase, me32.modBaseSize, NULL);

// 判断PDOS头的有效性
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBase;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE)
return;
// 计算出NT头数据
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (PBYTE)pBase);
if (pNt->Signature != IMAGE_NT_SIGNATURE)
return;

// 设置文件的入口地址
pNt->OptionalHeader.AddressOfEntryPoint = dwEntryPoint - pNt->OptionalHeader.ImageBase;
// 设置文件的对齐方式
pNt->OptionalHeader.FileAlignment = 0x1000;

// 找到节区首地址,并循环将当前节区数据赋值到新文件缓存中
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PBYTE)&pNt->OptionalHeader + pNt->FileHeader.SizeOfOptionalHeader);
for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
pSec->PointerToRawData = pSec->VirtualAddress;
pSec->SizeOfRawData = pSec->Misc.VirtualSize;
pSec++;
}
CloseHandle(hFile);

hFile = CreateFile(DumpFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
DWORD dwWriteNum = 0;

// 将读取的数据写入到文件
bRet = WriteFile(hFile, pBase, me32.modBaseSize, &dwWriteNum, NULL);
CloseHandle(hFile);
VirtualFree(pBase, me32.modBaseSize, MEM_RELEASE);
CloseHandle(hProcess);
CloseHandle(hSnap);
}
  获取DLL加载情况:
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#include <psapi.h>

void OnDllLoaded(const LOAD_DLL_DEBUG_INFO *pDebug)
{
printf("基址: 0x%-8X --> ", pDebug->lpBaseOfDll);

BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH + 1];
HANDLE hFileMap;

// Get the file size.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(pDebug->hFile, &dwFileSizeHi);

printf("长度: %9d --> ", dwFileSizeLo);

if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
{
return;
}
// 创建内存映射
hFileMap = CreateFileMapping(pDebug->hFile, 0, PAGE_READONLY, 0, 1, 0);

if (hFileMap)
{
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
if (GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH))
{
printf("路径: %s \n", pszFilename);
}
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
}

int main(int argc, char * argv[])
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
DEBUG_EVENT debug_event = { 0 };

// 创建调试进程
BOOL bRet = CreateProcess("C:/Program Files/Tencent/QQ/Bin/QQ.exe", 0, 0, 0, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, 0, 0, &si, &pi);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

// 开始调试循环
while (WaitForDebugEvent(&debug_event, INFINITE))
{
switch (debug_event.dwDebugEventCode)
{
// 当DLL加载到进程时自动的执行此处代码
case LOAD_DLL_DEBUG_EVENT:
OnDllLoaded(&debug_event.u.LoadDll);
break;
}
ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, DBG_CONTINUE);
}
return 0;
}


温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的最好奖励,还可以获得学币奖励,请尊重作者的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046
微信公众号
快速回复 返回顶部 返回列表