the other is taken when SearchResult == 1at that first branch in the begin-
ning of the function (at ntdll.7C924DFC). Notice that this branch doesn’t go
straight to where we are now—it goes through a relocated block at ntdll
.7C935D5D. Regardless of how we got here, let’s look at where we are now.
7C924E68 PUSH EBX
7C924E69 CALL ntdll.RtlSplay
7C924E6E MOV ECX,DWORD PTR [EBP+8]
7C924E71 MOV DWORD PTR [ECX],EAX
7C924E73 MOV EAX,DWORD PTR [EBP+14]
7C924E76 TEST EAX,EAX
7C924E78 JNZ ntdll.7C935D4F
7C924E7E LEA EAX,DWORD PTR [EBX+18]
This sequence calls a function called RtlSplay(whose name you have
because it is exported—remember, I’m not using the Windows debug symbol
files!). RtlSplaytakes one parameter. If SearchResult == 1that parame-
ter is the pNodeparameter passed to RtlRealInsertElementWorker. If
it’s anything else, RtlSplaytakes a pointer to the new element that was just
inserted. Afterward the tree root pointer at pTableis set to the return value of
RtlSplay, which indicates that RtlSplayreturns a tree node, but you don’t
really know what that node is at the moment.
The code that follows checks for the optional Boolean pointer and if it exists
it is set to TRUEif SearchResult != 1. The function then loads the return
value into EAX. It turns out that RtlRealInsertElementWorkersimply
returns the pointer to the data of the newly allocated element. Here’s a cor-
rected prototype for RtlRealInsertElementWorker.
PVOID RtlRealInsertElementWorker(
TABLE *pTable,
PVOID ElementData,
ULONG ElementSize,
BOOLEAN *pResult OPTIONAL,
NODE *pNode,
ULONG SearchResult
);
Also, because RtlInsertElementGenericTable returns the return
value of RtlRealInsertElementWorker, you can also update the proto-
type for RtlInsertElementGenericTable.
PVOID NTAPI RtlInsertElementGenericTable(
TABLE *pTable,
PVOID ElementData,
ULONG DataLength,
BOOLEAN *pResult OPTIONAL,
);
186 Chapter 5