有什么方法可以在.exe
不调用 WinAPI 函数(即导入的函数)的情况下获取图像库,以便在反汇编器/调试器中无法轻松查看?
我一直在考虑在代码中的任何位置声明一个全局变量,并在循环中向后读取其地址,直到MZ
找到eg (显然NULL
在此期间进行检查)。但是,可能存在不可读的部分,.rdata
其中包含MZ
包含MZ
字节 ( 0x4D
, 0x5A
)的字符或某些值(尤其是函数地址或通常的指针)的字符串(可能还有一些“情况”)。
你知道如何实现这一目标吗?甚至有可能吗?
有什么方法可以在.exe
不调用 WinAPI 函数(即导入的函数)的情况下获取图像库,以便在反汇编器/调试器中无法轻松查看?
我一直在考虑在代码中的任何位置声明一个全局变量,并在循环中向后读取其地址,直到MZ
找到eg (显然NULL
在此期间进行检查)。但是,可能存在不可读的部分,.rdata
其中包含MZ
包含MZ
字节 ( 0x4D
, 0x5A
)的字符或某些值(尤其是函数地址或通常的指针)的字符串(可能还有一些“情况”)。
你知道如何实现这一目标吗?甚至有可能吗?
我可以想到几种方法来做到这一点
您EIP
无需调用任何 API即可轻松获取自己的代码。使用内联汇编有几种方法可以实现这一点,但最常见的一种是包含以下两条指令:
call $+5
pop eax
这是有效的,因为call
将推送下一个地址(在pop eax
堆栈上的位置),然后在它之后立即执行指令(再次,我们的pop eax
)。当pop eax
被执行时,它会弹出只是推地址call $+5
到寄存器。
获得EIP
价值后,您可以按照自己的想法轻松地向后扫描,但这一次从离您的图像库更近的位置开始。
请记住,图像库与页面边界对齐,您可以按PAGE_SIZE
(4096 字节)间隔进行扫描。
的进程环境块(也此处)结构是可访问的使用指定的段寄存器(fs
在32位的系统和gs
在64位系统),其存储所述地址线程信息块从该PEB可达。尽管大部分 PEB 都没有记录,但其中包含大量与当前正在运行的流程的操作方面相关的数据。
其中一条信息是ImageBaseAddress
,一个未记录(但一致)的字段,用于保存进程的映像库。如果您对.exe
的映像库(与加载的 DLL 映像库相对)感兴趣,则可以这样做。
如果你想要更可靠的东西,你可以使用Ldr
成员,它指向一个PEB_LDR_DATA
指向LDR_DATA_TABLE_ENTRY
s链表的结构,它列出了所有加载的模块、它们的地址以及关于每个加载模块的许多其他信息。您的可执行文件的映像库是该DllBase
字段。
如果您使用 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
:
#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
部分而不是变量名本身。
要在 32 位代码中获得自己的图像库,您可以执行以下操作:
mov eax, fs:[30h]
mov eax, [eax+8]
您显然可以通过多种方式进行混淆,例如通过将 fs: 移动到 ds: 临时,通过乘法计算“30h”和“8”等。
64 位代码是 gs:[60h] 和 +10h。