Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

admin 2025年2月25日19:18:31评论12 views字数 17214阅读57分22秒阅读模式

写这篇文章的目的是想帮助刚开始或者准备开始研究windows系统的人员,此篇文章仅作为经验分享,是我在逆向还原windows10堆APICreateHeapAllocateHeap以及AllocateHeapInternal源代码时总结的一些经验,如有错误请各位前辈斧正。我们仅分享其windows10 ntdll中常见的一些汇编特征优化,如若不涉及的优化请各位自行翻阅资料学习。

环境

分析环境: Windows11
工具: Windbg, IDA7.5
分析目标平台: Windows10
分析目标文件: C:WindowsSysWow64ntdll.dll(32位)
版本信息:10.0.19041.5007 (WinBuild.160101.0800)
SHA-1:9C3A55D17C022D7B32EE558E8941C4C9938696CA

0

常见Release版优化梳理

对于常见的Release版编译的优化此篇文章涉及到的有

CPU流水线优化

真正的CPU流水线优化有许多概念且十分复杂,但是体现在代码中的我们只需要关注一些会影响我们分析的内容,比如乱序执行我们以最经典的三级流水线为例举出例子方便大家简易回顾一下流水线优化。如下三个操作分别由三个不同的CPU组件同步执行。

● 取指令
● 译码
● 执行

在CPU中形如如下流程,但是流水线优化只会优化无相关关联的代码譬如:mov eax, imm32 mov ebx, imm32 等,而如果出现一个指令序列其中的前后代码相关联则会破坏该处的流水线优化,对于3级流水线来讲最好情况是3行代码互相无关。

下面我们给出相关图例辅助理解

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

一旦出现的一组代码(A组)可以组成流水线优化则意味着该组代码A的执行顺序与组内其他代码无关,且改组代码也有可能与其他组代码无关,此时编译器有可能将该组代码提前(延后)到某组代码(B组)后执行且不会影响程序的逻辑,最简单的情况就是__cdecl后的调用方的平栈代码add esp, imm在ntdll中如果出现调用函数为__cdecl就有可能出现这种优化可能会在后面的代码中穿插一条平栈代码,在还原代码时我们只需要跳过即可。

加法优化(比例因子优化)

对于加法通常我们能看到利用lea指令进行优化,譬如eax + eax + 1在优化版中会出现形如lea eax, [eax+eax+1]等优化类型但是在此次还原中出现较少。

1

临时变量优化

在逆向的过程中我们经常能看到如下片段

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

对于这种情况我们在初次见到时如果发现前面的esi原先为某个变量值但是被赋值覆盖掉了原值,然后中间经过一系列操作运算传入了某个函数或者放入了某个变量,则有可能是编译过程编译器将临时变量优化掉了导致的譬如如下C代码。

DWORD add(DWORD a, DWORD b)
{
DWORD dwRet = a + b;
return dwRet;
}

在这种情况下就有可能将中间代码dwRet当作临时变量优化掉变成如下汇编形式

mov eax, [ebp + a]
add eax, [ebp + b]

当我们出现如下情况时考虑出现了临时变量优化尽量看此寄存器是否用作他用,如果是传参则可以还原为传入参数写表达式,如果是变量赋值则可以写作将表达式赋值为变量。

2

0值寄存器传递

对于0值寄存器传递是我在逆向CreateHeap API的时候发现的,

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

这里的EBX寄存器在整个函数中都被当作0值进行赋值操作,譬如在返回失败空值的时候对返回值esi的赋0处理。

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

3

函数调用约定优化

对于这个应该也属于是常见优化里面的但是笔者还是提出来单独分析一下,对于大家习惯用的ida如果F5之后发现函数参数不对多半就是因为编译器将原本的__stdcall优化为__fastcall譬如

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

对于见到在调用前给EDX和ECX等赋值的行为我们需要进入其调用的函数中查看

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

如果被调用的函数中直接使用了EDX或者ECX寄存器则考虑将该函数翻译为__stdcall或者照例翻译为__fastcall具体情况应该视可读性和可维护性而定。

4

if特征与平坦化优化(if反转)

在讨论这个汇编特征之前我们先看一下VS2019对if语句的优化如下

; if (a >= 0)
; {
;printf("hello world");
;}
mov edx, [a]
test edx, edx
jns xxxx ; 这里会取反
; 这里则是语句块中的代码
push str_xxx ;HelloWorld
call subxxxxx;printf
add esp, 8; 平栈
xxxx:
;这里一般为后面的代码

图示

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

在正常的VS2019编译器中Release将会把汇编优化为取反跳的形式但是在ntdll中如果出现了跳转分支在平坦流片段中的情况则需要翻译为条件不取反如果跳转后的平坦流片段中紧接判断还可以考虑将其整合翻译为条件表达式 + if语句的形式

