{"id":35,"date":"2006-10-02T00:55:48","date_gmt":"2006-10-02T00:55:48","guid":{"rendered":"https:\/\/driverentry.com.br\/en\/2006\/10\/02\/rtlgetmodulebase-rtlgetprocaddress\/"},"modified":"2026-07-27T20:44:47","modified_gmt":"2026-07-27T20:44:47","slug":"rtlgetmodulebase-rtlgetprocaddress","status":"publish","type":"post","link":"https:\/\/driverentry.com.br\/en\/2006\/10\/02\/rtlgetmodulebase-rtlgetprocaddress\/","title":{"rendered":"RtlGetModuleBase &#038; RtlGetProcAddress"},"content":{"rendered":"<div style=\"text-align: right;\"><a href=\"https:\/\/driverentry.com.br\/2006\/10\/02\/rtlgetmodulebase-rtlgetprocaddress\/\"><img loading=\"lazy\" decoding=\"async\" title=\"Em Portugu\u00eas\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/br.gif\" alt=\"\" width=\"21\" height=\"19\" \/><\/a><\/div>\n<p>In the post <a href=\"https:\/\/driverentry.com.br\/en\/2006\/08\/30\/exallocatepoolwithouttag\/\">ExAllocatePool (WithoutTag)<\/a>, I talked a little about the conflicts on using new APIs in drivers and making them incompatible with legacy systems. Our initial proposal was to have a single binary that would be able to run either in Windows NT and newer systems. We reached a solution that cannot be considered ideal, that old functions are always used on systems that support newer APIs.<\/p>\n<p>Thinking about solving this limitation, the DDK brought us <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff554563(VS.85).aspx\">MmGetSystemRoutineAddress()<\/a>. Analogous to <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683212(VS.85).aspx\">GetProcAddress()<\/a> exported by kernel32.dll, <em>MmGetSystemRoutineAddress()<\/em> dynamically obtains a function address from its exported name. But the world will not still be safe while Windows NT survives. This new function has only been implemented since Windows 2000.<\/p>\n<p>We don&#8217;t have an alternative to Windows NT, so let&#8217;s make one. With one or two PE concepts, we implemented this function version. PE structure carries tons of rules, but for our need we can implement a light version. If you want details about the rules adopted by PE, you can take a look at the article <a href=\"http:\/\/msdn.microsoft.com\/msdnmag\/issues\/02\/02\/PE\/\">An In-Depth Look into the Win32 Portable Executable File Format<\/a> by Matt Pietrek.<\/p>\n<p>Well, now that we already have read the entire article and already known everything about PE, follow the basic algorithm prototype about how to walk through PE structure, looking for a specific function exported by name. This function is not provided by Windows NT DDK and is written in the example source code available for downloading.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS RtlGetProcAddress(IN PVOID     pBaseAddress,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                           IN LPCSTR    pszFunctionName,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                           IN NTPROC    *pProcedure);<\/pre>\n<\/div>\n<p>Notice that the input parameters are the base address, the name of desired function and ultimately the address to the pointer that will get the obtained function address. But where will I get this base address from? Remember the functions we are looking for are exported by <strong>ntoskrnl.lib<\/strong> and they are implemented in the <strong>ntoskrnl.exe<\/strong> module. To make sure the function we&#8217;re looking for is in fact exported by this module, use the <em>&#8220;Dependency Walker&#8221;<\/em> to see this module export table. As I said before, some functions are implemented as macros, thus, its definition will be in a header file and not in the export table.<\/p>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/eat.png\" alt=\"\" \/><\/div>\n<p>To get the module base address that exports these functions, we use <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms725506(VS.85).aspx\">ZwQuerySystemInformation()<\/a> in which, although not documented by Microsoft, there are already several publications that comment about it. Thus, we&#8217;ll define the <strong>RtlGetModuleBase()<\/strong> which will behave similar to the well-known <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms683199(VS.85).aspx\">GetModuleHandle()<\/a>. This function is also defined in the example available for downloading.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">NTAPI<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">ZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                         IN OUT PVOID SystemInformation,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                         IN ULONG SystemInformationLength,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                         OUT PULONG ReturnLength OPTIONAL)<\/pre>\n<\/div>\n<p>There are many undocumented features and settings that can be extremely useful. The book <a href=\"http:\/\/www.amazon.com\/Windows-2000-Native-API-Reference\/dp\/1578701996\/sr=8-1\/qid=1159964043\/ref=sr_1_1\/104-1625282-2611163?ie=UTF8&amp;s=books\">Windows NT\/2000 Native API Reference<\/a> by Gary Nebbett is excellent to make use of these functions. He brings prototypes, enums, structures used in calls, descriptions of what each function does and each parameter. Here in our example, we only use the statements below for information about the modules loaded into the system address space.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">typedef<\/span> <span style=\"color: blue;\">struct<\/span> _SYSTEM_MODULE_INFORMATION   <span style=\"color: green;\">\/\/ Information Class 11<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    ULONG Reserved[<span style=\"color: purple;\">2<\/span>];<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PVOID Base;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    ULONG Size;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    ULONG Flags;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    USHORT Index;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    USHORT Unknown;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    USHORT LoadCount;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    USHORT ModuleNameOffset;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    CHAR ImageName[<span style=\"color: purple;\">256<\/span>];<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;<\/pre>\n<\/div>\n<p>The structure &#8220;Base&#8221; member above brings us the module base address containing PE structure. In the sample code,  is <strong>RtlGetModuleBase()<\/strong> routine definition that has the following prototype.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS RtlGetModuleBase(IN LPCSTR     pszModuleName,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                          OUT PVOID*    ppBaseAddress);<\/pre>\n<\/div>\n<p>With the union of its powers, now we can know whether a certain API is implemented in the current system and if so, get its address. So, it is perfectly possible to have a single binary that can run on both Windows NT using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff544590(VS.85).aspx\">ExFreePool()<\/a> and in later systems using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ff544593(VS.85).aspx\">ExFreePoolWithTag()<\/a>. Below it is a very basic example as always. Of course, we can create a single allocation routine that would do the dirty work.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#include<\/span> <span style=\"color: #a31515;\">\"GetProcAddr.h\"<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/\/-f--&gt; Type for the ExFreePoolWithTag routine<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">typedef<\/span> VOID (NTAPI* PF_EX_FREE_POOL_WITH_TAG)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">(<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    IN PVOID  P,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN ULONG  Tag <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">VOID OnDriverUnload(PDRIVER_OBJECT     pDriverObj)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; That routine, even empty, is here just<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      to allow the driver to be unloaded.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">}<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">\/****<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">***<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">**           There once was a Driver...<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">*\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS DriverEntry(PDRIVER_OBJECT     pDriverObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                     PUNICODE_STRING    pusRegistryPath)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    NTSTATUS                    nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PVOID                       pBaseAddress, pTemp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PF_EX_FREE_POOL_WITH_TAG    pfExFreePoolWithTag;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Set the unload callback routine<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDriverObj-&gt;DriverUnload = OnDriverUnload;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Get the module base address<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    nts = RtlGetModuleBase(<span style=\"color: #a31515;\">\"ntoskrnl.exe\"<\/span>,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                           &amp;pBaseAddress);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Testing the routine's return doesn't hurt,<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      but not doing this can kill your system.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">if<\/span> (!NT_SUCCESS(nts))<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        <span style=\"color: blue;\">return<\/span> nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Allocating memory for a test using the Free routine<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pTemp = ExAllocatePoolWithTag(NonPagedPool,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                                  <span style=\"color: purple;\">10<\/span>,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                                  <span style=\"color: #a31515;\">'tseT'<\/span>);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Get the function API address<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    nts = RtlGetProcAddress(pBaseAddress,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                            <span style=\"color: #a31515;\">\"ExFreePoolWithTag\"<\/span>,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                            (NTPROC*)&amp;pfExFreePoolWithTag);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">if<\/span> (NT_SUCCESS(nts))<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    {<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        <span style=\"color: green;\">\/\/-f--&gt; If that routine is implemented by the system, so<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        <span style=\"color: green;\">\/\/      we get a success return code here.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        pfExFreePoolWithTag(pTemp,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                            <span style=\"color: #a31515;\">'tseT'<\/span>);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    }<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">else<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    {<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        <span style=\"color: green;\">\/\/-f--&gt; Who has no dog, hunts like a cat.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        ExFreePool(pTemp);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    }<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">return<\/span> STATUS_SUCCESS;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">}<\/pre>\n<\/div>\n<p>Have fun! \ud83d\ude42<\/p>\n<p class=\"download\"><a href=\"http:\/\/www.driverentry.com.br\/samples\/ExGetProc.zip\">ExGetProc.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the post ExAllocatePool (WithoutTag), I talked a little about the conflicts on using new APIs in drivers and making them incompatible with legacy systems. Our initial proposal was to have a single binary that would be able to run either in Windows NT and newer systems. We reached a solution that cannot be considered [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-35","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/35","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/comments?post=35"}],"version-history":[{"count":4,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/35\/revisions"}],"predecessor-version":[{"id":313,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/35\/revisions\/313"}],"wp:attachment":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/media?parent=35"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/categories?post=35"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/tags?post=35"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}