that the callback takes a TABLEpointer, a pointer to the data of the element
being added, and a pointer to the data of the current element. The function is
comparing the new element with the data of the element we’re currently tra-
versing. Let’s try and define a prototype for the callback.
typedef int (stdcall * TABLE_COMPARE_ELEMENTS) (
TABLE *pTable,
PVOID pElement1,
PVOID pElement2
);
Summarizing the Findings
Let’s try and summarize all that has been learned about RtlLocateNode
GenericTable. Because we have a working theory on the parameters passed
into it, let’s revisit the code in RtlInsertElementGenericTable that
called into RtlLocateNodeGenericTable, just to try and use this knowl-
edge to learn something about the parameters that RtlInsertElement
GenericTabletakes. The following is the sequence that calls RtlLocate
NodeGenericTablefrom RtlInsertElementGenericTable.
7C924DC7 LEA EAX,DWORD PTR [EBP+8]
7C924DCA PUSH EAX
7C924DCB PUSH DWORD PTR [EBP+C]
7C924DCE CALL ntdll.7C92147B
It looks like the second parameter passed to RtlInsertElementGeneric
Tableat [ebp+C]is the new element currently being inserted. Because you
now know that ntdll.7C92147B(RtlLocateNodeGenericTable) locates
a node in the generic table, you can now give it an estimated prototype.
int RtlLocateNodeGenericTable(
TABLE *pTable,
PVOID ElementToLocate,
NODE **NodeFound;
);
There are still many open questions regarding the data layout of the generic
table. For example, what was that linked list we encountered in RtlGet
ElementGenericTableand how is it related to the binary tree structure
we’ve found?
RtlRealInsertElementWorker
After ntdll.7C92147Breturns, RtlInsertElementGenericTablepro-
ceeds by calling ntdll.7C924DF0, which is presented in Listing 5.7. You don’t
have to think much to know that since the previous function only searched for
178 Chapter 5