在还原过程中还有一种情况就是条件表达式融合到平坦流中,在这种情况下同样需要翻译为条件不去反然后将平坦流中的代码翻译到条件语句块中,一般这种情况会经历两次跳转,一次是跳入另一次是在平坦流跳出,如果有else语句则有可能跳出到出口前面。

我们来看下面的例子,首先是外层

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

然后是内层

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

在ntdll中我们会遇到大量这种形式的代码,唯独在较为短小的条件判断语句中能看到正常取反跳的优化身影,我们需要多往后看看比对跳转的汇编标签是否和平坦流内部的跳转相同,辅以上下文依据可读性进行判断。

5

push pop寄存器赋

对于这种情况多半是寄存器不够用了,这种情况后面多半会接一个call或者较为复杂的比较逻辑,如下示例。

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

总结

对于windows的逆向需要熟悉其内部编译器的汇编代码优化特征才能更好的进行还原,我仅PO出我逆向还原的CreateHeap API代码,而AllocateHeap等其他API代码就不发了。注意:此代码仅作学习用途,且由于是项目原因,所以我给出粗糙版的代码,某些结构体就不发出来了(偏移还在),如果各位感兴趣可以自己进行逆向 还原

typedef struct _SYSTEM_BASIC_INFORMATION {
ULONG Reserved; // 保留字段,通常为 0
ULONG TimerResolution; // 系统定时器的分辨率(以微秒为单位)
ULONG PageSize; // 页面大小(以字节为单位)
ULONG NumberOfPhysicalPages; // 物理页面的数量
ULONG LowestPhysicalPageNumber; // 物理页面的最低编号
ULONG HighestPhysicalPageNumber; // 物理页面的最高编号
ULONG AllocationGranularity; // 内存分配粒度
ULONG MinimumUserModeAddress; // 用户模式下的最小地址
ULONG MaximumUserModeAddress; // 用户模式下的最大地址
ULONG ActiveProcessorsAffinityMask; // 活动处理器的亲和性掩码
UCHAR NumberOfProcessors; // 处理器的数量
} SYSTEM_BASIC_INFORMATION;



/*
* Flags : 指定堆的可选属性的标志。
这些选项会影响通过调用堆函数(RtlAllocateHeap和RtlFreeHeap)对新堆的后续访问。
一共有3个值
1. HEAP_GENERATE_EXCEPTIONS 指定系统引发异常而不是通过返回空值即异常堆
2. HEAP_GROWABLE 可增长堆如果HeapBase为空必须指定
3. HEAP_NO_SERIALIZE 指定当堆函数从此堆分配和释放内存时不使用互斥。
当未指定 HEAP_NO_SERIALIZE 时,默认是序列化对堆的访问。
序列化堆访问允许两个或多个线程同时从同一堆分配和释放内存。
* HeapBase: 非空情况下就是将指定分配的地址,如果空的情况下则会在进程空间随机分配
* ReserveSize:
*
*
*/


ULONG g_initVar1ByInitalizeProccess = 0;
ULONG g_initVar2ByAvrfLoadDll = 0;
typedef int(__thiscall* PFN_4B3A32F4)(DWORD, DWORD, DWORD, DWORD, DWORD, PVOID);

PFN_4B3A32F4 g_4B3A32F4;


PVOID
RtlCreateHeap(
[in] ULONG Flags,
[in, optional] PVOID HeapBase,
[in, optional] SIZE_T ReserveSize,
[in, optional] SIZE_T CommitSize,
[in, optional] PVOID Lock,
[in, optional] PRTL_HEAP_PARAMETERS Parameters
)

