有没有办法通过脚本编写(IDC或Python,我不在乎)将我自己的导入添加到列表中,例如,让它们指向(地址属性)指向诸如此类的代码(突出显示的行)?:
即在这种情况下,该行看起来像:
0DCBA987 GetLongPathNameW kernel32.dll
,甚至只是
0DCBA987 GetLongPathNameW ???
假设上述
call GetProcAddress
将位于地址0DCBA987
。对我来说,优点是可读性。但是由于某些功能由于在各种Windows版本中的可用性而经常动态地导入,因此也会生成更全面的导入列表(并因此得到外部参照)。
鉴于某些二进制文件,这应该是相当琐碎的找出对候选函数的所有外部参照,这些候选函数检索导入的函数的地址(例如
GetProcAddress
),然后遍历其调用以查找导入的函数。 DLL部分可能更复杂,但是可以保留为空或手动输入。但是我没有找到允许我添加导入的函数。有办法吗?#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
评论
有趣的问题,当从内存转储分析程序时,这将很有用。能够编辑“导入”的功能将添加交叉引用,并使分析更加容易。如果您还没有看到,请查看reiat.py。除了添加导入选项卡之外,它完成了您讨论的大部分内容。
@alexanderh:没,虽然我以前在Bitbucket上看到过其他一些存储库。您是否知道如何完成最后一步?
@ 0xC0000022L不,我不知道。这个周末我会努力解决的。我的电子邮件在源代码中。如果可以共享,请随时通过电子邮件发送该示例。愚蠢的旁注,注释“ GetLongPathNameW”可用于通过热键X收集未注释的交叉引用。对自动分析没有用,但仍然有用。
您可以尝试更新import_node(从SDK的nalt.hpp导出),但是目前还不清楚IDA是否在每次重绘导入列表时都重新读取这些netnode。