Windows下IDA_Pro与Stelftools工具结合
前些阶段接了个项目,要求分析给定的僵尸网络样本。但是文件放入IDA后发现一个库函数都识别不出来,该文件被UPX打包过,并且l_info字段被更改过,修复完毕脱壳后是一个32位的ELF文件。最开始使用过FLAIR工具和libc.a做了一个简易的sig文件,但是也没有识别出什么函数来,询问了大佬,大佬推荐了Stelftools工具,于是我就试着用了这个工具。
前提环境
我IDA版本为IDA7.5 SP3
python环境为3.7
一、Stelftools下载
去github下载,https://github.com/shuakabane/stelftools
下载后根据教程,安装相关文件
本人python环境用的是.3.7的
1.1配置python的相关库
这里需要更改相关的sh文件,我电脑python版本比较多(本文使用3.7),这里/setup/init.sh更改如下
#!/bin/bash
#CURRENT_PATH=$(pwd)
CURRENT_PATH=$(pwd | sed -e 's/\//\\\//g')
# fix STELFTOOLS_PATH
sed -i "s/STELFTOOLS_PATH=\"\/path\/to\/stelftools\/\"/STELFTOOLS_PATH=\"$CURRENT_PATH\/\"/g" func_ident.py
sed -i "s/STELFTOOLS_PATH=\"\/path\/to\/stelftools\/\"/STELFTOOLS_PATH=\"$CURRENT_PATH\/\"/g" DubMaker.py
sed -i "s/STELFTOOLS_PATH=\"\/path\/to\/stelftools\/\"/STELFTOOLS_PATH=\"$CURRENT_PATH\/\"/g" libfunc_info_create.py
sed -i "s/STELFTOOLS_PATH=\"\/path\/to\/stelftools\/\"/STELFTOOLS_PATH=\"$CURRENT_PATH\/\"/g" ida_stelftools.py
# install the python3 package
python -m pip install yara-python
python -m pip install capstone
python -m pip install pyelftools
python -m pip install python-magic
python -m pip install arpy
# add directories to be used by scripts
#mkdir ./_tmpdir
mkdir ./_tmpdir/man_datasets
mkdir ./_tmpdir/link_order_list
mkdir ./_tmpdir/dummy_bin
在gitbash中运行该文件
这里本人运行时,报出一些连接超时的问题,可以直接在cmd下载相关库与建立文件夹
下载完所有库后,打开根目录下的DubMaker.py、func_ident.py、libfunc_info_create.py文件,将STELFTOOLS_PATH改为自己stelftools目录,因为在gitbash里的路径都为/d/CTFTools/re之类的,其路径遵循的是linux的一套规范
STELFTOOLS_PATH="D:/CTFTools/re/stelftools-main/"
这一部分结束后,就可以在终端直接使用了,可如官方教程,
但是这样做不能在IDA里帮你改sub函数,所以接下来我们在IDA里增加插件
二、IDA增加Stelftools插件
首先gitbash里运行./setup/ida_setup.sh文件
需要注意的是,这里IDA_pro的路径名得注意我这种格式,并且不能有空格。做完会发现自己的ida根目录里plugins会多出一下python文件
注意,这里的ida_shims.py是后期插入的,文章下面会说该问题
接下来打开IDA,会发现还是不能和官方的md文件里一样的可以在load file里看到相关,这是因为你还有一些函数没下载完毕或者出现问题,我这里做的时候忘记截图了,注意观察IDA中最下方的output window,去看自己的python缺少哪些库,
大概有以下库shims,cxxfilt库,这里需要你自己去下载,并且出现ImportError: failed to find libmagic. Check your installation
这里参考大佬https://blog.csdn.net/time_money/article/details/119635746重新下载库
pip uninstall python-magic
pip install python-magic-bin==0.4.14
同时这里出现了一个很关键的问题,我着重讲一下,不知道什么原因,在ida_stelftools.py中,导入from shims import ida_shims会报错,这里我更换了python版本发现也不行,于是根据大佬的博客https://www.cnblogs.com/unr4v31/p/16072562.html,我们直接在ida的plugins文件中,自己建立一个ida_shims文件,并更改ida_stelftools.py的引入,如图
同时,在./plugins建立ida_shims.py文件,其内容为
import idc
import idaapi
try:
import ida_bytes
except ImportError:
ida_bytes = None
try:
import ida_name
except ImportError:
ida_name = None
try:
import ida_kernwin
except ImportError:
ida_kernwin = None
try:
import ida_nalt
except ImportError:
ida_nalt = None
try:
import ida_ua
except ImportError:
ida_ua = None
try:
import ida_funcs
except ImportError:
ida_funcs = None
def _get_fn_by_version(lib, curr_fn, archive_fn, archive_lib=None):
if idaapi.IDA_SDK_VERSION >= 700:
try:
return getattr(lib, curr_fn)
except AttributeError:
raise Exception('%s is not a valid function in %s' % (curr_fn,
lib))
use_lib = lib if archive_lib is None else archive_lib
try:
return getattr(use_lib, archive_fn)
except AttributeError:
raise Exception('%s is not a valid function in %s' % (archive_fn,
use_lib))
def print_insn_mnem(ea):
fn = _get_fn_by_version(idc, 'print_insn_mnem', 'GetMnem')
return fn(ea)
def print_operand(ea, n):
fn = _get_fn_by_version(idc, 'print_operand', 'GetOpnd')
return fn(ea, n)
def define_local_var(start, end, location, name):
fn = _get_fn_by_version(idc, 'define_local_var', 'MakeLocal')
return fn(start, end, location, name)
def find_func_end(ea):
fn = _get_fn_by_version(idc, 'find_func_end', 'FindFuncEnd')
return fn(ea)
def is_code(flag):
fn = _get_fn_by_version(ida_bytes, 'is_code', 'isCode', idaapi)
return fn(flag)
def get_full_flags(ea):
fn = _get_fn_by_version(ida_bytes, 'get_full_flags', 'getFlags', idaapi)
return fn(ea)
def get_name(ea):
fn = _get_fn_by_version(idc, 'get_name', 'Name')
if idaapi.IDA_SDK_VERSION > 700:
return fn(ea, ida_name.GN_VISIBLE)
return fn(ea)
def get_func_off_str(ea):
fn = _get_fn_by_version(idc, 'get_func_off_str', 'GetFuncOffset')
return fn(ea)
def jumpto(ea, opnum=-1, uijmp_flags=0x0001):
fn = _get_fn_by_version(ida_kernwin, 'jumpto', 'Jump', idc)
if idaapi.IDA_SDK_VERSION >= 700:
return fn(ea, opnum, uijmp_flags)
return fn(ea)
def ask_yn(default, format_str):
fn = _get_fn_by_version(ida_kernwin, 'ask_yn', 'AskYN', idc)
return fn(default, format_str)
def ask_file(for_saving, default, dialog):
fn = _get_fn_by_version(ida_kernwin, 'ask_file', 'AskFile', idc)
return fn(for_saving, default, dialog)
def get_func_attr(ea, attr):
fn = _get_fn_by_version(idc, 'get_func_attr', 'GetFunctionAttr')
return fn(ea, attr)
def get_name_ea_simple(name):
fn = _get_fn_by_version(idc, 'get_name_ea_simple', 'LocByName')
return fn(name)
def next_head(ea, maxea=4294967295):
fn = _get_fn_by_version(idc, 'next_head', 'NextHead')
return fn(ea, maxea)
def get_screen_ea():
fn = _get_fn_by_version(idc, 'get_screen_ea', 'ScreenEA')
return fn()
def choose_func(title):
fn = _get_fn_by_version(idc, 'choose_func', 'ChooseFunction')
return fn(title)
def ask_ident(default, prompt):
fn = _get_fn_by_version(ida_kernwin, 'ask_str', 'AskIdent', idc)
if idaapi.IDA_SDK_VERSION >= 700:
return fn(default, ida_kernwin.HIST_IDENT, prompt)
return fn(default, prompt)
def set_name(ea, name):
fn = _get_fn_by_version(idc, 'set_name', 'MakeName')
if idaapi.IDA_SDK_VERSION >= 700:
return fn(ea, name, ida_name.SN_CHECK)
return fn(ea, name)
def get_wide_dword(ea):
fn = _get_fn_by_version(idc, 'get_wide_dword', 'Dword')
return fn(ea)
def get_strlit_contents(ea):
fn = _get_fn_by_version(idc, 'get_strlit_contents', 'GetString')
return fn(ea)
def get_func_name(ea):
fn = _get_fn_by_version(idc, 'get_func_name', 'GetFunctionName')
return fn(ea)
def get_first_seg():
fn = _get_fn_by_version(idc, 'get_first_seg', 'FirstSeg')
return fn()
def get_segm_attr(segea, attr):
fn = _get_fn_by_version(idc, 'get_segm_attr', 'GetSegmentAttr')
return fn(segea, attr)
def get_next_seg(ea):
fn = _get_fn_by_version(idc, 'get_next_seg', 'NextSeg')
return fn(ea)
def is_strlit(flags):
fn = _get_fn_by_version(ida_bytes, 'is_strlit', 'isASCII', idc)
return fn(flags)
def create_strlit(start, lenth):
fn = _get_fn_by_version(ida_bytes, 'create_strlit', 'MakeStr', idc)
if idaapi.IDA_SDK_VERSION >= 700:
return fn(start, lenth, ida_nalt.STRTYPE_C)
return fn(start, idc.BADADDR)
def is_unknown(flags):
fn = _get_fn_by_version(ida_bytes, 'is_unknown', 'isUnknown', idc)
return fn(flags)
def is_byte(flags):
fn = _get_fn_by_version(ida_bytes, 'is_byte', 'isByte', idc)
return fn(flags)
def create_dword(ea):
fn = _get_fn_by_version(ida_bytes, 'create_data', 'MakeDword', idc)
if idaapi.IDA_SDK_VERSION >= 700:
return fn(ea, ida_bytes.FF_DWORD, 4, idaapi.BADADDR)
return fn(ea)
def op_plain_offset(ea, n, base):
fn = _get_fn_by_version(idc, 'op_plain_offset', 'OpOff')
return fn(ea, n, base)
def next_addr(ea):
fn = _get_fn_by_version(ida_bytes, 'next_addr', 'NextAddr', idc)
return fn(ea)
def can_decode(ea):
fn = _get_fn_by_version(ida_ua, 'can_decode', 'decode_insn', idaapi)
return fn(ea)
def get_operands(insn):
if idaapi.IDA_SDK_VERSION >= 700:
return insn.ops
return idaapi.cmd.Operands
def get_canon_feature(insn):
if idaapi.IDA_SDK_VERSION >= 700:
return insn.get_canon_feature()
return idaapi.cmd.get_canon_feature()
def get_segm_name(ea):
fn = _get_fn_by_version(idc, 'get_segm_name', 'SegName')
return fn(ea)
def add_func(ea):
fn = _get_fn_by_version(ida_funcs, 'add_func', 'MakeFunction', idc)
return fn(ea)
def create_insn(ea):
fn = _get_fn_by_version(idc, 'create_insn', 'MakeCode')
return fn(ea)
def get_segm_end(ea):
fn = _get_fn_by_version(idc, 'get_segm_end', 'SegEnd')
return fn(ea)
def get_segm_start(ea):
fn = _get_fn_by_version(idc, 'get_segm_start', 'SegStart')
return fn(ea)
def decode_insn(ea):
fn = _get_fn_by_version(ida_ua, 'decode_insn', 'decode_insn', idaapi)
if idaapi.IDA_SDK_VERSION >= 700:
insn = ida_ua.insn_t()
fn(insn, ea)
return insn
fn(ea)
return idaapi.cmd
def get_bookmark(index):
fn = _get_fn_by_version(idc, 'get_bookmark', 'GetMarkedPos')
return fn(index)
def get_bookmark_desc(index):
fn = _get_fn_by_version(idc, 'get_bookmark_desc', 'GetMarkComment')
return fn(index)
def set_color(ea, what, color):
fn = _get_fn_by_version(idc, 'set_color', 'SetColor')
return fn(ea, what, color)
def msg(message):
fn = _get_fn_by_version(ida_kernwin, 'msg', 'Message', idc)
return fn(message)
def get_highlighted_identifier():
fn = _get_fn_by_version(ida_kernwin, 'get_highlight',
'get_highlighted_identifier', idaapi)
if idaapi.IDA_SDK_VERSION >= 700:
viewer = ida_kernwin.get_current_viewer()
highlight = fn(viewer)
if highlight and highlight[1]:
return highlight[0]
return fn()
def start_ea(obj):
if not obj:
return None
try:
return obj.startEA
except AttributeError:
return obj.start_ea
def end_ea(obj):
if not obj:
return None
try:
return obj.endEA
except AttributeError:
return obj.end_ea
def set_func_flags(ea, flags):
fn = _get_fn_by_version(idc, 'set_func_attr', 'SetFunctionFlags')
if idaapi.IDA_SDK_VERSION >= 700:
return fn(ea, idc.FUNCATTR_FLAGS, flags)
return fn(ea, flags)
def get_func_flags(ea):
fn = _get_fn_by_version(idc, 'get_func_attr', 'GetFunctionFlags')
if idaapi.IDA_SDK_VERSION >= 700:
return fn(ea, idc.FUNCATTR_FLAGS)
return fn(ea)
更改完毕后,全部保存,重新打开自己的IDA,即可看到如下插件
接下来我们就可以选择相关的规则文件,进行函数鉴别了。
三、鉴别过程
1.鉴别前
2.使用工具,调用鉴别规则,我这边是32位elf文件,所以选择i686架构的,具体选择可以看官方文档,根据自己的文件进行选择
3.确认完毕后即可看到左上角,functions windows里的函数识别出来一部分了
四、总结
该工具比FLAIR对于函数的识别正确率要更高,且对该样本的确有效,一路上真的踩了好多坑!!!终于可以看该病毒的样本啦啦啦啦啦!
本小菜鸡第一篇博客!希望自己在新的一年里变得更强哈哈哈哈!!