{
DWORD var_12C;
DWORD var_120;
DWORD var_110[10];
PVOID pLock = Lock;
ULONG ulFlags = Flags;
PVOID pBase = HeapBase;
PVOID pBase2 = HeapBase;
SIZE_T stReserver = ReserveSize;
DWORD var_BC;
SIZE_T stCommit = CommitSize;
PVOID pLock2 = Lock;
DWORD var_CC;
DWORD CriticalSectionFlag;
DWORD var_D8;
DWORD BaseAddress;
BOOL ntGlobalFlag = ((PPEB)__readfsdword(0x30))->NtGlobalFlag;
ULONG ulUnknow1 = 0;
DWORD dwRegionSize = 0;
ULONG ulUnknow2 = 0;
ULONG ulUnknow3 = 0;
DWORD pHeapHandle2;
BYTE var_A8[0x30];
DWORD dwCommiteSize;

SYSTEM_BASIC_INFORMATION SystemInformation;
DWORD var_58;
CPPEH_RECORD ms_exc;
// 模拟组
DWORD eax = 0;
DWORD ecx = 0;
DWORD edx = (DWORD)Flags;
DWORD ebx = 0;
DWORD esi = 0;
DWORD edi = (DWORD)Parameters;

// 进程默认堆
if (g_initVar1ByInitalizeProccess != NULL
&& pBase == NULL
&& pLock == NULL)
{
// 不允许应用程序更改策略
RtlpHpAppCompatDontChangePolicy();
// 如果用户设置了Commit指针则通过Commit指针来获取堆结果
esi = g_4B3A32F4(ulFlags, 0, stReserver, stCommit, 0, Parameters);
if (esi != 0)
{
goto RELEASE_SRC;
}
if (edi != 0xFFFFFFFF)
{
edi = (DWORD)pBase;
esi = 0;
goto RELEASE_SRC;
}
}
else
{
if (g_initVar2ByAvrfLoadDll != 0
&& (DWORD)Parameters == 1)
{
/*
mov eax, edx
and eax, 100h ; if ((eax & 100h) != 0)
; eax = 100h
; if ((eax & 100h) == 0)
; eax = 0
neg eax ; if ((eax & 100h) != 0)
; eax = 100h -> FFFF FEFF
; if ((eax & 100h) == 0)
; eax = 0 -> 0
sbb eax, eax ; if ((eax & 100h) != 0)
; eax = 100h -> FFFF FEFF -> -1
; if ((eax & 100h) == 0)
; eax = 0 -> 0 -> 0
not eax ; if ((eax & 100h) != 0)
; eax = 100h -> FFFF FEFF -> -1 -> 0
; if ((eax & 100h) == 0)
; eax = 0 -> 0 -> 0 -> -1
and edi, eax ; if ((eax & 100h) != 0)
; eax = 100h -> FFFF FEFF -> -1 -> 0 -> 0
; if ((eax & 100h) == 0)
; eax = 0 -> 0 -> 0 -> -1 -> edi
*/

edi = (edx & 100) == 0 ? 0 : edi;

}

}
//edx = edx & 0xF1FFFFFF;
ulFlags = ulFlags & 0xF1FFFFFF;

if ((ulFlags & 0x100) != 0)
{
if (!(LOWORD(ulFlags) & 2)
|| (DWORD)pBase != ebx
|| ecx
|| stCommit != ebx
|| (DWORD)pLock != ebx)
{
goto RELEASE_SRC;
}

if (edi == 0xFFFFFFFF)
{
edi = !g_initVar2ByAvrfLoadDll ? 0 : edi;
}
if (edi != 0)
{
esi = edi;
// 参数的验证
if (!RtlpHpParametersVerify(edi))
{
// goto

}
edx = ulFlags;
}
else
{
esi = pHeapHandle2;
}

}
else if (_RtlpHpHeapFeatures != 1)
{
if (((((LOWORD(edx) & 2)
&& (DWORD)pBase == ebx))
// 判断堆参数是否支持
&& RtlpHpParametersSupported(edi))
|| edi == 0
)
{
eax = 2;
if ((DWORD)pLock2 == ebx)
{
esi = (DWORD)&pHeapHandle2;
}

}
}

if (esi != 0)
{
eax = (DWORD)&pHeapHandle2;
if (esi == eax)
{
memset((PBYTE)esi, 0, 0x30u);
*(WORD*)esi = 2;
*(WORD*)((BYTE*)esi + 2) = 0x30;
*(DWORD*)((BYTE*)esi + 0xC) = 1;
*(DWORD*)((BYTE*)esi + 0x10) |= 0xFFFFFFFF;
}
if ((*((BYTE*)(esi + 0x4)) & 1))
{
if (g_initVar2ByAvrfLoadDll == 0)
{
esi = 0;
goto RELEASE_SRC;
}
RtlpHpAppCompatDontChangePolicy();
esi = g_4B3A32F4(ulFlags, 0, stReserver, stCommit, 0, Parameters);
goto RELEASE_SRC;
}
else
{
eax = (DWORD)RtlpHpEnvGetEnvHandleFromParams(esi);
edi = (DWORD)stReserver;
if (edi == 0) // 如果Reserver 是0则给出Commit的大小
{
edi = stCommit;
}

if (eax > edi)
{
eax = edi;
}

// 通过RtlpHpHeapCreate创建堆然后将堆移动到堆链表中去
if (!RtlpHpHeapCreate(
RtlpHpConvertCreationFlags(ulFlags,
ntGlobalFlag),
edi,
eax,
ecx,
edx))
{
goto RELEASE_SRC;
}
// 移动链表
RtlpMoveHeapBetweenLists(esi, edx, 1, ebx);
if (*(WORD*)(esi + 0x14) == LOWORD(ebx))
{
goto RELEASE_SRC;
}

RtlpHpHeapDestroy(esi);
esi = ebx;
goto RELEASE_SRC;

}

}

if (!(ulFlags & 0x10000000))
{
if (_RtlpHeapErrorHandlerThreshold >= 2
&& (ulFlags & 0xFFF80C00))
{
if (((PPEB)__readfsdword(0x30))->Ldr)
{
//_DbgPrint();
// 这里正常来讲应该是一个结构体而不是强转 + 偏移
// 但是为了赶项目进度暂时翻译为偏移的形式
printf("HEAP[%wZ]: ", *(DWORD*)(*(DWORD*)(__readfsdword(0x30) + 0xc) + 0xc) + 0x2c);
}
else
{
// dbg p
printf("HEAP: ");
}
// dbg p
printf("!(CheckedFlags & ~HEAP_CREATE_VALID_MASK)");

//
if (byte_4B3A5DA8 == 0)
{
RtlpReportHeapFailure(2);
}
//edx = ulFlags;
}
/*if (edx & 0xFFF80C00)
{
edx &= 0x7F3FF;
}*/

if (ulFlags & 0xFFF80C00)
{
ulFlags &= 0x7F3FF;
}
}
// ebx 万年老0, 这里推断var_A8是个结构体
// 或许初始化为 STRUCT var_A8 { 0 };
// 不过考虑到WINDOWS是C语言开发的
// 也不清楚微软内部编译器支持的语言规范版本
// 暂时设置为memset
memset(var_A8, 0, 0x30);

if (edi != 0)
{
// 这里对SEH进行一些设置
ms_exc.registration.TryLevel = 0;
if (edi == esi)
{
memcpy((PBYTE)edi, var_A8, 0xc);
}
ms_exc.registration.TryLevel = 0xFFFFFFFE;
}
// 这里的汇编为test cl, 20h 但是ntGlobalFlag
// 为4字节我不想翻译为强转只能祈祷编译器对取低位有优化
if (ntGlobalFlag & 0xff & 0x10)
{
ulFlags |= 0x20;
}

if (ntGlobalFlag & 0xff & 0x20)
{
ulFlags |= 0x40;
}

if (ntGlobalFlag & 0x200000)
{
ulFlags |= 0x80;
}

if (ntGlobalFlag & 0xff & 0x40)
{
ulFlags |= 0x40000000;
}


if (ntGlobalFlag & 0xff & 0x80)
{
ulFlags |= 0x20000000;
}


if (ntGlobalFlag & 0x1000)
{
ulFlags |= 0x8000000;
}

ecx = __readfsdword(0x30);

// 这里的var_a8应该是个结构体之后要改一下可以通过PEB的成员反推
// var_a8的成员
if (!var_A8[1])
{
var_A8[1] = *(DWORD*)(ecx + 0x78);
}
if (!var_A8[2])
{
var_A8[2] = *(DWORD*)(ecx + 0x7c);
}
if (!var_A8[3])
{
var_A8[3] = *(DWORD*)(ecx + 0x84);
}
if (!var_A8[4])
{
var_A8[4] = *(DWORD*)(ecx + 0x80);
}

if (!dword_4B3A6940)
{
dword_4B3A6944 = 0x10000;
// 首先是查询系统信息
if (NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemBasicInformation,
&SystemInformation, 0x2c, 0) < 0)
{
goto RELEASE_SRC;
}
dword_4B3A6940 = var_58;
edx = 0x1000;
}
// 接下来是对页段的操作
// 貌似这里是在对页进行处理-0x1000的操作正是在将
// 前面的设置为页首
if (!var_A8[5])
{
var_A8[5] = dword_4B3A6940 - dword_4B3A6944 - 0x1000;
}
// 最大值为0x7F000
if (!var_A8[6] || var_A8[6] > 0x7F000)
{
var_A8[6] = 0x7F000;
}

// 加了一些什么值
if (stCommit)
{
stCommit = (stCommit + 0xFFF) & 0xFFFFF000;
}

/*edi = stReserver;
if (edi != 0)
{
ecx = edi + 0XFFF;
ecx &= 0xFFFFF000;
var_BC = ecx;
}*/

if (stReserver)
{
var_BC = (stReserver + 0xFFF) & 0xFFFFF000;
}
else
{
var_BC = (stCommit + 0xFFFF) & 0xFFFF0000;
}

/*
mov [ebp+var_BC], ecx ; ecx = var_BC
mov esi, edx ; edx = dwCommiteSize
; esi = dwCommiteSize
cmp edx, ecx
ja markif19
*/


// 计算得到最大堆块大小:最大堆块大小实际上是 0x7f000,即 0x80000 减去一个页面。
// 最大块大小为 0xfe00,粒度偏移为 3
if (stCommit > var_BC)
{
stCommit = var_BC;
esi = var_BC;
}
//获取传入的堆参数块,根据PEB设置堆参数块的值,
// 根据PEB->NtGlobalFlag设置堆块的标志
// ebx = 0
if (!(ulFlags & 0xff & 2) && pBase != NULL)
{
ntGlobalFlag = 0;
}
else
{
ntGlobalFlag = 0x1000;
ulUnknow1 = 2;
//eax = var_BC - 0x1000;
if (stCommit < var_BC - 0x1000)
{
var_BC = (var_BC + 0x10fff) & 0xffff0000;
}
}

if (!stCommit || !var_BC)
{
esi = 0;
goto RELEASE_SRC;
}

// 返回调试堆
if (ulFlags & 0x61000000)
{
if (!(ulFlags & 0x10000000))
{
return (PVOID)RtlDebugCreateHeap(ulFlags, pBase, var_BC, stCommit, pLock2, &var_A8);
}
}

stReserver = 0x258;
if (!(ulFlags & 0xff & 1))
{
if (pLock2)
{
ulFlags |= 0x80000000;
}
CriticalSectionFlag = (DWORD)(pLock2 ? pLock2 : 0);
stReserver = !pLock2 ? 0x270 : 0x252;
}
else
{
if (pLock2 != 0)
{
esi = 0;
goto RELEASE_SRC;
}
}

// 如果调用者提供的堆基地址为0,调用ZwAllocateVirtualMemory从内存管理器分配内存
if (pBase == 0)
{
BaseAddress = 0;
if (var_A8[9] != (DWORD)pBase)
{
esi = 0;
goto RELEASE_SRC;
}
pLock = (PVOID)RtlpHeapGenerateRandomValue32();

var_BC = ebx;
// eax = RtlpSecMemFreeVirtualMemory(ecx, edx, &var_BC, 0x8000)
// | BaseAddress & 0x1f << 0x10;
var_D8 = RtlpHeapGenerateRandomValue32()
| BaseAddress & 0x1f << 0x10;

dwRegionSize = var_D8 + var_BC;

if (dwRegionSize < var_BC)
{
dwRegionSize = var_BC;
var_D8 = BaseAddress;
}


if (NtAllocateVirtualMemory(-1,
&BaseAddress,
0,
&dwRegionSize,
0x2000,
(ulFlags & 0x40000) == 0 ? 4 : 64) < 0)
{
esi = 0;
goto RELEASE_SRC;
}

pHeapHandle2 = BaseAddress;
var_BC = dwRegionSize;
if (var_D8 != 0)
{
RtlpSecMemFreeVirtualMemory(var_BC, &BaseAddress, &var_D8, 0x8000);
pHeapHandle2 = BaseAddress + var_D8;
var_BC = dwRegionSize - var_D8;
}
var_CC = BaseAddress + var_D8;
stCommit = BaseAddress + var_D8;
}
else
{
if (var_A8[0] != 0)
{
if (var_A8[6] == 0 || var_A8[7] == 0 || var_A8[6] > var_A8[7] || ulFlags & 0xff != 2)
{
esi = 0;
goto RELEASE_SRC;
}
var_CC = (DWORD)pBase;
stCommit = (DWORD)pBase + (DWORD)pLock2;
var_BC = var_A8[7];
memset(pBase, 0, 0x1000);
esi = ulFlags;
}
else
{
//pBase2需要切换为结构体

if (NtQueryVirtualMemory(-1, pBase, 0, &var_110, 0x1c, 0) < 0
|| var_110[0] != stCommit
|| var_110[3] == 0x10000)
{
esi = 0;
goto RELEASE_SRC;
}
// 根据传入的ReserveSize和CommitSize设置堆块的保留页面和提交页面
var_CC = (DWORD)var_110;
if (var_110[3] == 0x1000)
{
var_BC = var_110[2];
if (stCommit > var_BC)
{
stCommit = var_BC;
}
if (stCommit < 0x1000)
{
esi = 0;
goto RELEASE_SRC;
}
esi = ulFlags;
}
else
{
if ((ulFlags & 0x40000))
{
if (var_110[4] & 0x40)
{
goto RELEASE_SRC;
}
}
// 此时,已获得一个堆指针、已提交的基址、未提交的基址、段标志、
// 提交大小和保留大小。
// 如果已提交和未提交的基地址相同,那么我们需要调用ZwAllocateVirtualMemory提交由ComitSize指定的数量
memset(var_110, 0, 0x1000);
if (NtQueryVirtualMemory(-1, pBase, 3, &var_12C, 0x1c, 0) < 0)
{
esi = 0;
goto RELEASE_SRC;
}

var_BC = var_120;
stCommit = var_110[2];
dwCommiteSize = stCommit + var_CC;
}

}
var_110[10] |= 1;
pHeapHandle2 = (DWORD)pBase;
ulFlags &= 0x40000;
ecx = stCommit;
eax = var_CC;
}
// edi = pBase
if (var_CC == stCommit)
{
if (NtAllocateVirtualMemory(-1, &var_CC, 0, &dwCommiteSize, 0x1000, (ulFlags & 40000) == 0 ? 4 : 64) < 0)
{
esi = 0;
goto RELEASE_SRC;
}

if (RtlGetCurrentServiceSessionId())
{
eax = __readfsdword(0x30);
ecx = *(DWORD*)(eax + 0x50);
ecx += 0x226;
}
else
{
// 从这里进入0环
ecx = 0x7FFE0380;
}
if (*(BYTE*)ecx != 0)
{
if (*(BYTE*)(__readfsdword(0x30) + 0x240) != 1)
{
RtlpLogHeapCommit(pHeapHandle2, var_CC, dwCommiteSize, 1);
}
}
stCommit = stCommit + dwCommiteSize;

}


