标题说明了大部分内容。假设我有Windows PE(x86,32位)二进制文件(只是我们要讨论的情况),导入列表通常只会显示在导入目录中找到的导入。它显示的属性是函数的地址,名称和从中导入的库,如以下屏幕快照片段所示:



有没有办法通过脚本编写(IDC或Python,我不在乎)将我自己的导入添加到列表中,例如,让它们指向(地址属性)指向诸如此类的代码(突出显示的行)?:



即在这种情况下,该行看起来像:

0DCBA987     GetLongPathNameW       kernel32.dll


,甚至只是

0DCBA987     GetLongPathNameW       ???


假设上述call GetProcAddress将位于地址0DCBA987

对我来说,优点是可读性。但是由于某些功能由于在各种Windows版本中的可用性而经常动态地导入,因此也会生成更全面的导入列表(并因此得到外部参照)。

鉴于某些二进制文件,这应该是相当琐碎的找出对候选函数的所有外部参照,这些候选函数检索导入的函数的地址(例如GetProcAddress),然后遍历其调用以查找导入的函数。 DLL部分可能更复杂,但是可以保留为空或手动输入。但是我没有找到允许我添加导入的函数。有办法吗?

评论

有趣的问题,当从内存转储分析程序时,这将很有用。能够编辑“导入”的功能将添加交叉引用,并使分析更加容易。

如果您还没有看到,请查看reiat.py。除了添加导入选项卡之外,它完成了您讨论的大部分内容。

@alexanderh:没,虽然我以前在Bitbucket上看到过其他一些存储库。您是否知道如何完成最后一步?

@ 0xC0000022L不,我不知道。这个周末我会努力解决的。我的电子邮件在源代码中。如果可以共享,请随时通过电子邮件发送该示例。愚蠢的旁注,注释“ GetLongPathNameW”可用于通过热键X收集未注释的交叉引用。对自动分析没有用,但仍然有用。

您可以尝试更新import_node(从SDK的nalt.hpp导出),但是目前还不清楚IDA是否在每次重绘导入列表时都重新读取这些netnode。

#1 楼

我不知道是否有人知道这一点,但是IDA使用某种技巧来确定某个段是否为导入段,并对其进行完全不同的处理。

IDA使用一些段属性将细分视为进口细分。例如,命名段.idata或将段的类设置为XTRN会立即使其成为导入段。这样的段将不会显示通常的代码/数据列表,函数定义以及我们在代码视图中习惯的大多数其他内容。在IDA中,它将拒绝显示这些段中的任何程序集。我会说一个很好的Anti-IDA技巧:)

,它们只会显示名称定义,偏移量和注释。一旦分配了偏移量,API的名称就会存储在其中IDA的数据库IDA将获取API的原型和其他信息,分配类型定义和注释。

IIRC这些API也将显示在导入窗口中,但我不确定究竟是什么触发了此。它可能还取决于IDA版本和其他与导入相关的PE属性。

编辑
已经三年多了,显然我当时找不到该脚本,我只是过去了我的一些旧代码,能够重新构造我在该脚本中所做的工作,这就是它的要旨:

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)


评论


@Nirlzr:一定想知道这个答案是否充实,我很好奇。谢谢。

– 0xC0000022L♦
13年5月31日在21:56

@Nirlzr:关于上述脚本的任何新闻吗?

– 0xC0000022L♦
2013年6月7日14:49

@ 0xC0000022L:非常抱歉!我目前正在执行预备役,无法掌握工作中的所有东西。并没有忘记它。

– NirIzr
2013年6月7日15:36

@ 0xC0000022L我知道已经很长时间了(这是一种轻描淡写的说法,但是我已经编辑了答案以包含脚本)

– NirIzr
16-10-21在3:28

#2 楼

如果在PE文件中添加一个额外的导入节是可以接受的选项,请使用iidking之类的工具,并为导入节添加所有动态解析的导入文件

使用add cross参考对话框或idc add_dref()向其添加交叉引用

演示代码

#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;
}


已编译并执行

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


未修改的导入

00412000  GetCurrentThread                      KERNEL32 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00412130  MessageBoxA                           USER32   


用iidking修改的EXE和导入GetShortPathNameA
添加在额外的导入部分中

/>
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导入窗口修改后的exe粘贴粘贴

00412000  GetCurrentThread                      KERNEL32 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00412130  MessageBoxA                           USER32   
00419058  GetShortPathNameA                     kernel32 


双击419058->打开视图->打开子视图-> crooss参考->添加交叉参考

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


或idc命令

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




将这三行代码编译到一个插件中,会将所需的导入添加到第一个模块的导入中

void idaapi run(int) {
nodeidx_t index = import_node.alt1st();
unsigned long value = import_node.altval(index);
netnode(value).supset(0x410004,"GetShortPathNameA
#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");
}
"); }


创建段的IDC文件

q4312078q

打开exe /运行idc /插件/关闭并保存数据库/重新打开数据库以查看添加的导入在第一个模块的导入选项卡中

编辑
windows->重置桌面的工作方式与关闭和打开数据库的方式相同,从而消除了关闭和打开数据库的方式

评论


感谢你的回答!不幸的是,这对于某些解压缩的可执行文件或与位置无关的代码不起作用。

– alexanderh
2015年3月25日14:19在

您可以详细说明或发布/指出一个似乎无效的示例吗?

– blabb
15年3月25日在18:54

我可能会弄错了,但是您的示例无法用于缺少可移植可执行标头的示例。这些示例包括shellcode,将标头清零的注入DLL或与位置无关的代码(无标头,并通过遍历kernel32.dll的PEB解析符号地址)。当然,可以添加PE标头,但这并不总是可行的方法,因为它依赖于拥有和修改二进制文件。

– alexanderh
2015年3月25日在20:41

如果您确信我的推理是正确的,但如果ida有足够的信息来检索和创建原始导入,我的思考过程就是这样,那么它也应该能够找到这个。原始查询似乎暗示该pe是可用的,并且未对原始pe的不可用性说任何话,因此,如果您修改exe是可以接受的(如果您的新要求是与idb standlaone混用的话),那么我以警告的方式发布了此方法我不知道

– blabb
15年3月26日在5:34

我了解您的思考过程,但仍然无法回答如何通过IDC或IDA中的Python将导入添加到导入选项卡。这是原始请求。看看NirIzr的答案是最近的。

– alexanderh
15年3月26日在15:45

#3 楼

如果您无法在导入查看器中添加某些内容,则可以编写自己的内容。
这是简单的示例(此hexblog条目引用了此示例,并对其进行了稍微修改,并在其中添加了双击功能,添加了列并删除了该示例。导出,并修复了导入函数来源未知的情况下的错误)。请参阅BuildImports函数以创建其他导入(manual_func1和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()


评论


谢谢,我以前使用过查看器方法。我发现的一个问题是,一旦IDB关闭,查看器就会消失。找到永久的东西会很酷。

– alexanderh
2015年3月25日在20:50



例如,您可以使用OnClose保存数据。这将需要更多的开发,但是仍然很容易

– w s
2015年3月25日在20:53

一般来说,您有一些更好的选择。首先,您可以将脚本本身存储在脚本窗口中(文件->脚本命令具有该接口)-这将使所有最新添加的内容与idb保持一致。您也可以使用OnClose并将数据存储在文件中,该文件的名称由idautils.GetIdbPath可以获取的idb名称派生。您也可以将数据存储在idb的自定义节点中。

– w s
15年3月26日在7:30