We are going to play a little game. We will search inside the Native Windows Application Programming Interface (API) for functions that used internally by the Windows 7 operating system. The use of such functions is not suggested by Microsoft. We are not only going to uncover such functions, but also we will use them and we will examine their results.
The Native API is behind the Base API that Microsoft suggests to use for compatibility and portability reasons.
The Native API is the last layer (in user mode) that performs direct calls to the windows kernel mode and more specific to the NTOSKRNL.EXE that is the core windows kernel.
I must say that, in my opinion, the method of checking the API of windows is not the easiest thing. I could say that it is more difficult than this in Linux while windows source is not available. Its a closed source. How then is possible to study a specific API function? Only disassembly code can be extracted by processes that are not belong to core kernel. In case that we want to debug kernel, we will need special programs (a windows kernel debugger for example), but this is beyond the scope of this article. We will see from a user-mode point of view the procedures and functions (even undocumented) inside the Native API, aka ntdll.dll.
A question that one might ask, is: But why we do this?
Hmm... there are more than one reason:
1. It is a very good elementary lesson for the wannabe operating systems reverser's.
2. We will learn how to administer our operating system's basic internal actions.
3. We will see live the operation of the (somehow) cryptic native windows API.
What knowledge is required to read this article?
Well, not deep.
1. Elementary knowledge of some reversing techniques, for example how to use Olly debugger.
2. Little (yes little!) knowledge of assembly. We will meet inevitability a lot of assembly code in our trip but I am not willing to make this article an assembly listing with explanations! We will see how to achieve our goals without the need to be an assembly programmer.
Lets start!
The Native API is behind the Base API that Microsoft suggests to use for compatibility and portability reasons.
The Native API is the last layer (in user mode) that performs direct calls to the windows kernel mode and more specific to the NTOSKRNL.EXE that is the core windows kernel.
I must say that, in my opinion, the method of checking the API of windows is not the easiest thing. I could say that it is more difficult than this in Linux while windows source is not available. Its a closed source. How then is possible to study a specific API function? Only disassembly code can be extracted by processes that are not belong to core kernel. In case that we want to debug kernel, we will need special programs (a windows kernel debugger for example), but this is beyond the scope of this article. We will see from a user-mode point of view the procedures and functions (even undocumented) inside the Native API, aka ntdll.dll.
A question that one might ask, is: But why we do this?
Hmm... there are more than one reason:
1. It is a very good elementary lesson for the wannabe operating systems reverser's.
2. We will learn how to administer our operating system's basic internal actions.
3. We will see live the operation of the (somehow) cryptic native windows API.
What knowledge is required to read this article?
Well, not deep.
1. Elementary knowledge of some reversing techniques, for example how to use Olly debugger.
2. Little (yes little!) knowledge of assembly. We will meet inevitability a lot of assembly code in our trip but I am not willing to make this article an assembly listing with explanations! We will see how to achieve our goals without the need to be an assembly programmer.
Lets start!
The native API is implemented inside the ntdll.dll that is located in c:\windows\system32. For the shake of safety lets copy this dll to a work directory say d:\work2. Every test is presented here is performed in a box with Windows 7 Ultimate 64bit.
What I am going to do now is to extract every single function that this DLL contains and to (randomly...?) choose one to examine. In order to extract all the exported functions of ntdll.dll I will use the program dumpbin that is come with Visual Studio 2008.
I enter:
D:\work2> dumpbin ntdll.dll /exports > exports.txt
and I create (through redirection) the file exports.txt that contains all the functions that ntdll.dll exports.
When I open (using ultraedit) I will see the following:
What I am going to do now is to extract every single function that this DLL contains and to (randomly...?) choose one to examine. In order to extract all the exported functions of ntdll.dll I will use the program dumpbin that is come with Visual Studio 2008.
I enter:
D:\work2> dumpbin ntdll.dll /exports > exports.txt
and I create (through redirection) the file exports.txt that contains all the functions that ntdll.dll exports.
When I open (using ultraedit) I will see the following:
Image 1: Exported functions of ntdll.dll
Looking inside this file I found this:
ordinal hint RVA name
933 398 0003859A RtlGetVersion
Hmm... there is a function named RtlGetVersion that is located at RVA (Relative Virtual Address) 3859Α. The RVA is the relative address that the RtlGetVersion function will be loaded when the dll will be loaded into memory and thus gets a Base Address. If, for example, the RVA of a function is 15 and the dll will be loaded at position 100 then the function will be located at address 115.
I would like to see what this function does. So, I will dissaseble it. To do this I have to disassemble the whole ntdll as follows:
D:\work2>dumpbin /disasm ntdll.dll > ntdlldisasm.txt
The file ntdlldisasm.txt that I create contains the whole assembly code of ntdll. Its a small source file of... 17Mb!
Now, I have to search in this file for my RtlGetVersion function.
This is easy. I just have to apply the rule:
IMAGE_BASE_ADDRESS + Function_RVA = Function Address
I already have the RVA. its 0003859A.
But what is the IMAGE_BASE_ADDRESS of ntdll?
This is easy too. I just enter:
D:\work2>dumpbin /headers ntdll.dll | find "image base"
7DE70000 image base (7DE70000 to 7DFEFFFF)
So 7DE70000 + 0003859A = 7DEA859A.
which means that my function RtlGetVersion located at address 7DEA859A.
Image 2: the ntdll.dll disassembly code
The actual code of RtlGetVersion is the following:
7DEA859A: 8B FF mov edi,edi
7DEA859C: 55 push ebp
7DEA859D: 8B EC mov ebp,esp
7DEA859F: 51 push ecx
7DEA85A0: 64 A1 18 00 00 00 mov eax,dword ptr fs:[00000018h]
7DEA85A6: 53 push ebx
7DEA85A7: 56 push esi
7DEA85A8: 8B 75 08 mov esi,dword ptr [ebp+8]
7DEA85AB: 57 push edi
7DEA85AC: 8B 78 30 mov edi,dword ptr [eax+30h]
7DEA85AF: 8B 87 A4 00 00 00 mov eax,dword ptr [edi+000000A4h]
7DEA85B5: 89 46 04 mov dword ptr [esi+4],eax
7DEA85B8: 8B 87 A8 00 00 00 mov eax,dword ptr [edi+000000A8h]
7DEA85BE: 89 46 08 mov dword ptr [esi+8],eax
7DEA85C1: 0F B7 87 AC 00 00 movzx eax,word ptr [edi+000000ACh]
00
7DEA85C8: 89 46 0C mov dword ptr [esi+0Ch],eax
7DEA85CB: 8B 87 B0 00 00 00 mov eax,dword ptr [edi+000000B0h]
7DEA85D1: 89 46 10 mov dword ptr [esi+10h],eax
7DEA85D4: 8B 87 F4 01 00 00 mov eax,dword ptr [edi+000001F4h]
7DEA85DA: 85 C0 test eax,eax
7DEA85DC: 74 0A je 7DEA85E8
7DEA85DE: 66 83 38 00 cmp word ptr [eax],0
7DEA85E2: 0F 85 BB 78 05 00 jne 7DEFFEA3
7DEA85E8: 33 C0 xor eax,eax
7DEA85EA: 66 89 46 14 mov word ptr [esi+14h],ax
7DEA85EE: 81 3E 1C 01 00 00 cmp dword ptr [esi],11Ch
7DEA85F4: 75 5E jne 7DEA8654
7DEA85F6: 66 0F B6 87 AF 00 movzx ax,byte ptr [edi+000000AFh]
00 00
7DEA85FE: 66 89 86 14 01 00 mov word ptr [esi+00000114h],ax
00
7DEA8605: 66 8B 87 AE 00 00 mov ax,word ptr [edi+000000AEh]
00
7DEA860C: B9 FF 00 00 00 mov ecx,0FFh
7DEA8611: 66 23 C1 and ax,cx
7DEA8614: 66 89 86 16 01 00 mov word ptr [esi+00000116h],ax
00
7DEA861B: 66 A1 D0 02 FE 7F mov ax,word ptr ds:[7FFE02D0h]
7DEA8621: 66 89 86 18 01 00 mov word ptr [esi+00000118h],ax
00
7DEA8628: 8D 45 FC lea eax,[ebp-4]
7DEA862B: 8D BE 1A 01 00 00 lea edi,[esi+0000011Ah]
7DEA8631: 50 push eax
7DEA8632: C6 07 00 mov byte ptr [edi],0
7DEA8635: E8 28 00 00 00 call 7DEA8662
7DEA863A: 84 C0 test al,al
7DEA863C: 74 16 je 7DEA8654
7DEA863E: 8B 45 FC mov eax,dword line ptr [ebp-4]
7DEA8641: 88 07 mov byte ptr [edi],al
7DEA8643: 83 F8 01 cmp eax,1
7DEA8646: 75 0C jne 7DEA8654
7DEA8648: B8 EF FF 00 00 mov eax,0FFEFh
7DEA864D: 66 21 86 18 01 00 and word ptr [esi+00000118h],ax
00
7DEA8654: 5F pop edi
7DEA8655: 5E pop esi
7DEA8656: 33 C0 xor eax,eax
7DEA8658: 5B pop ebx
7DEA8659: C9 leave
7DEA865A: C2 04 00 ret 4
I present this code just for the shake of knowledge and I am not to go in a line by line explanation as I promised. I am going to do something more practical: I will execute the above code using Olly debugger and I will examine the results. Ok, it sounds interesting, but how can I call the specific function that is inside a DLL? With Olly this is easy.
Before I start, I want to check the documentation of this function. Indeed here is the documentation that microsoft provides.
The RtlGetVersion routine returns version information about the currently running operating system.
Syntax
NTSTATUS RtlGetVersion(
__out PRTL_OSVERSIONINFOW lpVersionInformation
);
Parameters
lpVersionInformation [out]
Pointer to either a RTL_OSVERSIONINFOW structure or a RTL_OSVERSIONINFOEXW structure that contains the version
information about the currently running operating system. A caller specifies which input structure is used by setting
the dwOSVersionInfoSize member of the structure to the size in bytes of the structure that is used.
Return Value
RtlGetVersion returns STATUS_SUCCESS.
Remarks
RtlGetVersion is the kernel-mode equivalent of the user-mode GetVersionEx function in the Windows SDK. See the example
in the Windows SDK that shows how to get the system version.
As we can see, this function has no input arguments and once it is called returns its results to the structure RTL_OSVERSIONINFOW. The definition in this structure is here.
dwOSVersionInfoSize
Specifies the size in bytes of an RTL_OSVERSIONINFOW structure. This member must be set before the structure is used with RtlGetVersion.
dwMajorVersion
Identifies the major version number of the operating system. For example, for Windows 2000, the major version number is five.
dwMinorVersion
Identifies the minor version number of the operating system. For example, for Windows 2000 the minor version number is zero.
dwBuildNumber
Identifies the build number of the operating system.
dwPlatformId
Identifies the operating system platform. For Microsoft Win32 on NT-based operating systems, RtlGetVersion returns the value VER_PLATFORM_WIN32_NT.
szCSDVersion
Contains a null-terminated string, such as "Service Pack 3", which indicates the latest Service Pack installed on the system. If no Service Pack has been installed, the string is empty.
Looking inside this file I found this:
ordinal hint RVA name
933 398 0003859A RtlGetVersion
Hmm... there is a function named RtlGetVersion that is located at RVA (Relative Virtual Address) 3859Α. The RVA is the relative address that the RtlGetVersion function will be loaded when the dll will be loaded into memory and thus gets a Base Address. If, for example, the RVA of a function is 15 and the dll will be loaded at position 100 then the function will be located at address 115.
I would like to see what this function does. So, I will dissaseble it. To do this I have to disassemble the whole ntdll as follows:
D:\work2>dumpbin /disasm ntdll.dll > ntdlldisasm.txt
The file ntdlldisasm.txt that I create contains the whole assembly code of ntdll. Its a small source file of... 17Mb!
Now, I have to search in this file for my RtlGetVersion function.
This is easy. I just have to apply the rule:
IMAGE_BASE_ADDRESS + Function_RVA = Function Address
I already have the RVA. its 0003859A.
But what is the IMAGE_BASE_ADDRESS of ntdll?
This is easy too. I just enter:
D:\work2>dumpbin /headers ntdll.dll | find "image base"
7DE70000 image base (7DE70000 to 7DFEFFFF)
So 7DE70000 + 0003859A = 7DEA859A.
which means that my function RtlGetVersion located at address 7DEA859A.
Image 2: the ntdll.dll disassembly code
The actual code of RtlGetVersion is the following:
7DEA859A: 8B FF mov edi,edi
7DEA859C: 55 push ebp
7DEA859D: 8B EC mov ebp,esp
7DEA859F: 51 push ecx
7DEA85A0: 64 A1 18 00 00 00 mov eax,dword ptr fs:[00000018h]
7DEA85A6: 53 push ebx
7DEA85A7: 56 push esi
7DEA85A8: 8B 75 08 mov esi,dword ptr [ebp+8]
7DEA85AB: 57 push edi
7DEA85AC: 8B 78 30 mov edi,dword ptr [eax+30h]
7DEA85AF: 8B 87 A4 00 00 00 mov eax,dword ptr [edi+000000A4h]
7DEA85B5: 89 46 04 mov dword ptr [esi+4],eax
7DEA85B8: 8B 87 A8 00 00 00 mov eax,dword ptr [edi+000000A8h]
7DEA85BE: 89 46 08 mov dword ptr [esi+8],eax
7DEA85C1: 0F B7 87 AC 00 00 movzx eax,word ptr [edi+000000ACh]
00
7DEA85C8: 89 46 0C mov dword ptr [esi+0Ch],eax
7DEA85CB: 8B 87 B0 00 00 00 mov eax,dword ptr [edi+000000B0h]
7DEA85D1: 89 46 10 mov dword ptr [esi+10h],eax
7DEA85D4: 8B 87 F4 01 00 00 mov eax,dword ptr [edi+000001F4h]
7DEA85DA: 85 C0 test eax,eax
7DEA85DC: 74 0A je 7DEA85E8
7DEA85DE: 66 83 38 00 cmp word ptr [eax],0
7DEA85E2: 0F 85 BB 78 05 00 jne 7DEFFEA3
7DEA85E8: 33 C0 xor eax,eax
7DEA85EA: 66 89 46 14 mov word ptr [esi+14h],ax
7DEA85EE: 81 3E 1C 01 00 00 cmp dword ptr [esi],11Ch
7DEA85F4: 75 5E jne 7DEA8654
7DEA85F6: 66 0F B6 87 AF 00 movzx ax,byte ptr [edi+000000AFh]
00 00
7DEA85FE: 66 89 86 14 01 00 mov word ptr [esi+00000114h],ax
00
7DEA8605: 66 8B 87 AE 00 00 mov ax,word ptr [edi+000000AEh]
00
7DEA860C: B9 FF 00 00 00 mov ecx,0FFh
7DEA8611: 66 23 C1 and ax,cx
7DEA8614: 66 89 86 16 01 00 mov word ptr [esi+00000116h],ax
00
7DEA861B: 66 A1 D0 02 FE 7F mov ax,word ptr ds:[7FFE02D0h]
7DEA8621: 66 89 86 18 01 00 mov word ptr [esi+00000118h],ax
00
7DEA8628: 8D 45 FC lea eax,[ebp-4]
7DEA862B: 8D BE 1A 01 00 00 lea edi,[esi+0000011Ah]
7DEA8631: 50 push eax
7DEA8632: C6 07 00 mov byte ptr [edi],0
7DEA8635: E8 28 00 00 00 call 7DEA8662
7DEA863A: 84 C0 test al,al
7DEA863C: 74 16 je 7DEA8654
7DEA863E: 8B 45 FC mov eax,dword line ptr [ebp-4]
7DEA8641: 88 07 mov byte ptr [edi],al
7DEA8643: 83 F8 01 cmp eax,1
7DEA8646: 75 0C jne 7DEA8654
7DEA8648: B8 EF FF 00 00 mov eax,0FFEFh
7DEA864D: 66 21 86 18 01 00 and word ptr [esi+00000118h],ax
00
7DEA8654: 5F pop edi
7DEA8655: 5E pop esi
7DEA8656: 33 C0 xor eax,eax
7DEA8658: 5B pop ebx
7DEA8659: C9 leave
7DEA865A: C2 04 00 ret 4
I present this code just for the shake of knowledge and I am not to go in a line by line explanation as I promised. I am going to do something more practical: I will execute the above code using Olly debugger and I will examine the results. Ok, it sounds interesting, but how can I call the specific function that is inside a DLL? With Olly this is easy.
Before I start, I want to check the documentation of this function. Indeed here is the documentation that microsoft provides.
The RtlGetVersion routine returns version information about the currently running operating system.
Syntax
NTSTATUS RtlGetVersion(
__out PRTL_OSVERSIONINFOW lpVersionInformation
);
Parameters
lpVersionInformation [out]
Pointer to either a RTL_OSVERSIONINFOW structure or a RTL_OSVERSIONINFOEXW structure that contains the version
information about the currently running operating system. A caller specifies which input structure is used by setting
the dwOSVersionInfoSize member of the structure to the size in bytes of the structure that is used.
Return Value
RtlGetVersion returns STATUS_SUCCESS.
Remarks
RtlGetVersion is the kernel-mode equivalent of the user-mode GetVersionEx function in the Windows SDK. See the example
in the Windows SDK that shows how to get the system version.
As we can see, this function has no input arguments and once it is called returns its results to the structure RTL_OSVERSIONINFOW. The definition in this structure is here.
dwOSVersionInfoSize
Specifies the size in bytes of an RTL_OSVERSIONINFOW structure. This member must be set before the structure is used with RtlGetVersion.
dwMajorVersion
Identifies the major version number of the operating system. For example, for Windows 2000, the major version number is five.
dwMinorVersion
Identifies the minor version number of the operating system. For example, for Windows 2000 the minor version number is zero.
dwBuildNumber
Identifies the build number of the operating system.
dwPlatformId
Identifies the operating system platform. For Microsoft Win32 on NT-based operating systems, RtlGetVersion returns the value VER_PLATFORM_WIN32_NT.
szCSDVersion
Contains a null-terminated string, such as "Service Pack 3", which indicates the latest Service Pack installed on the system. If no Service Pack has been installed, the string is empty.
Ok, now I know what I expect as a result: The structure RTL_OSVERSIONINFOW. Now, I just return to my initial goal: To execute the function via Olly. Because we are talking about a DLL (and to about an executable) I will do the following: After loading the DLL in Olly I will go to the address where my function is located and I will put there a new origin point, as you can see in the next image.
Image 3: The new origin point is the address of RtlGetVersion.
Note that the address of RtlGetVersion is not the same of what I get from the hexdump output. This is not odd and can be explained by the fact that the Base Address in each case is different.
After setting my new origin, I put a break point at address 776D859A and I am now ready to press RUN to meet this breakpoint and then I will go line by line (by pressing F10) examining the function execution and the registers values. In addition, in order to check the results in a more reliable way, I will do the following: I will execute from command line the "winver" system command in order to check (according to Microsoft documentation) the values. The winver command gives me the following:
Image 4: The output of the winver command.
Note the string "Version 6.1 (Build 7600)". I must see the same result after executing the function RtlGetVersion.
Below there is an analysis of the code that I get from Olly with my remarks for the registers values I get. I suppose that the remarks are descriptive enough so no more are necessary.
776D859A > 8BFF MOV EDI,EDI |
776D859C 55 PUSH EBP | Standard function prologue
776D859D 8BEC MOV EBP,ESP |
776D859F 51 PUSH ECX
776D85A0 64:A1 18000000 MOV EAX,DWORD PTR FS:[18] ====>> return the TEB (Thread Entry Block) address
776D85A6 53 PUSH EBX
776D85A7 56 PUSH ESI
776D85A8 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ====>> return the PEB (Process Entry Block) address
776D85AB 57 PUSH EDI
776D85AC 8B78 30 MOV EDI,DWORD PTR DS:[EAX+30]
776D85AF 8B87 A4000000 MOV EAX,DWORD PTR DS:[EDI+A4] ===>> eax=6 (MajorVersion)
776D85B5 8946 04 MOV DWORD PTR DS:[ESI+4],EAX
776D85B8 8B87 A8000000 MOV EAX,DWORD PTR DS:[EDI+A8] ===>> eax=1 (MinorVersion)
776D85BE 8946 08 MOV DWORD PTR DS:[ESI+8],EAX
776D85C1 0FB787 AC000000 MOVZX EAX,WORD PTR DS:[EDI+AC] ===>> eax=00001DB0 i.e. 7600 (BuildNumber) MOV with zero eXtended
776D85C8 8946 0C MOV DWORD PTR DS:[ESI+C],EAX
776D85CB 8B87 B0000000 MOV EAX,DWORD PTR DS:[EDI+B0] ===>> eax=2 (platformID)
776D85D1 8946 10 MOV DWORD PTR DS:[ESI+10],EAX
776D85D4 8B87 F4010000 MOV EAX,DWORD PTR DS:[EDI+1F4]
776D85DA 85C0 TEST EAX,EAX
776D85DC 74 0A JE SHORT ntdll.776D85E8
776D85DE 66:8338 00 CMP WORD PTR DS:[EAX],0
776D85E2 0F85 BB780500 JNZ ntdll.7772FEA3
776D85E8 33C0 XOR EAX,EAX
776D85EA 66:8946 14 MOV WORD PTR DS:[ESI+14],AX
776D85EE 813E 1C010000 CMP DWORD PTR DS:[ESI],11C
776D85F4 75 5E JNZ SHORT ntdll.776D8654
776D85F6 66:0FB687 AF0000>MOVZX AX,BYTE PTR DS:[EDI+AF]
776D85FE 66:8986 14010000 MOV WORD PTR DS:[ESI+114],AX
776D8605 66:8B87 AE000000 MOV AX,WORD PTR DS:[EDI+AE]
776D860C B9 FF000000 MOV ECX,0FF
776D8611 66:23C1 AND AX,CX
776D8614 66:8986 16010000 MOV WORD PTR DS:[ESI+116],AX
776D861B 66:A1 D002FE7F MOV AX,WORD PTR DS:[7FFE02D0]
776D8621 66:8986 18010000 MOV WORD PTR DS:[ESI+118],AX
776D8628 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
776D862B 8DBE 1A010000 LEA EDI,DWORD PTR DS:[ESI+11A]
776D8631 50 PUSH EAX
776D8632 C607 00 MOV BYTE PTR DS:[EDI],0
776D8635 E8 28000000 CALL ntdll.RtlGetNtProductType
776D863A 84C0 TEST AL,AL
776D863C 74 16 JE SHORT ntdll.776D8654
776D863E 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
776D8641 8807 MOV BYTE PTR DS:[EDI],AL
776D8643 83F8 01 CMP EAX,1
776D8646 75 0C JNZ SHORT ntdll.776D8654
776D8648 B8 EFFF0000 MOV EAX,0FFEF
776D864D 66:2186 18010000 AND WORD PTR DS:[ESI+118],AX
776D8654 5F POP EDI
776D8655 5E POP ESI
776D8656 33C0 XOR EAX,EAX
776D8658 5B POP EBX
776D8659 C9 LEAVE
776D865A C2 0400 RETN 4
Very well. As you can see, I get the same results.
Note that the address of RtlGetVersion is not the same of what I get from the hexdump output. This is not odd and can be explained by the fact that the Base Address in each case is different.
After setting my new origin, I put a break point at address 776D859A and I am now ready to press RUN to meet this breakpoint and then I will go line by line (by pressing F10) examining the function execution and the registers values. In addition, in order to check the results in a more reliable way, I will do the following: I will execute from command line the "winver" system command in order to check (according to Microsoft documentation) the values. The winver command gives me the following:
Image 4: The output of the winver command.
Note the string "Version 6.1 (Build 7600)". I must see the same result after executing the function RtlGetVersion.
Below there is an analysis of the code that I get from Olly with my remarks for the registers values I get. I suppose that the remarks are descriptive enough so no more are necessary.
776D859A > 8BFF MOV EDI,EDI |
776D859C 55 PUSH EBP | Standard function prologue
776D859D 8BEC MOV EBP,ESP |
776D859F 51 PUSH ECX
776D85A0 64:A1 18000000 MOV EAX,DWORD PTR FS:[18] ====>> return the TEB (Thread Entry Block) address
776D85A6 53 PUSH EBX
776D85A7 56 PUSH ESI
776D85A8 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ====>> return the PEB (Process Entry Block) address
776D85AB 57 PUSH EDI
776D85AC 8B78 30 MOV EDI,DWORD PTR DS:[EAX+30]
776D85AF 8B87 A4000000 MOV EAX,DWORD PTR DS:[EDI+A4] ===>> eax=6 (MajorVersion)
776D85B5 8946 04 MOV DWORD PTR DS:[ESI+4],EAX
776D85B8 8B87 A8000000 MOV EAX,DWORD PTR DS:[EDI+A8] ===>> eax=1 (MinorVersion)
776D85BE 8946 08 MOV DWORD PTR DS:[ESI+8],EAX
776D85C1 0FB787 AC000000 MOVZX EAX,WORD PTR DS:[EDI+AC] ===>> eax=00001DB0 i.e. 7600 (BuildNumber) MOV with zero eXtended
776D85C8 8946 0C MOV DWORD PTR DS:[ESI+C],EAX
776D85CB 8B87 B0000000 MOV EAX,DWORD PTR DS:[EDI+B0] ===>> eax=2 (platformID)
776D85D1 8946 10 MOV DWORD PTR DS:[ESI+10],EAX
776D85D4 8B87 F4010000 MOV EAX,DWORD PTR DS:[EDI+1F4]
776D85DA 85C0 TEST EAX,EAX
776D85DC 74 0A JE SHORT ntdll.776D85E8
776D85DE 66:8338 00 CMP WORD PTR DS:[EAX],0
776D85E2 0F85 BB780500 JNZ ntdll.7772FEA3
776D85E8 33C0 XOR EAX,EAX
776D85EA 66:8946 14 MOV WORD PTR DS:[ESI+14],AX
776D85EE 813E 1C010000 CMP DWORD PTR DS:[ESI],11C
776D85F4 75 5E JNZ SHORT ntdll.776D8654
776D85F6 66:0FB687 AF0000>MOVZX AX,BYTE PTR DS:[EDI+AF]
776D85FE 66:8986 14010000 MOV WORD PTR DS:[ESI+114],AX
776D8605 66:8B87 AE000000 MOV AX,WORD PTR DS:[EDI+AE]
776D860C B9 FF000000 MOV ECX,0FF
776D8611 66:23C1 AND AX,CX
776D8614 66:8986 16010000 MOV WORD PTR DS:[ESI+116],AX
776D861B 66:A1 D002FE7F MOV AX,WORD PTR DS:[7FFE02D0]
776D8621 66:8986 18010000 MOV WORD PTR DS:[ESI+118],AX
776D8628 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
776D862B 8DBE 1A010000 LEA EDI,DWORD PTR DS:[ESI+11A]
776D8631 50 PUSH EAX
776D8632 C607 00 MOV BYTE PTR DS:[EDI],0
776D8635 E8 28000000 CALL ntdll.RtlGetNtProductType
776D863A 84C0 TEST AL,AL
776D863C 74 16 JE SHORT ntdll.776D8654
776D863E 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
776D8641 8807 MOV BYTE PTR DS:[EDI],AL
776D8643 83F8 01 CMP EAX,1
776D8646 75 0C JNZ SHORT ntdll.776D8654
776D8648 B8 EFFF0000 MOV EAX,0FFEF
776D864D 66:2186 18010000 AND WORD PTR DS:[ESI+118],AX
776D8654 5F POP EDI
776D8655 5E POP ESI
776D8656 33C0 XOR EAX,EAX
776D8658 5B POP EBX
776D8659 C9 LEAVE
776D865A C2 0400 RETN 4
Very well. As you can see, I get the same results.
There is another thing that I would like to point out: Pay attention at address 776D8635. In this address a function is called: the RtlGetNtProductType. This RtlGetNtProductType is probably internal and... undocumented. More about undocumented functions we will discuss in a later article...
We achive the following, so far:
1. We see which functions exist inside the ntdll.dll.
2. We select and examine one of them.
3. We execute it and we get its result line by line by examining its source (disassembly) code.
Be carefull when you mess around with native API functions. If something goes wrong it is very possible to freeze your system.
REMARK:
Until now we examine functions with 'Rtl' as prefix. Microsoft choose this naming convention for its internal functions. Rtl means RunTime Library. In addition, inside ntdll we will see another prefix: the Zw. Functions with this prefix make direct calls to the kernel. In general, you must know that the naming convetion used by Microsoft following the formula:
<Prefix><Operation><Object>
What remains to be done is a program in C language that will direct call this function from the native API. This program could be used as a base for more advanced... checks, using even undocumented functions ;)
If you search the internet you will not find many sources of how to call RtlGetVersion direct from Native API. So, I suppose that this code could put a small brick to cover this gap.
The program is in C++ in Visual Studio 2010.
///////////////////////////////////////////////////////////////////////
// Kernel01.cpp : Call the RtlGetVersion from native API
// © by Thiseas 2011 for www.p0wnbox.com
//
#include "stdafx.h"
#include <Windows.h>
typedef void (WINAPI *pwinapi)(PRTL_OSVERSIONINFOW); //http://www.osronline.com/ddkx/kmarch/k109_452q.htm
int _tmain(int argc, _TCHAR* argv[])
{
RTL_OSVERSIONINFOW info;
pwinapi p_pwinapi;
ZeroMemory(&info, sizeof(RTL_OSVERSIONINFOW));
p_pwinapi = (pwinapi) GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlGetVersion");
p_pwinapi(&info);
return(0);
}
There is no need to spend more bytes for comments. Please try this by yourself...
Debug and disassemble this little program and repeat the procedure that we just describe above.
I will promise you that you will be disappointed... ;)
Happy Reversing!
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.