// edi = HeapHandle + 258h
if (*(DWORD*)(__readfsdword(0x30) + 0x68) != 0x800)
{
/*
;ecx = *(DWORD*)(pHeapHandle2 + 0x258 + 0x7) & 0xFFFFFFF8;
*(DWORD*)(pHeapHandle2 + 0xBC) = ecx;
stReserver = ecx;
edi = *(DWORD*)(pHeapHandle2 + 0xBC) + ecx;
ulFlags |= 0x4000000;
esi = ulFlags;
*/

*(DWORD*)(pHeapHandle2 + 0xBC) = *(DWORD*)(pHeapHandle2 + 0x258 + 0x7) & 0xFFFFFFF8;
stReserver = *(DWORD*)(pHeapHandle2 + 0x258 + 0x7) & 0xFFFFFFF8;
edi = *(DWORD*)(pHeapHandle2 + 0xBC) + *(DWORD*)(pHeapHandle2 + 0x258 + 0x7) & 0xFFFFFFF8;;
ulFlags |= 0x4000000;
esi = ulFlags;

}
var_110[9] = (stReserver + 7) & 0xFFFFFFF8;
// ecx = var_110[9] / 8;
*(WORD*)pHeapHandle2 = var_110[9] / 8;
*(WORD*)(pHeapHandle2 + 2) = 1;
*(WORD*)(pHeapHandle2 + 7) = 1;
*(DWORD*)(pHeapHandle2 + 0x60) = 0xEEFFEEFF;
*(DWORD*)(pHeapHandle2 + 0x40) = ulFlags & 0xEFFFFFFF;
*(DWORD*)(pHeapHandle2 + 0x58) = 0;
memset((BYTE*)pHeapHandle2 + 0x1F4, 0, 0x5C);
// 实际上这里的参数应该为 *_HEAP_HANDLE
// 这里是对堆头进行加密
RtlpCreateHeapEncoding((PVOID)pHeapHandle2);
*(DWORD*)(pHeapHandle2 + 0x234) = 1;

if (*(DWORD*)(pHeapHandle2 + 0x40) & 0x8000000)
{
// 这里或许是获取拦截器索引
/*
基于此猜测我们给出如下的一些定义
typedef DWORD(__stdcall* INTERCEPTOR_PFN)(DWORD, DWORD, DWORD, DWORD);

DWORD
NTAPI
RtlpGetHeapInterceptorIndex(INTERCEPTOR_PFN);

INTERCEPTOR_PFN RtlpStackTraceDatabaseLogPrefix;

*/

/*
mov ecx, offset _RtlpStackTraceDatabaseLogPrefix@16 ; RtlpStackTraceDatabaseLogPrefix(x,x,x,x)
call _RtlpGetHeapInterceptorIndex@4 ; RtlpGetHeapInterceptorIndex(x)
movzx eax, ax 值得注意的是这里用了movzx但是对于movzx来讲是取用了低位所以我们有必要考虑
// 返回的是一个4字节的结构体而非一个DWORD,但是我们不知道语义所以这里给出LOWORD来取代结构体
mov [esi+58h], eax
*/


*(DWORD*)(pHeapHandle2 + 0x58) = (DWORD)LOWORD(RtlpGetHeapInterceptorIndex(RtlpStackTraceDatabaseLogPrefix));
*(DWORD*)(pHeapHandle2 + 0x40) = 0xFFFFFFBF;
}
*(DWORD*)(pHeapHandle2 + 0x44) = ulFlags & 0x6001007D;
// 这里的还原存疑
/*
edi 来源
lea edi, [edx+258h] ; edx = heapHandle
...
mov ecx, edi ; edi = heapHandle + 0x258
mov eax, [ebp+HeapHandle]
sub ecx, eax ; ecx = 0x258
mov [eax+7Eh], cx
mov eax, [ebp+HeapHandle]
*/

*(WORD*)(pHeapHandle2 + 0x7E) = LOWORD(0x258);
// ebx 万年老0
*(DWORD*)(pHeapHandle2 + 0x80) = 0;


//eax = pHeapHandle2 + 0xc0;
//*(DWORD*)(eax + 4) = eax;
*(DWORD*)(pHeapHandle2 + 0xc4) = pHeapHandle2 + 0xc0;
*(DWORD*)(pHeapHandle2 + 0xc0) = pHeapHandle2 + 0xc0;



//eax = pHeapHandle2 + 0x9c;
//*(DWORD*)(eax + 4) = eax;
//*(DWORD*)(eax) = eax;
*(DWORD*)(pHeapHandle2 + 0x9c + 4) = pHeapHandle2 + 0x9c;
*(DWORD*)(pHeapHandle2 + 0x9c) = pHeapHandle2 + 0x9c;

*(DWORD*)(pHeapHandle2 + 0xA4 + 4) = pHeapHandle2 + 0xA4;
*(DWORD*)(pHeapHandle2 + 0xA4) = pHeapHandle2 + 0xA4;

*(DWORD*)(pHeapHandle2 + 0x8C + 4) = pHeapHandle2 + 0x8C;
*(DWORD*)(pHeapHandle2 + 0x8C) = pHeapHandle2 + 0x8C;

if (!CriticalSectionFlag && !(ulFlags & 0xff & 1))
{
//CriticalSectionFlag = edi;
CriticalSectionFlag = pHeapHandle2 + 0x258;
if (RtlInitializeCriticalSectionEx(CriticalSectionFlag, 0, 0x10000000) < 0)
{

//return 0;
esi = 0;
goto RELEASE_SRC;
}
edi += 0x18;
ecx = CriticalSectionFlag;
}
*(DWORD*)(pHeapHandle2 + 0xC8) = CriticalSectionFlag;
*(DWORD*)(pHeapHandle2 + 0x48) |= 0x80000000;


if (!(RtlpInitializeHeapSegment(pHeapHandle2,
pHeapHandle2,
var_110[9] + 0x238,
CriticalSectionFlag,
var_110[0xA],
var_CC,
stCommit,
var_CC - var_110[0xE] + var_BC) & 0xff))
{
esi = 0;
goto RELEASE_SRC;
}
esi = 0x80;
if (pBase != 0)
{
memset((BYTE*)pHeapHandle2 + 0x258 + 0x18, 0, 0x80);
}
*(DWORD*)(pHeapHandle2 + 0x258 + 0x4) = 0x80;
*(DWORD*)(pHeapHandle2 + 0x258 + 0x1c) = pHeapHandle2 + 0x258 + 0x24;
*(DWORD*)(pHeapHandle2 + 0x258 + 0x18) = pHeapHandle2 + 0xc0;
*(DWORD*)(pHeapHandle2 + 0x258 + 0x20) = pHeapHandle2 + 0x258 + 0x24 + 0x10;

RtlpPopulateListIndex(pHeapHandle2, pHeapHandle2 + 0x258);
*(WORD*)(pHeapHandle2 + 0x7c) = 0;
*(DWORD*)(pHeapHandle2 + 0x64) = var_A8[0];
*(DWORD*)(pHeapHandle2 + 0x68) = var_A8[1];
*(DWORD*)(pHeapHandle2 + 0x6c) = var_A8[2] / 8;
*(DWORD*)(pHeapHandle2 + 0x70) = var_A8[3] / 8;
*(DWORD*)(pHeapHandle2 + 0x78) = var_A8[4] / 8;
*(DWORD*)(pHeapHandle2 + 0x5c) = (var_A8[5] + 7) / 8;
*(DWORD*)(pHeapHandle2 + 0xcc) = (var_A8[8] ^ _RtlpHeapKey);
*(DWORD*)(pHeapHandle2 + 0x250) = 4;
*(DWORD*)(pHeapHandle2 + 0x254) = 0xFE000;

if (_RtlpDisableHeapLookaside != 1)
{
//test byte ptr _RtlpDisableHeapLookaside, dl
*(DWORD*)(pHeapHandle2 + 0x48) = 1;
}

