Video Tutorial Manual Mapping DLL Injection Tutorial - How To Manual Map

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

Ctrl+Alt+1337

Dank Tier Donator
Sep 2, 2018
34
688
0
Great tutorial, Broihon.

Can someone explain what is happening at this stage in the video 1/4 (6:10):

C++:
using f_LoadLibraryA    = HINSTANCE    (WINAPI*)(const char * lpLibFilename);
using f_GetProcAddress    = UINT_PTR    (WINAPI*)(HINSTANCE hModule, const char * lpProcName);
using f_DLL_ENTRY_POINT = BOOL        (WINAPI*)(void * hDll, DWORD dwReason, void * pReserved);
I thought the using keyword was used for namespaces and typedefs only. Sorry for the basic question but really want to understand everything you do and couldn't find any relevant explaination online.

Happy new year :)
 

M47Z

Full Member
Aug 26, 2019
43
2,178
4
Great tutorial, Broihon.

Can someone explain what is happening at this stage in the video 1/4 (6:10):

C++:
using f_LoadLibraryA    = HINSTANCE    (WINAPI*)(const char * lpLibFilename);
using f_GetProcAddress    = UINT_PTR    (WINAPI*)(HINSTANCE hModule, const char * lpProcName);
using f_DLL_ENTRY_POINT = BOOL        (WINAPI*)(void * hDll, DWORD dwReason, void * pReserved);
I thought the using keyword was used for namespaces and typedefs only. Sorry for the basic question but really want to understand everything you do and couldn't find any relevant explaination online.

Happy new year :)
that above is exactly the same as
C++:
typedef HINSTANCE (WINAPI* f_LoadLibraryA)(const char * lpLibFilename);
...
because using can be used to declarate type alias and alias template aswell.
 
  • Like
Reactions: Ctrl+Alt+1337

Ctrl+Alt+1337

