In Chapter 5 of the book "Secrets of Reverse Engineering" (by Eldad Eilam), the author goes about reversing an undocumented "table" API functions in the NTDLL library. The logic behind his reasoning in one particular function bothers me. Here is the relevant portions of the function:
RtlGetElementGenericTable:
7C9624E0 PUSH EBP
7C9624E1 MOV EBP, ESP
7C9624E3 MOV ECX, DWORD PTR [EBP+8]
7C9624E6 MOV EDX, DWORD PTR [ECX+14]
7C9624E9 MOV EAX, DWORD PTR [ECX+C]
7C9624EC PUSH EBX
7C9624ED PUSH ESI
7C9624EE MOV ESI, DWORD PTR [ECX+10]
7C9624F1 PUSH EDI
7C9624F2 MOV EDI, DWORD PTR [EBP+C]
7C9624F5 CMP EDI, -1
7C9624F8 LEA EBX, DWORD PTR [EDI+1]
7C9624F8 JE SHORT 7C962559
7C9624FD CMP EBX, EDX
7C9624FF JA SHORT 7C962559
....
7C962554 ADD EAX, 0C
7C962557 JMP SHORT 7C96255B
7C962559 XOR EAX, EAX
7C96255B POP EDI
7C96255C POP ESI
7C96255D POP EBX
7C96255E POP EBP
7C96255F RET 8
From some other "table" functions, he surmises that the structure of the table must look like this:
struct Table {
unknown_ptr member1; // This is non-zero when table has elements
unknown_ptr member2;
unknown_ptr member3;
unknown_ptr member4;
unknown member5;
ulong numberOfElements;
unknown member7;
unknown member8;
unknown member9;
unknown member10;
};
Looking at the last 10 lines before the big jump (so from 7C9624EC to 7C9624FF), he comes up with the following conclusion:
Recall that EDX was loaded from offset +14 in the structure, and that this is the member that stores the total number of elements in the table. This indicates that the second parameter passed to RtlGetElementGenericTable is an index into the table. These last two instructions simply confirm that it is a valid index by comparing it against the total number of elements. This also sheds some light on why the index was incremented. It was done in order to properly compare the two, because the index is probably zero-based, and the total element count is certainly not.
It is this conclusion that I fail to understand, this leap of logic. I fail to see how the second argument is an index into the table. All I can see is that the final two lines of the code chunk translate into if (arg2+1 > numberOfElements) { return 0; }
. Looking at it backwards it makes sense (i.e. a confirmation of the conclusion), but looking at it forwards makes no sense to me (i.e. from premise to conclusion). How was simply that conditional on line 7C9624FF indicate that the second argument must be an index and not something else? In other words, how did the author piece the evidence together into a logical high-level conclusion?