if (!(ulFlags & 0x10000))
{
*(DWORD*)(pHeapHandle2 + 0x94) = 0xf;
*(DWORD*)(pHeapHandle2 + 0x98) = 0xFFFFFFF8;
}

if (*(DWORD*)(pHeapHandle2 + 0x40) & 0x20)
{
*(DWORD*)(pHeapHandle2 + 0x94) += 8;
}
*(DWORD*)(pHeapHandle2 + 0xE4) = 0;
*(WORD*)(pHeapHandle2 + 0xE8) = 0;
*(WORD*)(pHeapHandle2 + 0xEA) = 0;
*(WORD*)(pHeapHandle2 + 0xEB) = 0;
*(DWORD*)(pHeapHandle2 + 0xE8) = 0;

// 根据快表的标志初始化块表
if (((ulFlags & 3) == 2) && ((_RtlpDisableHeapLookaside & 1) == 0))
{
*(DWORD*)(pHeapHandle2 + 0xEC) = RtlAllocateHeap((PMY_HEAPSTRUCT)pHeapHandle2, 0x80000A, 0x100);
if (!(*(DWORD*)(pHeapHandle2 + 0xEC)))
{
goto RELEASE_SRC;
}
*(BYTE*)(pHeapHandle2 - 1) = 1;
*(BYTE*)(pHeapHandle2 + 0xf0) = 0x80;
}

// 函数指针传入
/*
* 暂时给出如下定义
typedef DWORD(__stdcall*RtlpProcessHeapsListLock_PFN)();

DWORD
NTAPI
RtlEnterCriticalSection(RtlpProcessHeapsListLock_PFN);

RtlpProcessHeapsListLock_PFN _RtlpProcessHeapsListLock;

*/

RtlEnterCriticalSection(_RtlpProcessHeapsListLock);
// 添加堆到链表
RtlpAddHeapToUnprotectedList(pHeapHandle2);

// 离开临界区
RtlLeaveCriticalSection(_RtlpProcessHeapsListLock);


if (!*(DWORD*)(pHeapHandle2 + 0x7c))
{
esi = 0;
goto RELEASE_SRC;
}
if (RtlGetCurrentServiceSessionId())
{
eax = __readfsdword(0x30);
eax = *(DWORD*)(eax + 0x50);
eax += 0x226;
}
else
{
eax = 0x7FFE0380;
}

if (*(BYTE*)eax != 0 && (*(BYTE*)(__readfsdword(0x30) + 0x240) & 1))
{
if (RtlGetCurrentServiceSessionId())
{
esi = *(DWORD*)(__readfsdword(0x30) + 0x50) + 0x226;
}
RtlpLogHeapCreateEvent(pHeapHandle2, ulFlags, var_BC, dwCommiteSize, (DWORD) * (BYTE*)esi);
}
else
{
esi = ulFlags;
}

if (RtlGetCurrentServiceSessionId())
{
eax = __readfsdword(0x30);
eax = *(DWORD*)(eax + 0x50);
eax += 0x230;
}
else
{
eax = 0x7FFE0388;
}

if (*(BYTE*)eax != 0)
{
// 记录创建的堆范围
RtlpHeapLogRangeCreate(pHeapHandle2, var_BC, ulFlags);
}

*(DWORD*)(pHeapHandle2 + 0x48) &= 0x7FFFFFFF;
*(DWORD*)(pHeapHandle2 + 0xd0) = 0;
esi = pHeapHandle2; // 返回值
pHeapHandle2 = 0;
CriticalSectionFlag = 0;

/*
*
这里为统一出口用于释放资源与返回值
此函数返回值为HeapHandle
*/


RELEASE_SRC:
// todo
edi = (DWORD)pBase;
edx = (DWORD)pLock2;
eax = ulUnknow3;
if (eax != 0
&& eax != edx)
{
RtlDeleteCriticalSection(eax);
}

if (ulUnknow2 != 0
&& edi == 0)
{
var_BC = 0;
// eax = RtlpSecMemFreeVirtualMemory(ecx, edx, &var_BC, 0x8000)
// | BaseAddress & 0x1f << 0x10;
var_D8 = RtlpSecMemFreeVirtualMemory(ecx, (PVOID)edx, &var_BC, 0x8000)
| BaseAddress & 0x1f << 0x10;


}
RET:
return (PVOID)esi;
}

Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

看雪ID:TeddyBe4r

https://bbs.kanxue.com/user-home-983513.htm

*本文为看雪论坛优秀文章,由 TeddyBe4r 原创,转载请注明来自看雪社区

原文始发于微信公众号(看雪学苑):Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月25日19:18:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)https://cn-sec.com/archives/3773226.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息