19

The title says most of it. Say I have a Windows PE (x86, 32bit) binary (just so we have case to discuss), the imports list will usually only show the imports found in the import directory. The attributes it shows are address of the function, name and library from which it got imported as shown in this screenshot snippet:

Screenshot of import tab in IDA Pro

Is there a way for me through scripting (IDC or Python, I don't care too much), to add imports of my own to the list and, for example, have them point (the address attribute) to code such as this (highlighted line)?:

Dynamically imported function in IDA Pro

I.e. the line would in such case look like:

0DCBA987     GetLongPathNameW       kernel32.dll

or even just

0DCBA987     GetLongPathNameW       ???

assuming the above call GetProcAddress would be at address 0DCBA987.

The advantage to me would be readability. But it would also yield a more comprehensive list of imports (and consequently xrefs) as some functions are frequently imported dynamically due to their availability in the various Windows versions.

It should be quite trivial given a certain binary to figure out all xrefs to candidate functions that retrieve the imported function's address (such as GetProcAddress) and then walk their calls to find which function was imported. The DLL part may be more complicated to find out, but it could be left empty or entered manually. But I didn't find a function that would allow me to add imports. Is there a way?

6
  • 2
    Interesting question, that would be useful when analyzing a program from a memory dump. Having the ability to edit the Imports would add cross-references and make the analysis easier.
    – ekse
    Commented May 30, 2013 at 9:17
  • 4
    If you haven't seen it you should check out reiat.py. It does most of what you discussed except adding to the import tabs.
    – alexanderh
    Commented May 30, 2013 at 19:58
  • @alexanderh: hadn't, although I saw some of the other repos you have on Bitbucket before. Do you have any idea how to get that last step done?
    – 0xC0000022L
    Commented May 30, 2013 at 20:42
  • 1
    @0xC0000022L No, I do not. I'll try to take a crack at it this weekend. My email is in the source code. Feel free to email the sample if you can share it. Stupid side note, the comment "GetLongPathNameW" can be used to gather non-commented cross-references via hot-key X. Not useful for automated analysis but still helpful.
    – alexanderh
    Commented May 31, 2013 at 22:09
  • You could try updating import_node (exported from the SDK's nalt.hpp), but it's unclear if IDA re-reads those netnodes every time it redraws the list of imports. Commented Mar 24, 2015 at 17:43

3 Answers 3

13

I don't know if many people know this, but IDA uses some kind of a trick to decide if a segment is an imports segment, and handles it completely different.

IDA uses a few segment properties to treat a segment as an imports segment. for example, naming a segment .idata, or setting the segment's class to XTRN will immediately make it an imports segment. such segments won't display the usual code/data listings, function definitions and most of the other stuff we're used to in the Code View.

More specifically, adding code to those segments will be hidden in IDA, which will refuse displaying any assembly in those segments. A nice Anti-IDA trick I'd say :)

Instead, they'll only display name definitions, offsets and comments. Once an offset is assigned a name of an API stored in IDA's database, IDA will get the API's prototype and other info, assign type definition and comments.

IIRC These APIs will also be shown in the imports window but i'm not sure what exactly triggers this. it might also depend on IDA versions and other import-related PE properties.

EDIT
It's been more than three years and obviously I couldn't find the script then, and I just went over some old code of mine and were able to reconstruct what I did in that script, and here is the gist of it:

import idaapi
import idc

for import_rva, import_name in LIST_OF_IMPORTS:
    ea = imagebase + import_rva

    if idaapi.get_segm_class(getseg(ea)) == "XTRN":
        print("import is already inside an XTRN segment, "
              "assuming it's correctly named")
        continue
    elif idaapi.get_segm_class(getseg(ea-1)) == "XTRN":
        print("Import is just below an import segment, "
              "extending segment to include this additional import")
        # shrinking it's current segment
        # WARNING: this assumes current import is at the top of its segment
        # otherwise we'll have to SPLIT the import's current segment
        # and to that I say CBA aka left as an exercise to the reader
        idaapi.set_segm_start(ea, SegStart(ea)+4, 0)
        # expanding it's new segment
        idaapi.set_segm_end(ea-1, SegEnd(ea-1)+4, 0)
    else:
        print("Creating new segment for import")
        idc.AddSeg(ea, ea+4, 0, 1, 4, 0)
        idc.SetSegClass(ea, "XTRN")

    # renaming import to API name. This will make IDA add type
    # information and automatic comments for any function it's
    # familiar with
    idc.MakeName(ea, import_name)
    # Making it an offset to have IDA show it as an import instead
    # of hiding it
    idc.MakeDword(ea)
4
  • @Nirlzr: by all means, I'd be curious to see this answer fleshed out a bit. Thanks.
    – 0xC0000022L
    Commented May 31, 2013 at 21:56
  • @Nirlzr: any news concerning the mentioned script?
    – 0xC0000022L
    Commented Jun 7, 2013 at 14:49
  • 2
    @0xC0000022L: Terribly sorry! I'm currently in reserve duty and I can't get to stuff I have at work. didn't forget about it.
    – NirIzr
    Commented Jun 7, 2013 at 15:36
  • 3
    @0xC0000022L I know it's been a long time (and that's an understatement), but I've edited my answer to include the script)
    – NirIzr
    Commented Oct 21, 2016 at 3:28
8
+150

if adding an extra import section to the PE file is an acceptable option

use tools like iidking and add an import section with all the imports that are resolved dynamically

use add cross referances dialog or idc add_dref() to add cross references to them

code for demo

#include <stdio.h>
#include <windows.h>
#pragma comment(lib , "user32.lib")
DWORD (WINAPI * MyGetShortPathName)(LPCTSTR,LPTSTR,DWORD);
int main (void) {
    MessageBox(NULL,"testing add import" , "Test", MB_OK);
    char modname[MAX_PATH] = {0};
    GetModuleFileName(NULL,modname,MAX_PATH);
    printf("%s\n",modname);
    HMODULE hMod = LoadLibrary("kernel32.dll");
    if(hMod) {
        *(FARPROC *)&MyGetShortPathName = GetProcAddress(hMod,"GetShortPathNameA");
        if(MyGetShortPathName) {
            MyGetShortPathName(modname,modname,MAX_PATH);
            printf("%s\n",modname);
        }
    }
    return 0;
}

compiled and executed

C:\codesnips\addimp\addimp.exe
C:\CODESN~1\addimp\addimp.exe

unmodified imports

00412000  GetCurrentThread                      KERNEL32 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00412130  MessageBoxA                           USER32   

exe modified with iidking and an import GetShortPathNameA added in the extra import section

C:\codesnips\addimp>fc /b addimp.exe modaddimp.exe
Comparing files addimp.exe and MODADDIMP.EXE
000000E6: 04 05  no of section 
00000131: 90 A0  
00000160: F4 00
00000161: 47 90
00000164: 3C 50
00000278: 00 2E .
00000279: 00 49 I
0000027A: 00 49 I
0000027B: 00 44 D
0000027C: 00 4B K
0000027D: 00 69 I
0000027E: 00 6E N
0000027F: 00 67 G
00000281: 00 02 vsize
00000285: 00 90 
00000286: 00 01
00000289: 00 02
0000028D: 00 5E
0000028E: 00 01
0000029C: 00 20
0000029F: 00 E0

ida imports window copy paste of modified exe

00412000  GetCurrentThread                      KERNEL32 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00412130  MessageBoxA                           USER32   
00419058  GetShortPathNameA                     kernel32 

double clicked 419058 -> open view -> open subviews -> crooss referances-> add cross refernces

Up P sub_401000+89 call    ds:GetProcAddress  (data xref)

or idc command

add_dref(0x401089,0x419058,53);
AddCodeXref(0x419058,0x401089,53);

AddImports

compiling these three lines into a plugin will add the Desired import into the first module's import

void idaapi run(int) {
nodeidx_t index = import_node.alt1st();
unsigned long value = import_node.altval(index);
netnode(value).supset(0x410004,"GetShortPathNameA\0");
}

the idc file to create Segment

#include <idc.idc>
static main()
{
auto addr;
addr = 0x410000;
SegCreate(addr,addr+0x200,0,1,3,2);
SetSegmentType(addr,SEG_XTRN);
MakeDword(addr+4);
MakeName(addr+4,"GetShortPathNameA");
}

open an exe / run idc / plugin / close and save database / reopen database to see the added imports in imports tab of first module

edit
windows -> reset desktop works the same ways as closing and opening the database so that eleiminates closing and opening of database

7
  • Thanks for the answer! Unfortunately this won't work for some unpacked executables or when dealing with position independent code.
    – alexanderh
    Commented Mar 25, 2015 at 14:19
  • can you elaborate or post / point to a sample where this doesn't seem to work ?
    – blabb
    Commented Mar 25, 2015 at 18:54
  • I might be mistaken but your example would not work on samples that are missing the portable executable header. Examples of these would be shellcode, injected DLLs that zero out the header, or position independent code ( no header and resolves symbols address by traversing PEB for kernel32.dll). Sure, a PE header could be added but this isn't always a feasible approach because it relies on having and modifying the binary.
    – alexanderh
    Commented Mar 25, 2015 at 20:41
  • if you are convinced about the reasoning i am fine with that but my thought process is like this if ida has enough information to retirieve and create the original imports then it should be capable of finding this too. the original query seemed to imply that the pe is available and didn't say anything about non availabilty of the original pe so i posted this approach with a caveat if modifying the exe is acceptable if your new requiremnts are about mucking with an idb standlaone then i dont have any idea
    – blabb
    Commented Mar 26, 2015 at 5:34
  • I understand your thought process but it still doesn't answer how to add the imports to the import tab via IDC or Python in IDA. Which is the original request. Check out NirIzr answer it is the closest.
    – alexanderh
    Commented Mar 26, 2015 at 15:45
5

If you can not add something to the imports viewer you can write your own. Here is the simple example (it is slightly modified example referenced at this hexblog entry and located here with added double-click functionality, added columns, removed exports and fixed bug for a case of unknown origin of the imported function). See the function BuildImports for creating additional imports (manual_func1 and manual_func2)

import idaapi
import idautils
from idaapi import PluginForm
from PySide import QtGui, QtCore

class ImpExpForm_t(PluginForm):

    def imports_names_cb(self, ea, name, ord):
        self.items.append((ea, '' if not name else name, ord))
        # True -> Continue enumeration
        return True

    def BuildImports(self):
        tree = {}
        nimps = idaapi.get_import_module_qty()

        for i in xrange(0, nimps):
            name = idaapi.get_import_module_name(i)
            if not name:
                name = "unknown"
            # Create a list for imported names
            self.items = []

            # Enum imported entries in this module
            idaapi.enum_import_names(i, self.imports_names_cb)

            if name not in tree:
                tree[name] = []
            tree[name].extend(self.items)
        tree["manually_added"] = [(0x01, "manual_func1", 3), (0x02, "manual_func2",4)]

        return tree

    def PopulateTree(self):
        # Clear previous items
        self.tree.clear()

        # Build imports
        root = QtGui.QTreeWidgetItem(self.tree)
        root.setText(0, "Imports")

        for dll_name, imp_entries in self.BuildImports().items():
            imp_dll = QtGui.QTreeWidgetItem(root)
            imp_dll.setText(0, dll_name)

            for imp_ea, imp_name, imp_ord in imp_entries:
                item = QtGui.QTreeWidgetItem(imp_dll)
                item.setText(0, "%s" % imp_name)
                item.setText(1, "0x%08x" % imp_ea)
                item.setText(2, "0x%08x" % imp_ord)

    def dblclick(self, item):
        try:
            idaapi.jumpto(int(item.text(1).encode("ascii", "ignore"), 16))
        except:
            print "Can not jump"


    def OnCreate(self, form):
        """
        Called when the plugin form is created
        """
        # Get parent widget
        self.parent = self.FormToPySideWidget(form)

        # Create tree control
        self.tree = QtGui.QTreeWidget()
        self.tree.setColumnCount(4)
        self.tree.setHeaderLabels(("Names","Address", "Ordinal", "Source"))
        self.tree.itemDoubleClicked.connect(self.dblclick)
        self.tree.setColumnWidth(0, 100)

        # Create layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.tree)

        self.PopulateTree()
        # Populate PluginForm
        self.parent.setLayout(layout)


    def OnClose(self, form):
        """
        Called when the plugin form is closed
        """
        global ImpExpForm
        del ImpExpForm
        print "Closed"


    def Show(self):
        """Creates the form is not created or focuses it if it was"""
        return PluginForm.Show(self,
                               "Imports / Exports viewer",
                               options = PluginForm.FORM_PERSIST)

# --------------------------------------------------------------------------
def main():
    global ImpExpForm

    try:
        ImpExpForm
    except:
        ImpExpForm = ImpExpForm_t()

    ImpExpForm.Show()

# --------------------------------------------------------------------------
main()
3
  • Thanks, I have used the viewer approach before. An issue I have found is that once the IDB is closed the viewer and the data is gone. It would be cool to find something a little more permanent.
    – alexanderh
    Commented Mar 25, 2015 at 20:50
  • 1
    You can use OnClose to save the data for example. It will require a bit more development, but it is still easy
    – w s
    Commented Mar 25, 2015 at 20:53
  • Generally speaking you have some better alternatives. First of all, you can store the script itself in the script window (File-->Script command has an interface for that) - this will allow to keep all your latest additions with the idb. You can also use OnClose and store your data in a file with name derived from the name of the idb which can be obtained by idautils.GetIdbPath. You can also store the data in the custom node of the idb.
    – w s
    Commented Mar 26, 2015 at 7:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.