Dank Tier Donator
Sep 2, 2018
34
688
0
hi, if i try to inject a dll more than 1mb, the injector will crash :( only work with sizes under 1mb
Sorry for replying to an old message but didn't see it answered anywhere. @Broihon writes a size check in video 2/4 (3:45)

C++:
auto FileSize = File.tellg();
if (FileSize < 0x1000)
{
    printf("Filesize is invalid.\n");
    File.close();
    return false;
}
Just edit the size (0x1000 = 1MB) and it should work fine
 
Last edited by a moderator:
  • Like
Reactions: Rake

terrible_c0der

Fleep Tier Donator
Jan 21, 2020
2
2
0
Sorry if I missed it during the video(s), but why do we need to pass the address of LoadLibraryA and GetProcAddress with MANUAL_MAPPING_DATA?

The address of the functions MANUAL_MAPPING_DATA are set to the addresses of the respective functions in the injector process.

When the shell code executes in the target process, why would these function addresses from the injector process still work at all?

I'm able to see why calling LoadLibraryA or GetProcAddress directly in the shell code would fail, since the addresses of these functions will be different in the target process and the injector process. Why given the way that we initialize MANUAL_MAPPING_DATA, why do we not face the same issues, given that it seems like we're not doing anything that would fix the aforementioned issue?

C++:
    MANUAL_MAPPING_DATA data{ 0 };
    data.pLoadLibraryA        = LoadLibraryA;
    data.pGetProcAddress    = reinterpret_cast<f_GetProcAddress>(GetProcAddress);
 

terrible_c0der

Fleep Tier Donator
Jan 21, 2020
2
2
0
Sorry for replying to an old message but didn't see it answered anywhere. @Broihon writes a size check in video 2/4 (3:45)

C++:
auto FileSize = File.tellg();
if (FileSize < 0x1000)
{
    printf("Filesize is invalid.\n");
    File.close();
    return false;
}
Just edit the size (0x1000 = 1MB) and it should work fine
Doesn't tellg() return the position in bytes? So wouldn't 0x1000 bytes be like 4 kilobytes, not 1 megabyte?
 

0xDEC0DE

retired
Dank Tier VIP
Fleep Tier Donator
Dank Tier Donator
Oct 28, 2018
449
18,798
90
Sorry if I missed it during the video(s), but why do we need to pass the address of LoadLibraryA and GetProcAddress with MANUAL_MAPPING_DATA?

The address of the functions MANUAL_MAPPING_DATA are set to the addresses of the respective functions in the injector process.

When the shell code executes in the target process, why would these function addresses from the injector process still work at all?

I'm able to see why calling LoadLibraryA or GetProcAddress directly in the shell code would fail, since the addresses of these functions will be different in the target process and the injector process. Why given the way that we initialize MANUAL_MAPPING_DATA, why do we not face the same issues, given that it seems like we're not doing anything that would fix the aforementioned issue?

C++:
    MANUAL_MAPPING_DATA data{ 0 };
    data.pLoadLibraryA        = LoadLibraryA;
    data.pGetProcAddress    = reinterpret_cast<f_GetProcAddress>(GetProcAddress);
Actually they call the exact same code inside of your pc memory.

The address for windows modules is the same in every process, windows does not load a copy for every process but instead references the existing module.

Therefore your functions like LoadLibraryA will have the same offset in the injector and in the target process.
 
  • Like
Reactions: terrible_c0der

Broihon

edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
1,744
40,528
316
Sorry if I missed it during the video(s), but why do we need to pass the address of LoadLibraryA and GetProcAddress with MANUAL_MAPPING_DATA?

The address of the functions MANUAL_MAPPING_DATA are set to the addresses of the respective functions in the injector process.

When the shell code executes in the target process, why would these function addresses from the injector process still work at all?

I'm able to see why calling LoadLibraryA or GetProcAddress directly in the shell code would fail, since the addresses of these functions will be different in the target process and the injector process. Why given the way that we initialize MANUAL_MAPPING_DATA, why do we not face the same issues, given that it seems like we're not doing anything that would fix the aforementioned issue?

C++:
    MANUAL_MAPPING_DATA data{ 0 };
    data.pLoadLibraryA        = LoadLibraryA;
    data.pGetProcAddress    = reinterpret_cast<f_GetProcAddress>(GetProcAddress);
The mapped shellcode needs LoadLibraryA and GetProcAddress to fix the imports of the dll. If instead I called LoadLibraryA and GetProcAddress directly the mapped code would fail since in the context of the target process it doesn't have access to any IAT or relative calls wouldn't work since the position of the shellcode is more or less random.
But the addresses of LoadLibraryA and GetProcAddress are basically static since the kernel32.dll is basically always at the same address in all processes. This means the address of LoadLibraryA and GetProcAddress is the same in the injector process and in the "injectee" process. Saving the addresses to the manual mapping data allows the shellcode to access those addresses and call them.
To be super safe you could enumerate the injectee process' modules, find kernel32.dll, search the export directory for LoadLibraryA and GetProcAddress and use those addresses but since it actually never happened to me that the kernel32.dll is at a different address this just isn't necessary.

tellg() returns the current stream pointer which in this case points to the end of the file thus giving us the filesize in bytes. Since in modern portable executables the first 4096 (0x1000) bytes are reserved for the PE header it's just a simple check that noone tries to pass an invalid file to the injector.
 

.visual

Dank Tier Donator
Full Member
Dec 24, 2018
15
378
0
Sorry for bumping this thread but I'm trying to code a Manual Mapper and truly understand each step it does.
In the past weeks I've read dozens of articles about the PE file format and topics related to that but I still have some questions that google couldn't answer:
  1. Why do we need to map sections?
  2. How does one find out which Data Directories need to be worked on? Cause as I see there are even more than the ones needed in Manual Mapping.
  3. Coding question:
    // Why doesn't data override pSrcdata here?
    memcpy(pSrcData, &data, sizeof(data));
    // How do we know that the first 1000 bytes are reserved for the headers?
    WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr);
  4. And finally: Why do we have to Do relocations, Fix imports and execute tls callbacks in the shellcode? (I know this already got asked but I didn't quite get the answer.)
Sorry for the newb questions but I got kinda stuck here.
 

Broihon

edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
1,744
40,528
316
Sorry for bumping this thread but I'm trying to code a Manual Mapper and truly understand each step it does.
In the past weeks I've read dozens of articles about the PE file format and topics related to that but I still have some questions that google couldn't answer:
  1. Why do we need to map sections?
  2. How does one find out which Data Directories need to be worked on? Cause as I see there are even more than the ones needed in Manual Mapping.
  3. Coding question:
    // Why doesn't data override pSrcdata here?
    memcpy(pSrcData, &data, sizeof(data));
    // How do we know that the first 1000 bytes are reserved for the headers?
    WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr);
  4. And finally: Why do we have to Do relocations, Fix imports and execute tls callbacks in the shellcode? (I know this already got asked but I didn't quite get the answer.)
Sorry for the newb questions but I got kinda stuck here.
1. The PE file on disk is "compressed". Some sections don't even exist in the raw file. The section header contains the required information for the "runtime version" of the file.

2. I originally followed this write-up by Joachim Bauch when I started learning about manual mapping. There are a lot of different directories, some just aren't necessary, some exist as part of legacy code, some are for backwards compatibility and aren't used at all by modern compilers (bound imports for example). This tutorial ONLY covers the basics of manual mapping which means it works for most DLLs. This is because back then when I made the tutorial I didn't have enough knowledge to cover a complete PE loader but a decent PE loader would also take like 20 parts. For an essentially complete PE loader check out this PE loader made by DarthTon. Another great library to look at is @Akaion's Bleak library (C# and deprecated but still good) and the new library Lunar.

3. It does. The data structure is only 12 / 24 bytes big and the first 12 / 24 bytes of the DOS header are irrelevant at this point. For the sake of simplicity I've decided to avoid additional memory allocations and use already allocated memory to the store the required data. It's not nice but makes things a little easier. Also I was retarded when I created this tutorial.

4. The first 0x1000 bytes or the first 4k section of a PE are always reserved for the header. There are some super hacky ways to change this or some super old PEs with different sizes reserved for the headers but this is essentially irrelevant. I've never encountered a PE which's header sections wasn't 4k big (ignoring stuff like this).
 
  • Like
Reactions: .visual and Rake

Akaion

Wizard
Meme Tier VIP
Trump Tier Donator
Oct 13, 2018
230
7,948
14
Sorry for bumping this thread but I'm trying to code a Manual Mapper and truly understand each step it does.
In the past weeks I've read dozens of articles about the PE file format and topics related to that but I still have some questions that google couldn't answer:
  1. Why do we need to map sections?
  2. How does one find out which Data Directories need to be worked on? Cause as I see there are even more than the ones needed in Manual Mapping.
  3. Coding question:
    // Why doesn't data override pSrcdata here?
    memcpy(pSrcData, &data, sizeof(data));
    // How do we know that the first 1000 bytes are reserved for the headers?
    WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr);
  4. And finally: Why do we have to Do relocations, Fix imports and execute tls callbacks in the shellcode? (I know this already got asked but I didn't quite get the answer.)
Sorry for the newb questions but I got kinda stuck here.
Adding some more information onto Broihons answer

- Sections are just 'blocks' of data that are contained in the DLL. For example, the .reloc section holds all the data needed to do the relocations, the .rdata holds all data that needs to be in read-only memory etc.

- Almost all the data directories contain information that is used in PE loading, however, as Broihon mentioned some of them are never really used i.e. Bound Imports. As figuring out what needs to be used, a combination of stepping through a debugger and analysing various compiled libraries to see what the compiler has constructed.

- There's a field in the PEHeaders called SizeOfHeaders that tells you the exact size of the headers in that DLL (which is always 0x1000 or 4096 bytes in modern libraries.) Broihon was just lazy and decided to hardcode it.

In regards to your last question, manual mapping can be done in various ways - Internally via shellcode (Broihons way) or externally (my way and Darthtons way) or even a mix of both

- Relocating a PE is just the process of updating a bunch of pointers in various sections so is usually done before the sections are mapped over to the remote process. You can also do it in shellcode though it just needs to be done before you call DLL Main.

- Building the IAT aka Fixing imports is just acquiring the addresses of all the imported functions (in the remote process) and writing them into the IAT of the PE which again can be done before mapping the sections over. This step also requires any dependencies be mapped or loaded into the process if they aren't already loaded so you use the imports needed.

- Calling TLS callbacks obviously has to be done in the context of the remote process as they are routines that are called upon DLL loading (usually) and as they require more than 1 parameter you have to build shellcode to call these
 
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods