.exe
的映像库,以使其在反汇编程序/调试器中不易于查看?我一直在思考在代码中的任何地方声明一个全局变量,并向后循环读取其地址,直到例如找到了
MZ
(同时正在检查NULL
)。但是,可能存在不可读的部分,.rdata
中的字符串包含MZ
字符或某些值(尤其是函数地址或一般的指针),其中包含MZ
字节(0x4D
,0x5A
)(可能还有更多) “情况”)。您知道如何实现这一目标吗?可能吗?
#1 楼
我可以想到几种方法来实现此目的从EIP扫描内存
您可以轻松地获得自己代码的
EIP
,而无需调用任何API。使用内联汇编有几种方法可以实现此目的,但是最常见的一种方法是包括以下两条指令:地址(
call
在堆栈上的位置),然后紧接其后执行指令(同样是我们的pop eax
)。当执行pop eax
时,它将pop eax
刚刚推送的地址弹出到寄存器中。 请紧记图像基与页面边界对齐,可以按
call $+5
(4096字节)的间隔进行扫描。读取加载的模块来自PEB
可使用指定的段寄存器(32位系统上的
EIP
和64位系统上的PAGE_SIZE
)访问过程环境块(也在此处)结构,该寄存器存储线程信息块的地址, PEB可达。尽管大多数PEB都是未记录的,但其中有许多与当前正在运行的流程的操作方面有关的数据。保留了流程的图像基础。如果您对fs
的映像库(与已加载的DLL映像库相反)感兴趣,可以这样做。如果您想要更可靠的东西,可以使用
gs
成员,该成员指向一个ImageBaseAddress
结构,该结构指向.exe
的链表,该链表列出了所有已加载的模块,它们的地址以及有关每个已加载模块的许多其他信息。可执行文件的映像库是Ldr
字段。#2 楼
如果使用Visual C ++,则可以使用特殊符号__ImageBase
指向当前模块的图像库。例如,这是VS2010 CRT源代码(pesect.c
)的代码:BOOL __cdecl _IsNonwritableInCurrentImage(
PBYTE pTarget
)
{
PBYTE pImageBase;
DWORD_PTR rvaTarget;
PIMAGE_SECTION_HEADER pSection;
pImageBase = (PBYTE)&__ImageBase;
__try {
//
// Make sure __ImageBase does address a PE image. This is likely an
// unnecessary check, since we should be running from a normal image,
// but it is fast, this routine is rarely called, and the normal call
// is for security purposes. If we don't have a PE image, return
// failure.
//
if (!_ValidateImageBase(pImageBase))
{
return FALSE;
}
//
// Convert the targetaddress to a Relative Virtual Address (RVA) within
// the image, and find the corresponding PE section. Return failure if
// the target address is not found within the current image.
//
rvaTarget = pTarget - pImageBase;
pSection = _FindPESection(pImageBase, rvaTarget);
if (pSection == NULL)
{
return FALSE;
}
//
// Check the section characteristics to see if the target address is
// located within a writable section, returning a failure if yes.
//
return (pSection->Characteristics & IMAGE_SCN_MEM_WRITE) == 0;
}
__except (GetExceptionCode() == STATUS_ACCESS_VIOLATION)
{
//
// Just return failure if the PE image is corrupted in any way that
// triggers an AV.
//
return FALSE;
}
}
在文件顶部,有代码将此变量声明为
extern
: br /> #if defined (_WIN64) && defined (_M_IA64)
#pragma section(".base", long, read)
__declspec(allocate(".base"))
extern IMAGE_DOS_HEADER __ImageBase;
#else /* defined (_WIN64) && defined (_M_IA64) */
extern IMAGE_DOS_HEADER __ImageBase;
#endif /* defined (_WIN64) && defined (_M_IA64) */
所以键可能是
.base
部分,而不是变量名本身。评论
+1酷!我找不到任何官方文档,最近的是:blogs.msdn.microsoft.com/oldnewthing/20041025-00/?p=37483太糟糕了,它特定于Visual C!
– NirIzr
18-10-18在21:22
#3 楼
要使用32位代码获得自己的图像库,可以执行以下操作: ds:暂时通过乘以此类推来计算“ 30h”和“ 8”。64位代码是gs:[60h]和+ 10h。
评论
可能要提及的是,这实际上是公认的答案中讨论的PEB / TEB方法。
– 0xC0000022L♦
18-10-20在22:41
不过,绝对是更切合实际的做法! +1
– NirIzr
18-10-20在22:43
评论
PEB.ImageBaseAddress比遍历Ldr结构更容易。
–乔希·波利(Josh Polary)
18-10-18在19:10
@Nirlzr非常感谢,这是个好主意!但是我不明白为什么我可以以0x1000的间隔读取eip。如果您的意思是这样的:while(*(WORD *)eip!='ZM')(DWORD)eip-= PAGE_SIZE ;,如果图像基数实际上是0x400000而eip是0x400ABC,该怎么办?我想念什么吗?
–詹森
18-10-18在19:45
@joshpoley酷。我以为一定有类似的东西,但略读时找不到。我将添加!
– NirIzr
18-10-18在19:46
@Jason您可以通过执行eip&〜(4096-1)轻松地将地址与4k边界对齐
– NirIzr
18-10-18在19:49
@Nirlzr好吧,我已经看过这种使用虚拟地址的技术,只是误解了您。
–詹森
18-10-18在19:50