The Red Team Vade Mecum
  • The Red Team Vade Mecum
  • Techniques
    • Defense Evasion
      • Binary Properties and Code Signing
      • ATA/ATP
        • Important Note
        • Intro
        • Lateral Movement
        • Domain Dominance
        • Identification
        • Recon
        • Blocking/Disabling Telemetry
          • Trusted Installer
      • Tips and Tricks
      • Basics
        • IOCs
          • High Level Overview of EDR technologies
        • Sandbox Evasion
        • Obfuscating Imports
          • Bootstrapping
        • Encrypting Strings
      • Disabling/Patching Telemetry
        • ETW Bypasses
        • AMSI Bypasses
      • Minimization
        • Commands to Avoid
        • Pivoting
        • Benefits of Using APIs
        • Thread-less Payload Execution
        • DLL Hollowing
      • Misdirection
        • Command Line Argument Spoofing
        • PPID Spoofing via CreateProcess
        • Switching Parents
          • Dechaining via WMI
      • Hiding our Payloads
        • Event Logs
        • File metadata
        • Registry Keys
        • ADS
      • IPC For Evasion and Control
    • Privilege Escalation
      • Hunting For Passwords
      • To System
        • New Service
        • Named Pipe Impersonation
        • Local Exploits
        • AlwaysInstallElevated
      • Hijacking Execution
        • Environment Variable interception
        • DLL Hijacking
      • Insecure Permissions
        • Missing Services and Tasks
        • Misconfigured Registry Hives
        • Insecure Binary Path
        • Unquoted Service Paths
    • Enumeration
      • Situational Awareness
      • Recon Commands
        • .NET AD Enum commands
        • WMIC commands
          • WMI queries from c++
    • Execution
      • Cool ways of Calling a Process
      • One Liners
    • Initial Access
      • Tips and Tricks
      • Tools
      • Staging/Stagers
      • MS Office
        • Macros
          • Evasion
            • VBA Stomping
            • Revert To Legacy Warning in Excel
            • Sandbox Evasion
          • Info Extraction
          • Inline Shapes
          • .MAM Files
          • PowerPoint
          • ACCDE
          • Shellcode Execution
          • Info Extraction
          • Dechaining Macros
        • Field Abuse
        • DDE
      • Payload Delivery
      • File Formats
        • MSG
        • RTF
        • REG
        • BAT
        • MSI Files
        • IQY
        • CHM
        • LNK
          • Using LNK to Automatically Download Payloads
        • HTA
    • Lateral Movement
      • Linux
        • SSH Hijacking
        • RDP
        • Impacket
      • No Admin?
      • Checking for access
      • Poison Handler
      • WinRM
      • AT
      • PsExec
      • WMI
      • Service Control
      • DCOM
      • RDP
      • SCShell
    • Code Injection
      • Hooking
        • Detours
      • CreateRemoteThread
      • DLL Injection
      • APC Queue Code Injection
      • Early Bird Injection
    • Persistence
      • Scheduled Tasks
        • AT
      • MS Office
      • SQL
      • Admin Level
        • SSP
        • Services
        • Default File Extension
        • AppCert DLLs
        • Time Provider
        • Waitfor
        • WinLogon
        • Netsh Dlls
        • RDP Backdoors
        • AppInit Dlls
        • Port Monitor
        • WMI Event Subscriptions
      • User Level
        • LNK
        • Startup Folder
        • Junction folders
        • Registry Keys
        • Logon Scripts
        • Powershell Profiles
        • Screen Savers
  • Infrastructure
    • SQL
      • MS SQL
        • Basics
        • Finding Sql Servers
        • Privilege Escalation
        • Post Exploitation
  • Other
    • Vulnerability Discovery
      • Web Vulnerabilities
        • Code Grepping
          • PHP Cheatsheet
    • Windows Internals
      • Unorganized Notes
Powered by GitBook
On this page
  • Conclusion
  • Resources

Was this helpful?

  1. Techniques
  2. Defense Evasion
  3. Basics
  4. Obfuscating Imports

Bootstrapping

PreviousObfuscating ImportsNextEncrypting Strings

Last updated 3 years ago

Was this helpful?

Before, we used GetModuleHandle and GetProcAddress to find the addresses of functions and call them. It is possible to not call any of these and get the address of the functions like how bootstrappers in shellcode work.

This is possible due to the fact that:

  1. The PEB address is located at theGS register: GS:[0x60]

  2. _PEB_LDR_DATA structure which contains information about the loade modules is located at $PEB:[0x18]

  3. The loader structure contains a pointer to InMemoryOrderModuleList at offset 0x20

  4. InMemoryOrderModuleList is a doubly linked list of LDR_DATA_TABLE_ENTRY structures, each contains BaseDllName and DllBase of a single module

  5. We can then browser all loaded modules and find kernel32.dll and it's base address

  6. with the knowledge of its location in memory, we can find its export directory and browser for the GetProcAddress function

  7. We can then use GetProcAddress to find the addresses of other functions

Let's open notepad in windbg and see how this works.

Let's first get the address of the PEB with:

And then overlay the PEB structure to the memory pointed to be peb to see the what the structures members are holding/pointing to:

The field we are interested in is the Ldr field:

Lets click in the highlighted Ldr, which should take you to a structure called PEB_LDR_DATA:

We then have 3 lists listed in the structure, we are interested in the second called, "InMemoryOrderModuleList". Let's click the highlighted name:

And we see that its a double linked list, this is a chain of pointers which points backwards and forwards to structures. This will loop, meaning if we parse the pointers going from structure to structure, we will end up going back to where we started.

To parse this, we can use the LDR DATA TABLE ENTRY structure to do so:

And now lets issue the following command in windbg

The most important thing about this structure is the DllBase field in which we will see later on.

Lets now take the Flink address of the LIST ENTRY structure and parse that in the LDR DATA TABLE ENTRY structure

Now, lets click the hext highlighted flink address, copy that address and parse it like how we did in the above:

As we can see, the FullDllName field holds the string, "ntdll.dll"

...

If we keep on getting the flink addreses, and parsing it through this structure, we will eventually get back to the first structure which in our case had the FullDllName to be "notepad.exe".

The above showed us how to parse through the PEB and get the DllBase addresses of the modules.

We can also see a pointer to the PEB in the TEB structure like so(which is located at offset 0x060):

Note that if is 32 bit, it would be at offset 30, but since we are using 64 bit, the offset is 60.

Lets make a code implementation of the above.

typedef HMODULE(WINAPI *PGetModuleHandleA)(PCSTR);
typedef FARPROC(WINAPI *PGetProcAddress)(HMODULE, PCSTR);

(in main)
#ifdef _M_IX86 
	PEB * ProcEnvBlk = (PEB *) __readfsdword(0x30);
#else
	PEB * ProcEnvBlk = (PEB *)__readgsqword(0x60);
#endif

The above code retrieves a pointer to the PEB by retrieving what's at offset 0x60 from the gs register. Note that it is offset 0x30 for 32 bit and we read from the fs register instead of the gs register. We store the address of PEB at ProcEnvBlk.

We can then start parsing the structures to search for memory addresses and such.

PPEB_LDR_DATA pLoaderData = pPEB->Ldr;
PLIST_ENTRY listHead = &pLoaderData->InMemoryOrderModuleList;
PLIST_ENTRY listCurrent = listHead->Flink;

PLIST_ENTRY * listOfModules = NULL;

The above stores pointer to the necessary data structures we are going to use to resolve the address of the functions.

for (LIST_ENTRY * pListEntry = listCurrent; pListEntry != listOfModules; pListEntry = pListEntry->Flink)	{
		LDR_DATA_TABLE_ENTRY * pEntry = (LDR_DATA_TABLE_ENTRY *) ((BYTE *) pListEntry - sizeof(PLIST_ENTRY));
		LPCSTR dllName = (LPCSTR)pEntry->BaseDllName.Buffer;
		CharUpperA(dllName);
		if (lstrcmpiW(dllName, "KERNEL32.DLL") {
				kernel32Address = (HMODULE) pEntry->DllBase;
				// if making function, return this, but if not, break here
		}
}

The above iterates though all entries of the double linked list and compares the BaseDLLName to kernel32.dll, if it matches, it will assign kernel32Address to the DLLBase address of the dll.

After we get our dll base address, we will have to find its export directory, we will use some pointer arithmetic to get pointers to certain data structures.

  1. We find ImageDosHeader from the base address

  2. With a pointer to the DosHeader, we can then find the e_lfanew field which stores the RVA address of NtHeader, we then add that to the Base address to find its actual location in memory

  3. With a pointer to NtHeader, we can find OptionalHeader

  4. With the OptionalHeader, we can find the DataDirectory which holds a pointer to the export directory's virtual address, we then add that to the base address to find its actual location in memory

  5. We then get the pointer to the EAT's

    1. AddressOfFunctions

    2. AddressOfNames

    3. AddressOfNameOrdinals

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)kernel32Address;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)kernel32Address + pDosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&(pNtHeader->OptionalHeader);
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)kernel32Address + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
	
PULONG pAddressOfFunctions = (PULONG)((PBYTE)kernel32Address + pExportDirectory->AddressOfFunctions);
PULONG pAddressOfNames = (PULONG)((PBYTE)kernel32Address + pExportDirectory->AddressOfNames);
PUSHORT pAddressOfNameOrdinals = (PUSHORT)((PBYTE)kernel32Address + pExportDirectory->AddressOfNameOrdinals);

We will then go through the Export directory using a for loop, and compare their function names extracted from the table with GetProcAddress and GetModulehandle.

	for (DWORD i = 0; i < pExportDirectory->NumberOfNames; ++i)
	{
		PCSTR pFunctionName = (PSTR)((PBYTE)kernel32Address + pAddressOfNames[i]);
		
		if (!strcmp(pFunctionName, "GetModuleHandleA"))
		{
			pGetModuleHandleA = (PGetModuleHandleA)((PBYTE)kernel32Address + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
		if (!strcmp(pFunctionName, "GetProcAddress"))
		{
			pGetProcAddress = (PGetProcAddress)((PBYTE)kernel32Address + pAddressOfFunctions[pAddressOfNameOrdinals[i]]);
		}
	}

Here, we get the function's virtual address by adding the RVA and the base address of the function and store it in a pointer if the comparison succeeds.

Now that we have the addresses of the GetModuleHandle and GetProcAddress functions, we can use them to call any function we want. Lets do this by calling VirtualAlloc:

typedef PVOID(WINAPI *PVirtualAlloc)(PVOID, SIZE_T, DWORD, DWORD);

HMODULE hKernel32 = pGetModuleHandleA("kernel32.dll");
PVirtualAlloc funcVirtualAlloc = (PVirtualAlloc)pGetProcAddress(hKernel32, "VirtualAlloc");
PVOID *exec = funcVirtualAlloc(0, sizeof shellcode, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

Conclusion

This was a simple lab which showed you how to call functions without GetProcAddress, GetModuleHandle or the actual function showing up in the import table by using the PEB structure.

Resources