Module

A Module object represents the different programs that form our process, that means:

  • The program itself
  • Imported libraries (DLLs, Shared Objects, Shared Libraries, etc)
  • Other imported files

Module objects are returned when we use Process.enumerateModulesSync or Process.enumerateModules, this object have the following properties:

  • base: Base address where the module is loaded in memory
  • name: Name of the module, usually it’s the name of the file but it may be missing if it was manually loaded in memory (f.e. loading an encrypted library)
  • path: Absolut path of the loaded file, may be missing too
  • size: Size in memory of the module

Looking for a function in modules

One of the most important features about the Module module (No pun intended) is that it allows us to look for exported functions/methods and get its address in memory which we can use later to call that function or change its behaviour

1
2
3
4
5
6
7
8
 var function_ptr = Module.findExportByName('module_name', 'function_name');

 // In case we don't know which module has the function we can use ``null``
 // but note that it may impact the performance if there are a lot of modules
 var function_ptr = Module.findExportByName(null, 'function_name');

 var function_ptr = Module.getExportByName('module_name', 'function_name');
 var function_ptr = Module.getExportByName(null, 'function_name');

This returns a NativePointer object. The only difference between getExportByName and findExportByName is that the former will raise an exception if the function is not found while the later will just return an undefined object.

Getting all exports in a module

Sometimes we won’t know exactly which export we’re looking for, for this cases it may be useful to get a list of all the available exports in a certain module.

There are two kind of exports: function and variable. The names are self explanatory.

Getting imports from a module

In the same way that a module exports some of its functions and variables it may be importing functions and variables from other modules. For example, we may be interested in looking the function imported by the main module:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
 var nc = Process.enumerateModulesSync()[0];
 Module.enumerateImportsSync(nc.name);
 >>> [
 >>> {
 >>>     "address": "0x7fa8a8f7c010",
 >>>     "module": "/lib/x86_64-linux-gnu/libc-2.27.so",
 >>>     "name": "__snprintf_chk",
 >>>     "type": "function"
 >>> },
 >>> {
 >>>     "address": "0x7fa8a8fd4f80",
 >>>     "module": "/lib/x86_64-linux-gnu/libc-2.27.so",
 >>>     "name": "strcasecmp",
 >>>     "type": "function"
 >>> },
 >>> {
 >>>     "address": "0x7fa8a8e6bf20",
 >>>     "module": "/lib/x86_64-linux-gnu/libc-2.27.so",
 >>>     "name": "__errno_location",
 >>>     "type": "function"
 >>> },
 >>> ...

Using the ModuleMap

Usually we won’t be interested in looking into the entire process, often we’ll just neet to look at a few modules that belong to the application we’re analyzing but we don’t care about some dependencies - f.e. ld.so. To avoid scanning all those modules innecesarily, we can create a ModuleMap that only has the modules we want:

1
2
3
4
 var my_map = new ModuleMap(function(mod){
     if(mod.name == 'nc.openbsd' || mod.name == 'libc-2.27.so')
         return true; // We return true for the modules we want to keep
 });

The map creates a snapshot of the currently imported modules and will use this cached version to respond our queries.

Some useful functions:

  • my_map.update(): Update the snapshot in use keeping same filters.
  • my_map.find(address) or my_map.get(address): Returns the module that address belongs to (The former returns null if none module matches while the later will raise an exception);
  • my_map.values(): Returns an array with all the mapped modules

The we can make bulk operations on all the mapped modules at once:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 my_map.values().map(function(mod){
     return Module.enumerateExportsSync(mod.name);
 });
 >>> [
 >>>   [
 >>>     {
 >>>         "address": "0x5601ae4db360",
 >>>         "name": "uflag",
 >>>         "type": "variable"
 >>>     },
 >>>     {
 >>>         "address": "0x5601ae4db378",
 >>>         "name": "Pflag",
 >>>         "type": "variable"
 >>>     },
 >>>     {
 >>>         "address": "0x5601ae4db1f0",
 >>>         "name": "minttl",
 >>>         "type": "variable"
 >>>     },
 >>>     {
 >>>         "address": "0x5601ae4db250",
 >>>         "name": "Cflag",
 >>>         "type": "variable"
 >>>     },
 >>>     {
 >>>         "address": "0x5601ae4db3a4",
 >>>         "name": "tflag",
 >>>         "type": "variable"
 >>>     },
 >>>     ...
 >>>   ],
 >>>   [
 >>>     {
 >>>         "address": "0x7fa8a8fb0520",
 >>>         "name": "__libc_dlsym",
 >>>         "type": "function"
 >>>     },
 >>>     {
 >>>         "address": "0x7fa8a8f62200",
 >>>         "name": "__endmntent",
 >>>         "type": "function"
 >>>     },
 >>>     {
 >>>         "address": "0x7fa8a8f08730",
 >>>         "name": "wcstoq",
 >>>         "type": "function"
 >>>     },
 >>>     {
 >>>         "address": "0x7fa8a8f586d0",
 >>>         "name": "pwrite",
 >>>         "type": "function"
 >>>     },
 >>>     {
 >>>         "address": "0x7fa8a8e896b0",
 >>>         "name": "sigstack",
 >>>         "type": "function"
 >>>     },
 >>>     ...
 >>>   ]
 >>> ]

Other interesting functions

  • Module.getBaseAddress(name) and Module.findBaseAddress(name): Returns the base address of the module name
  • Module.ensureInitialized(name): It ensures that the module has been initialized before continuing execution. This is particularly useful when attempting early instrumentation but we still need a specific module available.
  • Module.enumerateRanges(protection_object): Same as Process.enumerateRanges(protection) but specific to a single module.