Wednesday, October 16, 2019

DLL Entry-Point Function

DLL Entry-Point Function

BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID fImpLoad)

It is not required to implement DllMain. If DllMain is not present, the DLL will still link but no entry function will be called. 

fImpLoad (aka lvpReserved) is nonzero if DLL implicitly loaded (static load) and 0 when explicitly loaded (dynamic load). 
hInstDll identifies the virtual memory address where DLL's file image was mapped into the process space
fdwReason indicates why system is calling the function 

Remember that DLLs use DllMain to initialize themselves. Avoid calling other functions imported from other DLLs inside DllMain since they might not have been initialized yet. 

Also avoid calling LoadLibrary(Ex) and FreeLibrary from DllMain because dependency loops. 
Also avoid calls to User, Shell, ODBC, COM, RPC, socket functions. 

Constructor/Destructor for global/static C++ objects is called at the same time as DllMain function. 

DLL_PROCESS_ATTACH

System passes this as fdwReason only when Dll is first mapped. A thread that calls LoadLibrary(Ex) on this same Dll that has already been mapped to proc address space will not cause DllMain to be called again with DLL_PROCESS_ATTACH.

System only cares about the return value of DllMain when passed DLL_PROCESS_ATTACH. Return true if initialization is successful. 

The process primary thread calls each of the DLL's DllMain functions that the process needs. If any return FALSE, then proc is terminated. 

When DLL is load explicitly, the system will not terminate proc if LoadLibrary(Ex) fails. It will just unmap DLL file and return NULL. 

DLL_PROCESS_DETACH

When DLL is unmapped from process space, system calls DllMain with DLL_PROCESS_DETACH (assuming DLL did not return FALSE when initially called with DLL_PROCESS_ATTACH). 

Primary thread usually makes this call, but if thread called FreeLibrary or FreeLibraryAndExitThread then that thread executes this call. 

OS kills proc only after DLL completes DLL_PROCESS_DETACH notification. 

If a thread calls TerminateProcess, DLL does not get a chance to DLL_PROCESS_DETACH :( 


DLL_THREAD_ATTACH

When process creates a thread, system examines all DLL files currently mapped into proc address space, and calls each DllMain with DLL_THREAD_ATTACH. The new thread is responsible for executing these calls. 

System only makes this call for new threads, not for existing threads. 

DLL_THREAD_DETACH


System does not immediately kill a thread that calls ExitThread. System calls all the mapped DLL's DllMain's functions with DLL_THREAD_DETACH on the thread that wants to die. 

System does not get to make this call when TerminateThread :(

System does not call DLL_THREAD_DETACH for threads that are currently running when DLL is detached. 

Serialized Calls to DllMain

Calls to DllMain are serialized. That means if we call CreateThread in the DLL_PROCESS_ATTACH of one thread, the system suspends the new thread until the current DLL returns from DllMain. Do not try to wait for thread while in PROCESS_ATTACH code because the system will not resume that thread until DllMain is finished. 

DisableThreadLibraryCalls will not solve this problem. Do not call WaitForSingleObject inside and DllMain function. 

(DisableThreadLibraryCalls tells the system that we do not want DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications sent to the specified DLL's  DllMain)


C/C++ Run-Time Library

Linker embeds address of DLL's entry-point function in resulting DLL file image. 
Specify address of function with linker's /ENTRY switch. 

MSFT linker assumes _DllMainCRTStartup is the entry function (a statically linked function from C/C++ run time library)

_DllMainCRTStartup initializes C/C++ run time library and then calls your DllMain. 

If the linker cannot find a DllMain function in DLL .obj files, it will link the DllMain from C/C++ run-time library. 

Sources

Windows via C/C++ - J. Richter 2008 Chapter 20

Dynamic Linked Libraries Explicit Linking

Explicit DLL Module Loading and Symbol Linking

Implicit load/linking: when loader implicitly loads/links the DLL when application is invoked. This is when application code references symbols contained in DLL

Explicit load/linking: A thread explicitly loads DLL into its calling process' address space, retrieves virtual memory address of a function contained within the DLL, and calls the function using the memory address

LoadLibraryEx to load the DLL into process' address space
GetProcAddress to indirectly reference the DLL's exported symbols

Explicitly Loading the DLL Module

Can use either LoadLibrary or LoadLibraryEx to map the DLL. They will return the virtual memory address where the file image is mapped. (HMODULE == HINSTANCE).
Mapping addresses returned by LoadLibrary and LoadLibraryEx should not be used interchangeably. 

They both increase the library's per-process usage count. 

DllMain entry point returns the virtual memory address where the file is mapped. 

LoadLibraryEx allows you to pass handle to a file and flags. 

DONT_RESOLVE_DLL_REFERENCES

Tell system to map DLL into calling process' address space without calling DllMain nor loading any additional DLLs that the DLL may require. 

LOAD_LIBRARY_AS_DATAFILE

Tells system to map DLL into proc address space as if it were a data file. System does not setup the page file permissions for a DLL module. 
Reasons to use this flag:
- DLL contains resources only and no functions
- This flag can be used to load resources from a .exe without starting a new process

LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE

Similar to LOAD_LIBRARY_AS_DATAFILE but does not let other applications modify the file's contents. 

LOAD_WITH_ALTERED_SEARCH_PATH

Changes the DLL search algorithm depending on what is passed to pszDLLPathName parameter

Can call SetDllDirectory with the library folder as a parameter as well, and the folder set will be searched after the folder containing the application. Retrieve this parameter with GetDllDirectory

LOAD_IGNORE_CODE_AUTHZ_LEVEL

Turns off validation provided by WinSafer (Software Restriction Policies)

Explicitly Unloading the DLL Module

FreeLibrary to explicitly unload DLL from process' address space
FreeLibraryAndExitThread also works

They both decrement the library's per-process usage count. The library is only unmapped from address space when usage count is 0. 

GetModuleHandle to determine if DLL is already mapped into process' address space. Returns NULL if not mapped. 

GetModuleFileName - gets the full pathname of DLL/.exe 

Explicitly Linking to an Exported Symbol

Pass the handle returned from LoadLibrary(Ex) to GetProcAddress in order to get the address where the DLL is loaded. 
pszSymbolName only accepts ANSI strings. Can either pass it the name of the symbol as an ANSI string, or pass it the ordinal number of the symbol. 

GetProcAddress(hInstDll, MAKEINTRESOURCE(2)); 

Warning: Passing an ordinal number that has not been assigned to any of the exported functions might return a non-NULL value. 

Finally, cast the function pointer into something that matches its signature. 

PEN_DUMPMODULE pFnDumpModule = (PFN_DUMPMODULE)GetProcAddress(hDll, "DumpModule"); 
if (pFnDumpModule != NULL) pFnDumpModule(hDll);

Sources

Windows via C/C++ - J. Richter 2008 - Chapter 20

Dynamic-Link Libraries Implicit Linking

Implicit Linking Overview

A DLL module must first be built before an executable module that imports from that DLL can be built. 

  1. Header file of DLL contains function prototypes, structures, symbols that the DLL wants to export
  2. Source code of DLL not required to build an executable module that imports from DLL
  3. Compiler processes each source code module and produces one .obj module per code module
  4. Linker combines .obj modules and produces single DLL image file
  5. If DLL exports at least one function or variable, linker produces a .lib file, which contains a list of exported functions and variable symbol names
  6. Linker combines .obj modules into single executable image file. 
  7. Executable image file contains an import section, which lists all needed DLL module along with their functions/symbols/variables
  8. Loader creates VA space for new process, maps executable module into address space, recursively parses executable module's import section (maps each required DLL into the address space)

Building DLL Module

C++ classes can be exported only if modules importing the C++ class are compiled using a compiler from the same vendor (so do not export C++ classes unless you know executable module uses the same developer tools). This is because of name mangling. 

Header file of DLL contains all the variables and functions DLL wants to export along with any symbols/data structures used with the exported functions/variables. 

Use single header file to include in both executable and DLL source code. Avoid exporting variables because this breaks the abstraction barrier. 

In Mylib.h: 
#ifdef MYLIBAPI
// Functions/variable definitions being exported. 
#else
// Tells the compiler that we import variable/functions from some DLL module
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif


In MyLib.cpp
// Tells the compiler  that the variable, function, C++ class will be exported from the resulting DLL module
#define MYLIBAPI extern "C" __declspec(dllexport)
// Technically the define is not necessary here because compiler remembers the symbols to export when it parses the header file
#include "Mylib.h"


Use extern "C" modifier only when writing C++ code because C++ compilers mangle function and variable names, which causes linker problems. 

extern "C" tells compiler not to mangle the variable or function names, which makes the variable/functions accessible to modules written in C, C++, or other programming languages. 

Don't define MYLIBAPI before the header file in executable since compiler will be confused. 

The __declspec(dllexport) causes compiler to embed additional info in .obj file. When DLL is linked, linker detects information about exported symbol and automatically makes a .lib file. 

The .lib file is required to link any executable module that references the DLL's exported symbols. 
Linker also embed a table of exported symbols and their relative virtual addresses (RVA) in DLL file 

RVA identifies offsets in the DLL file image to where the exported symbol can be found. 

MSFT wants you to link using symbol's name even though you can technically link by ordinal. MSFT guarantees that linking to DLLs by ordinal will work. 

DLLs for Use with Non-Visual C++

MSFT C compiler mangles C functions when your function uses __stdcall (WINAPI) calling convention. 

When you export an __stdcall function, MSFT compiler mangles function names by adding a _ and a @ followed by number of bytes that are passed to the function as a parameter. 

Example:

__declspec(dllexport) LONG __stdcall MyFunc(int a, int b); 

This function will be exported as _MyFunc@8

Building an executable and attempting to link MyFunc will fail if you do not use MSFT compiler. 

To tell MSFT compiler to not export mangled names:

Create a .def file with an EXPORTS section, which will tell linker to export using the .def file name

Or, in the DLL source code modules, add a linker directive to tell linker to export a MyFunc with the same entry point as _MyFunc@8. 
#pragma comment(linker, "/export:MyFunc=_MyFunc@8")

Building Executable Module

When including the DLL header, the __declspec(dllimport) tells compiler that some symbols are imported from some DLL module. 

Linker combines .obj modules and determines which DLL contains imported symbols. User must pass DLL's .lib file (the list of exports) to the linker, so that the linker can figure out where the referenced symbol is and which DLL module contains the symbol. 

When linker resolves import symbols, it embeds import section in the executable module's image, which lists the DLLs required and the symbols referenced from each DLL. 

A memory address next to an import appears in the executable module if the symbol is bound. 

Running Executable Module

Import section does not contain the pathname of the DLL, so loader searches for the DLL. 

Search Order:
  1. Directory of the executable image file
  2. Windows system directory (returned by GetWindowsDirectory)
  3. 16-bit system directory
  4. Process' current directory
  5. Directories listed in PATH env vars
The search order can be changed by setting HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager


Loader only maps a module once regardless of how many other modules need that module. 

When loader finishes mapping DLL modules into the proc address space, it fixes up references to import symbols:
For each symbol listed, loader examines DLL's export section to make sure symbol exists.
Loader then retrieves RVA of symbol and adds it to the base address at which the DLL module is loaded (in the process) and saves the virtual address in the executable's import section.
When code references imported symbol, it will look in the calling module's import section, and retrieve the address of the imported symbol. 

To improve application load time, rebase and bind executable and DLL modules. 

Sources

Windows via C/C++ - J.Richter 2008

Memory-Mapped Files

Physical storage from reserving regions in a memory-mapped file comes from the file itself rather than the system's paging file. A file that has been mapped can be accessed as if the file were loaded in memory.

Uses of memory-mapped files:
  1. Load and execute PE files
  2. Access data file on disk without having to perform file I/O or buffering file contents
  3. Interprocess communication between processes on the same machine (other IPC implemented using memory-mapped files anyway)

Memory-Mapped Executables and DLLs

System reserves a region of address space. That location is specified in the linker's /BASE option (defaults to 0x00400000 for 32-bit applications)

Physical storage backing reserved region is the .exe file on disk (as opposed to the system's paging file)

System recursively calls LoadLibrary on each of the DLLs that the process says it needs (as well as each DLL's dependencies). 

The DLL specifies its preferred image base (usually 0x10000000 or 0x00400000). Override this option using linker's /BASE option. Standard system DLLs specify different base addresses to avoid conflicting when loaded into a single address space. 

If system cannot load DLL at preferred base, then it uses the relocation information to load it somewhere else. If relocation information was removed (using the linker's /FIXED switch), then system will not be able to load the DLL. 

If system needs to relocate DLL, then system notes that some of the physical storage for DLL is mapped to the paging file.

Static Data is Not Shared by Multiple Instances of an Executable or DLL

Memory-mapped files allows multiple running instances of the same application to share the same code and data in RAM. When a second instance of an app is ran, system maps the pages of VA containing the file's code and data into the second app's address space. 

GetSystemInfo to determine the size of the page 

Copy-on-write allows an instance to modify global/static data without changing the global data for all other instances. 

System commits storage in the paging file for all pages that are protected with copy-on-write when the process is first loaded. 

Sharing Data Across Multiple Instances

Compiler places all code in .text, uninitialized data in .bss, and initialized data in .data. Each section marked with some combination of READ, WRITE, EXECUTE, SHARED. Sections marked SHARED are shared across multiple instances (no copy-on-write). 

Use #pragma data_seg("mysection") to create user-defined section.

Initialized data can be placed in the Shared section. If data not initialize, compiler will not place it in the Shared section unless declared with allocate declaration specifier. 

#pragma data_seg("Shared")
LONG g_lInstanceCount = 0;
#pragma data_seg()

To place uninitialized in Shared section:
__declspec(allocate("Shared")) int mydata; 

Make sure the section is first created before __declspec-ing it. 

In the /SECTION switch of the linker's command line, set /SECTION:Shared, RWS which names a section called "Shared" with Read Write Shared attributes. 

Can embed the linker switch in the source:
#pragma comment(linker, "/SECTION:Shared, RWS")

This embeds the string in the .drectve section of the generated .obj file, which makes it seem as if the string were passed as a command line argument. 

MSFT recommends not using Shared sections since anyone can load any DLL and have access to the shared memory. 

Maybe use this method instead of using a MUTEX if okay with dropping files to disk.

Memory-Mapped Data Files

To deal with large streams of data, can memory map data files to process' address space.

Open the file and tell system to reserve a region of VA space. Map the first byte of the file to the first byte of reserved region. Then access the region of virtual memory as if it contained the file. If file ends with single 0 byte, then can treat the data like it were an in-memory text string. (Interruptions during operations can cause problems and corrupt data.)

Using Memory-Mapped Files

A memory mapping is also known as a "section". Use ProcessExplorer to see sections. 

Three steps:
1. Create/open file kernel object identifying the file on disk to be used as a memory-mapped file
2. Create file-mapping kernel object 
3. Tell system to map all or part of the file-mapping into process' address space

Clean up:
1. Unmap the file-mapping kernel object from process' address space
2. Close file-mapping kernel object
3. Close file kernel object

CreateFile tells the OS the location of the file mapping's physical storage. The pathname indicates the exact location of physical storage. 

File-Sharing Modes

Flags that specify shared access to the file's data.
0 to not allow other attempts to open the file
FILE_SHARE_READ - do not allow attempts to open using GENERIC_WRITE
FILE_SHARE_WRITE - do not allow attempts to open using GENERIC_READ

CreateFileMapping

Similar to reserving address space and then committing physical storage to the region. Only difference is that physical storage from the file itself rather than the paging file.  
User must request permissions to the file-mapping object. 
For example, specifying PAGE_READONLY means that at least GENERIC_READ must have been passed to CreateFile. 

Section Attributes

SEC_NOCACHE - update the file's data on disk as you write data to the file
SEC_IMAGE - tells system to map the file with the page protections of a PE file (so PAGE_EXECUTE_READ for .text and PAGE_READWRITE for .data)
CreateFileMapping ignores SEC_RESERVE and SEC_COMMIT. 

File Mapping Size

Specify the maximum size of the file mapping: 

dwMaximumSizeHigh - 32-bit value that expresses the high 32 bits of the size (should always be 0 if the file is less than 4GB)
dwMaximumSizeLow - 32 bit value that expresses the low 32 bits of the size

Passing 0 to both params will create a file-mapping object that reflects current size of the file. This is okay if you only going to read from the file. Do not do this if the current file on disk is 0 bytes. 

pszName

Zero-terminated string assigned to the file-mapping object. The string allows sharing of the object with other processes. 

MapViewOfFile

Tells the system to reserve the region of address space for the file's data and commit the file's data as physical storage mapped to the region. 

Don't have to map the entire file into the process' address space at once. 
Map a small portion of the file - a view. 

Specify which byte in the data file should be mapped as the first byte in the view: 
dwFileOffsetHigh - high 32 bits
dwFileOffsetLow -  low 32 bits
Offset must be a multiple of system's allocation granularity. 

Specify how much of the file to map (same as specifying how large a region to reserve)
dwNumberOfBytesToMap (0 will map a view starting at specified offset to the end of the file) 

Specifying FILE_MAP_COPY will commit physical storage from the system's paging file, 

When system makes copy of original page, the copy has PAGE_READWRITE (as oppose to PAGE_WRITECOPY)

UnmapViewOfFile

Unmap the file's data, pass the base address of the returned region that was returned from MapViewOfFile.

To ensure updates written to disk, call FlushViewOfFile to force system to write a portion or all modified data to the disk image. This is useful if the memory-mapped file is over a network. Use FILE_FLAG_WRITE_THROUGH in CreateFile to ensure that server writes the file's data (and doesn't just cache it) when calling FlushViewOfFile. 

MoveMemory - to copy the page of data from the first view to a second view mapped with PAGE_READWRITE if one wants to save changed page of data. 

Make sure to close all open handles. 

Coherence

As long as we are mapping the same file-mapping object, the view data is guaranteed to be coherent meaning that if one application alters the contents of the file in one view, all other views are updated. 

Windows does not guarantee that view of different file-mapping objects backed by a single data file are coherent - only that multiple views of a single file-mapping object are coherent. 

When calling CreateFile for files that will be memory mapped, specify 0 in dwSharedMode to prevent other processes from opening it. 

Do not use memory-mapped files to share writable files over a network because system can't guarantee coherent view of data. 

Specifying the Base Address of Memory-Mapped File

MapViewOfFileEx to suggest that file be mapped into a particular address in the process' VA space. 
Specify the address in pvBaseAddress parameter. Useful for using memory-mapped files to share data with other processes. 

Using Memory-Mapped Files to Share Data Among Processes

IPC mechanisms such as RPC, COM, OLE, DDE, window messages (WM_COPYDATA), Clipboard, mailslots, pipes, sockets, etc are all based on memory-mapped files :) 

Have the two processes that want to share data map views of the same file-mapping object. This makes them share the same pages of physical storage. (Remember to use the exact same name for the file-mapping object). 

When system starts an application:
1. Calls CreateFile to open the .exe file on disk
2. CreateFileMapping 
3. MapViewOfFileEx with SEC_IMAGE flag and specified to map at the preferred base address
4. Create primary thread and point it to first byte of executable code of the mapped view

A second instance of the same application doesn't create a new file object or file-mapping object. The system simply maps a view of the file a second time in the context of the new process' address space. 

Memory-Mapped Files Backed by Paging File

For creating memory-mapped files backed by system's paging file rather than hard disk file. 

Call CreateFileMapping and pass INVALID_HANDLE_VALUE into hFile. System will create file-mapping object and commit physical storage from paging file. 

To share with other process, specify name in pszName. Other process can use OpenFileMapping to access the storage. 

Call CloseHandle when done. 

When file-mapping object is destroyed, data written to the file-mapping storage is destroyed by the system. 

SEC_RESERVE and SEC_COMMIT

These flags only applicable to file-mapping objects backed by paging file. They allow the creation of a file-mapping object without having to commit all the physical storage upfront. 

SEC_COMMIT commits storage from system paging file. 

SEC_RESERVE does not commit physical storage from the paging file. Calling MapViewOfFile(Ex) then reserves a region but does not commit physical storage back to the region. Attempts to access memory address in reserved region raises an access violation. 

Other threads can use the same file-mapping object to map a view of the same region. 

Use VirtualAlloc to commit physical storage to the region. All other processes that have mapped a view of the same file-mapping object can now successfully access the committed pages. 

Sources:

Jeffrey Richter, Christophe Nasarre - Windows via C/C++ 2008 - Chapter 17

Tuesday, October 15, 2019

Windows Memory Architecture


Virtual Address Space of a Process

Each process has its own address space. 
32-bit processes have 4GB
64-bit processes have 16 EB

Virtual address space is not physical storage. It is just a range of addresses. One does not simply access data without mapping physical storage to portions of the address space. 

Virtual Address Space Partitions

Partitions based on the implementation of the OS.

Null-Pointer Assignment Partition (0x00000000-0x0000FFFF)

This partition is purposely set aside to catch NULL-pointer assignments. Virtual memory in this range may not be reserved with Win32 API functions.

User-Mode Partition

This is the usable address range. The size depends on the CPU architecture. 
x86 0x00010000-0x7FFEFFFF ( ~ 2GB)
x64 0x00000000'00010000 - 0x000007FF'FFFEFFFF (~8192 GB)

Kernel-Mode Partition 

x86 0x80000000-0xFFFFFFFF
x64 0x00000800'00000000 - 0xFFFFFFFF'FFFFFFFF

Contains the OS code (thread scheduling, memory management, file systems support, networking support, device drivers) 

Everything in this partition is shared among all processes.

Regions in Address Space

Most of the address space is free when process is created. 
Use VirtualAlloc to allocate (reserve) regions within the address space. 

Allocation requests rounded to a 64-KB boundary. 
When reserving a region, the size of the region needs to be a multiple of the system's page size (usually 4-KB but IA-64 uses 8-KB). System will round up the request. 

System allocates PEB on behalf of the process.
System also allocates TEB upon thread creation and destruction. 
System can allocate wherever it wants, so PEB and TEB will not likely start on 64-KB boundary (but the size of the regions will still be a multiple of CPU page size) 

Call VirtualFree when the region is no longer needed. 

Committing Physical Storage Within a Region

Physical storage must be committed to a reserved region in order to actually use the region. To commit physical storage, call VirtualAlloc. It is not required to commit physical storage to the entire region. 

Decommit physical storage with VirtualFree.

Physical Storage and the Paging File

RAM is expensive, so OS can make disk space look like RAM. 

A paging file contains virtual memory available to all processes and is located on disk. 

CPU needs to know if memory that a thread is requesting to access is in RAM or on disk. 

OS and CPU save portions of RAM to paging file and loads portions of paging file back into RAM when requested.  

When an application commits memory with VirtualAlloc, that physical space is allocated from a file on the hard disk. 


When thread tries to access data in process' address space:

- if data is in RAM
   translate virtual memory to physical address in memory
- if data not in RAM but in paging file
   page fault

When there is a page fault, OS locates free page of memory in RAM (or frees one if there are none), loads the data from the paging file to the free page in RAM. The OS then updates the translation table to indicate the physical location of the newly loaded data. Try the instruction that caused the page fault again. 

Physical Storage Not Maintained in the Paging File

When invoking an application, system reserves a region of address space and uses the contents (image) of the .exe file as the program's reserved region of address space. 

A memory-mapped file refers to a program's file on the hard disk that the system is using as the physical storage for a region of address space. The system maps the file's image to the reserved region of address space when the PE file is loaded. 

Using multiple page files on different physical hard drives is actually nice because OS can write to multiple drives simultaneously. Add/Remove paging files in Virtual Memory settings. 

System only copies images from removable media to RAM if the image is linked using /SWAPRUN:CD or /SWAPRUN:NET. 

Protection Attributes

Only PAGE_EXECUTE_* memory can execute without throwing access violation (when DEP enabled). 

Copy-on-Write Access

PAGE_WRITECOPY and PAGE_EXECUTE_WRITECOPY are used by OS when mapping PE files to images. Do not pass these to VirtualAlloc. 

When module is first mapped to the process' address space, the system allocates storage for all potential writable pages. Paging file storage not used unless module's writable pages are actually written to. 

When attempting to write to shared block, system finds one of the pages that it allocated when the module was loaded for writing. It then copies contents of the page to the free page so that original page does not have to change. 


Special Access Protection Attribute Flags

PAGE_NOCACHE - disables caching of committed pages (usually for hardware device-drivers)

PAGE_WRITECOMBINE - allows multiple writes to single device to be combined 

PAGE_GUARD - allows an app to be notified when a page has been written to

These attributes are never associated with a region, but can be associated with a block.

A block is a set of contiguous pages that have the same protection attributes and are backed by the same type of physical storage.

GetMappedFileName to get regions backed by data files
Use ToolHelp functions to get regions backed by executable files

Memory Region Types

Free - region not backed by any storage and not reserved
Private - region backed by system's paging file
Image - region backed by memory-mapped image file (.exe or DLL). Not necessarily backed by image file since copy-on-write mechanism will change the region to be backed by a page from the paging file. 

Sources:

Windows via C/C++ - J.Richter, 2008. Chapter 13

Leetcode: Meeting Rooms 2

Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] find the minimum number of conference rooms required.

Example


Notice how we need 4 meeting rooms because a maximum number of 4 intervals overlap even though there are more than a total of four overlapping intervals.

Approach


The idea is to count the maximum number of overlapping intervals. First, sort the intervals in order of finishing time. O(n lg n) Second, maintain a min heap of intervals sorted by finishing time (earliest finishing time is at the top). This min heap represents the meeting rooms. Process each interval. At each interval, if the current interval starts after the last interval scheduled finishes, then we can replace that interval with the current interval. Otherwise, we must allocate another room.

Process Doppelganging Notes

This post consists of notes on the process doppelganging technique.

One of the limitations of the Process Hollowing technique is its use of API calls: NtUnmapViewOfSection, VirtualProtectEx, and SetThreadContext. Process doppelganging eliminates the need to make these API calls nor unmap the main module of a process.

The process doppelganging technique was first presented at blackhat Europe 2017 by researches from enSilo. It is a method for PE injection. It leverages Windows Transactional NTFS (TxF), which was introduced as a way to perform safe file operations.

The main reasons to use process doppelganging is that it bypasses whitelisting policies, does not unmap code,  does not need manual mapping, can be used to load DLLs. For these reasons, process doppelganging appears highly promising as a first stage loader.

Transactional NTFS (TxF) Concepts

TxF is essentially an implementation that solves the readers-writer problem as applied to file systems. The possible parties involve are the transacted writer, non-transacted writers, transacted readers and non-transacted readers.

TxF binds a file handle to a transaction. TxF allows only one transacted writer at a time. All other attempts to open the file are blocked. Non-transacted writers are always blocked.

Non-transacted readers will not see changes until the transacted writer completes the transaction. They see the changes since the last transaction on the file. When the transacted writer commits the transaction, non-transacted readers will receive the updated file. Non-transacted readers are the AV processes.

In contrast, transacted readers see the last commit to the file even if the transacted writer has not completed the transaction. In contrast to the non-transacted readers, transacted readers see a consistent view of the file. They do not receive updates when the transacted-writer completes the transaction unless they close and reopen the handle.

Process doppelganging leverages the fact that nontransacted readers can only see the file since the last transaction. By rolling back changes, our changes to the transacted file will not be saved to disk.

Also CreateProccess creates sections with PAGE_EXECUTE. AV scans for sections with PAGE_EXECUTE. We create with PAGE_READ.

Process Doppelganging as presented at blackhat

High Level

  1. Open the legit executable and overwrite it with ours
  2. Load malicious executable by creating a section in the transacted file
  3. Rollback to get original executable
  4. Resume Thread

Details

  1. Use CreateTransaction to create a transaction
  2. Get the transacted file handle with CreateFileTransacted 
  3. Write our executable into the transacted file
  4. Use NtCreateSection to create section from the transacted file to point to our executable
  5. RollbackTransaction to remove our changes
  6. NtCreateProcessEx, NtCreateThreadEx (using our entry point - this is Windows XP CreateProcess)
  7. RtlCreateProcessParametersEx 
  8. VirtualAllocEx with PAGE_READWRITE
  9. Write parameters to remote process
  10. NtResumeThread
Windows XP uses NtCreateProcessEx to create a process. This function takes a handle to a section (as oppose to a handle to the file path of the executable). Also, the user is responsible for setting up process parameters. The Windows 10 loader uses NtCreateUserProcess, which takes a file path. However, NtCreateProcessEx is still available because Windows creates minimal processes with it.

Avoid AV Hooks

NTDLL is the usermode wrapper for syscalls. AV will often monitor functions exported by NTDLL. One solution is to load our own copy of NTDLL. The authors of the Osiris Banking Trojan loaded the ntdll.dll file from disk as a section. This maps the DLL as an image, which is good because manually loading DLLs makes them stand out from normally-loaded DLLs.
  1. NtCreateFile to open ntdll.dll
  2. NtCreateSection
  3. ZwMapViewOfSection to map the section to the our address space

Process Doppelganging and Process Hollowing loader

The Osiris Banking Trojan uses a combination of process doppelganging and process hollowing.

  1. Create the suspended process (use CreateProcessInternal from Kernel32)
  2. Map the image of the suspended process to our address space (using same ntdll technique)
  3. Create the transaction with ZwCreateTransaction from NTDLL
  4. Open the transacted file by calling RtlSetCurrentTransaction and ZwCreateFile
  5. Write our executable to the file with RtlSetCurrentTransaction and ZwWriteFile
  6. Create a section from the transacted file (ZwCreateSection)
  7. Rollback the file with ZwRollbackTransaction 
  8. ZwQuerySection
  9. NtClose, NtClose handles
  10. ZwMapViewOfSection to map the section from the transacted file to the suspended process
  11. ZwProtectVirtualMemory, ZwWriteVirtualMemory to patch PEB.ImageBase
  12. ZwProtectVirtualMemory, ZwWriteVirtualMemory to patch Entry Point
  13. If Entry Point patch does not work, then do ZwGetThreadContext, ZwSetThreadContext
  14. ZwResumeThread

Limitations

TxF is technically deprecated although it is still enabled as of Windows 10.

The CreateTransaction, CreateFileTransacted, RollbackTransaction calls are not commonly used. AV might detect this technique by monitoring these functions.

Original method requires the Windows XP NtCreateProcessEx and NtCreateThreadEx functions.

Other Notes

Sandboxing may be detected if the executable is not launched from its original directory.

Clear event logs with EvtOpenChannelEnum, EvtNextChannelPath, EvtClearLog from Wevtapi.dll.


Sources

https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf

https://blog.malwarebytes.com/threat-analysis/2018/08/process-doppelganging-meets-process-hollowing_osiris/

https://attack.mitre.org/techniques/T1186/

https://securelist.com/synack-targeted-ransomware-uses-the-doppelganging-technique/85431/

Process Hollowing Notes


This post consists of notes on implementing the process hollowing code injection technique.

The main reason to use the process hollowing technique is to bypass white list application policy. Process hollowing disguises a malicious process as a legitimate one.

High Level Technique

  1. Create suspended process
  2. Deallocate executable section of the process
  3. Write our executable to the remote process
  4. Overwrite the base address in the remote process' PEB with that of our executable
  5. Change the entry point of suspended thread
  6. Resume thread


Detailed Technique

  1. Download second stage executable and store in some local buffer
  2. CreateProcess in SUSPENDED mode
  3. Retrieve thread context of the suspended process with GetThreadContext
  4. Read PEB.ImageBaseAddress to get the address where the remote process is loaded 
  5. Resolve the address of NtUnmapViewOfSection from ntdll.dll
  6. Hollow the suspended process using NtUnmapViewOfSection
  7. Now allocate memory in the suspended process at the address for which our executable wants to load (our image base)
  8. Decrypt our second stage executable (if it is encrypted) 
  9. Write the PE header of our executable into the allocated process memory
  10. Write each section of the executable file (.text, .rdata, .data) into allocated memory
  11. Overwrite PEB.ImageBaseAddress to point to our image base 
  12. Use SetThreadContext to overwrite the entry point in the suspended thread's context with our executable's entry point
  13. ResumeThread 

Limitations

The primary way that process hollowing gets detected is from unmapping the remote process' main module.

The other way to be detected is by allocating pages with PAGE_EXECUTE_READWRITE permissions. Mitigate this by allocating only READWRITE permissions first. And then change to READEXECUTE when finished copying.

If using svchost.exe, it looks suspicious for svchost.exe to not be started by services.exe
Possible solution: change parent process ID?

The size differences in our svchost vs normal svchost is suspicious.


Sources:


https://cysinfo.com/7th-meetup-reversing-and-investigating-malware-evasive-tactics-hollow-process-injection/

https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf