Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
bip — 역공학을 위해 IDA와의 상호작용을 단순화하는 객체 지향 Python API로, 플러그인 개발과 디스어셈블리 분석 자동화를 가능하게 합니다. | Kitploit
도구/GitHubGitHub/synacktiv/bip
Static AnalysisCode AnalysisReverse EngineeringScripting & AutomationBinary Analysis
GitHubsynacktiv/bip

bip

역공학을 위해 IDA와의 상호작용을 단순화하는 객체 지향 Python API로, 플러그인 개발과 디스어셈블리 분석 자동화를 가능하게 합니다.

저장소 보기
205194년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

Bip

Bip은 IDA와 상호작용하기 위해 Python 사용을 단순화하는 것을 목표로 하는 프로젝트입니다. 주요 목표는 IDA의 대화형 콘솔에서 Python 사용을 용이하게 하고 플러그인 작성을 돕는 것입니다. 더 일반적으로는 Python API를 통해 수행되는 반복적인 작업을 자동화하는 것이 목표입니다. Bip은 또한 더 객체 지향적이고 "python-like"한 API와 실제 문서를 제공하기 위해 개발되었습니다.

이 코드는 완전하지 않으며, 많은 기능이 아직 누락되어 있습니다. 개발은 사람들이 요청하는 것과 개발자들이 사용하는 것에 우선순위를 두므로, PR, Feature Request 및 Issues(문서 관련 포함)를 주저하지 말고 만들어 주세요.

문서는 RST 형식으로 제공되며(docs/ 디렉토리에서 sphinx를 사용하여 컴파일할 수 있습니다), 온라인 <https://synacktiv.github.io/bip/build/html/index.html>_으로도 제공됩니다.

  • 현재 IDA 버전: IDA 7.5SP1 및 Python 2.7 또는 3.8
  • 마지막 Bip 버전: 1.0

설치

이 설치는 Windows와 Linux에서만 테스트되었습니다: python install.py.

특정 폴더에 설치하려면 선택적 --dest 인수를 사용할 수 있습니다:

.. code-block:: none

root@kitploit:~
usage: install.py [-h] [--dest DEST]

optional arguments:
  -h, --help   show this help message and exit
  --dest DEST  Destination folder where to install Bip

이 설치 프로그램은 기본적으로 플러그인을 설치하지 않으며, 단순히 Bip의 핵심만 설치합니다. 기본 대상 폴더는 IDA가 로컬에서 사용하는 폴더입니다(Windows의 경우 %APPDATA%\Hex-Rays\IDA Pro\, Linux 및 MacOSX의 경우 $HOME/.idapro).

개요

이 개요는 가장 일반적인 작업이 어떻게 수행될 수 있는지 보여주기 위한 것으로, 완전하지 않습니다. Bip의 모든 함수와 객체는 doc string을 사용하여 문서화되므로 셸에서 도움말을 얻으려면 help(BipClass) 및 help(obj.bipmethod)를 사용하세요.

기본

bip.base 모듈은 IDA와 인터페이스하기 위한 기본 기능의 대부분을 포함합니다. 실제로 이것은 주로 IDA의 디스어셈블러 부분으로, 다음을 포함합니다: 명령어, 함수, 기본 블록, 피연산자, 데이터, xrefs, 구조체, 타입 조작, ...

명령어 / 피연산자~~~~~~~~~~~~~~~~~~~~~~~

The classes bip.base.BipInstr and bip.base.BipOperand:

.. code-block:: pycon

root@kitploit:~
>>> from bip.base import *
>>> i = BipInstr() # BipInstr is the base class for representing an instruction
>>> i # by default the address on the screen is taken
BipInstr: 0x1800D324B (mov     rcx, r13)
>>> i2 = BipInstr(0x01800D3242) # pass the address in argument
>>> i2
BipInstr: 0x1800D3242 (mov     r8d, 8)
>>> i2.next # access next instruction, previous with i2.prev
BipInstr: 0x1800D3248 (mov     rdx, r14)
>>> l = [i3 for i3 in BipInstr.iter_all()] # l contains the list of all BipInstruction of the database, iter_all produces a generator object
>>> i.ea # access the address
6443315787
>>> i.mnem # mnemonic representation
mov
>>> i.ops # access to the operands
[<bip.base.operand.BipOperand object at 0x0000022B0291DA90>, <bip.base.operand.BipOperand object at 0x0000022B0291DA58>]
>>> i.ops[0].str # string representation of an operand
rcx
>>> i.bytes # bytes in the instruction
[73L, 139L, 205L]
>>> i.size # number of bytes of this instruction
3
>>> i.comment = "hello" # set a comment, rcomment for the repeatable comments
>>> i
BipInstr: 0x1800D324B (mov     rcx, r13; hello)
>>> i.comment # get a comment
hello
>>> i.func # access to the function
Func: RtlQueryProcessLockInformation (0x1800D2FF0)
>>> i.block # access to basic block
BipBlock: 0x1800D3242 (from Func: RtlQueryProcessLockInformation (0x1800D2FF0))

Function / Basic block

root@kitploit:~

The classes ``bip.base.BipFunction`` and ``bip.base.BipBlock``:

.. code-block:: pycon

    >>> from bip.base import *
    >>> f = BipFunction() # Get the function, screen address used if not provided
    >>> f
    Func: RtlQueryProcessLockInformation (0x1800D2FF0)
    >>> f2 = BipFunction(0x0018010E975) # provide an address, not necessary the first one
    >>> f2
    Func: sub_18010E968 (0x18010E968)
    >>> f == f2 # compare two functions
    False
    >>> f == BipFunction(0x001800D3021)
    True
    >>> hex(f.ea) # start address
    0x1800d2ff0L
    >>> hex(f.end) # end address
    0x1800d3284L
    >>> f = BipFunction.get_by_name("RtlQueryProcessLockInformation") # fetch the function from its name
    >>> f.name # get and set the name
    RtlQueryProcessLockInformation
    >>> f.name = "test"
    >>> f.name
    test
    >>> f.size # number of bytes in the function
    660
    >>> f.bytes # bytes of the function
    [72L, ..., 255L]
    >>> f.callees # list of functions called by this function
    [<bip.base.func.BipFunction object at 0x0000022B0291DD30>, ..., <bip.base.func.BipFunction object at 0x0000022B045487F0>]
    >>> f.callers # list of functions which call this function
    [<bip.base.func.BipFunction object at 0x0000022B04544048>]
    >>> f.instr # list of instructions in the function
    [<bip.base.instr.BipInstr object at 0x0000022B0291DB00>, ..., <bip.base.instr.BipInstr object at 0x0000022B0454D080>]
    >>> f.comment = "welcome to bip" # comment of the function, rcomment for repeatable ones
    >>> f.comment
    welcome to bip
    >>> f.does_return # does this function return ?
    True
    >>> BipFunction.iter_all() # allows to iter on all functions defined in the database
    <generator object iter_all at 0x0000022B029231F8>
    >>> f.nb_blocks # number of basic blocks
    33
    >>> f.blocks # list of blocks
    [<bip.base.block.BipBlock object at 0x0000022B04544D68>, ..., <bip.base.block.BipBlock object at 0x0000022B04552240>]
    >>> f.blocks[5] # access the basic block 5, could be done with BipBlock(addr)
    BipBlock: 0x1800D306E (from Func: test (0x1800D2FF0))
    >>> f.blocks[5].func # link back to the function
    Func: test (0x1800D2FF0)
    >>> f.blocks[5].instr # list of instructions in the block
    [<bip.base.instr.BipInstr object at 0x0000022B04544710>, ..., <bip.base.instr.BipInstr object at 0x0000022B0291DB00>]
    >>> f.blocks[5].pred # predecessor blocks, blocks where control flow lead to this one
    [<bip.base.block.BipBlock object at 0x0000022B04544D68>]
    >>> f.blocks[5].succ # successor blocks
    [<bip.base.block.BipBlock object at 0x0000022B04544710>, <bip.base.block.BipBlock object at 0x0000022B04544438>]
    >>> f.blocks[5].is_ret # is this block containing a return
    False

Data
~~~~

The class ``bip.base.BipData``:

.. code-block:: pycon

    >>> from bip.base import *
    >>> d = BipData(0x000180110068) # .rdata:0000000180110068 bip_ex          dq offset unk_180110DE0
    >>> d
    BipData at 0x180110068 = 0x180110DE0 (size=8)
    >>> d.name # Name of the symbol if any
    bip_ex
    >>> d.is_word # is it a word
    False
    >>> d.is_qword # is it a qword
    True
    >>> hex(d.value) # value at that address, this take into account the basic type (byte, word, dword, qword) defined in IDA
    0x180110de0L
    >>> hex(d.ea) # address
    0x180110068L
    >>> d.comment = "example" # comment as before
    >>> d.comment
    example
    >>> d.value = 0xAABBCCDD # change the value
    >>> hex(d.value)
    0xaabbccddL
    >>> d.bytes # get the bytes, as before
    [221L, 204L, 187L, 170L, 0L, 0L, 0L, 0L]
    >>> hex(d.original_value) # get the original value before modification
    0x180110de0L
    >>> d.bytes = [0x11, 0x22, 0x33, 0x44, 0, 0, 0, 0] # patch the bytes
    >>> hex(d.value) # get the value
    0x44332211L
    >>> BipData.iter_heads() # iter on "heads" of the IDB, heads are defined data in the IDB
    <generator object iter_heads at 0x0000022B02923240>
    >>> hex(BipData.get_dword(0x0180110078)) # staticmethod for reading value at an address
    0x60004L
    >>> BipData.set_byte(0x0180110078, 0xAA) # static method for modifying a value at an address
    >>> hex(BipData.get_qword(0x0180110078))
    0x600aaL

Element
~~~~~~~

In Bip most basic objects inherit from the same classes: ``BipBaseElt`` which is
the most basic one, ``BipRefElt`` which includes all the objects which can have
xrefs (including structures (``BipStruct``) and structure members
(``BStructMember``), see below), ``BipElt``
which represents all elements which have an address in the IDA DataBase (idb),
including ``BipData`` and ``BipInstr`` (it is this class which
implements the properties: ``comment``,  ``name``, ``bytes``, ...).

It is possible to use the functions ``GetElt`` and ``GetEltByName``
to get the right basic element from an address or a name
representing a location in the binary.

.. code-block:: pycon

    >>> from bip.base import *
    >>> GetElt() # get the element at current address, in this case return a BipData object
    BipData at 0x180110068 = 0xAABBCCDD (size=8)
    >>> GetElt(0x00180110078) # get the element at the address 0x00180110078
    BipData at 0x180110078 = 0xAA (size=1)
    >>> GetElt(0x1800D2FF0) # in this case it returns an BipInstr object because this is code
    BipInstr: 0x1800D2FF0 (mov     rax, rsp)
    >>> GetEltByName("bip_ex") # Get using a name and not an address
    BipData at 0x180110068 = 0xAABBCCDD (size=8)
    >>> isinstance(GetElt(0x1800D2FF0), BipInstr) # test if that element is an instruction ?
    True
    >>> GetElt(0x1800D2FF0).is_code # are we on code ? same for is_data; do not work for struct
    True
    >>> isinstance(GetElt(0x1800D2FF0), BipData) # or data ?
    False

Some static functions are provided to search elements in the database:

.. code-block:: pycon

    >>> from bip.base import *
    >>> GetElt()
    BipInstr: 0x1800D3248 (mov     rdx, r14)
    >>> BipElt.next_code() # find next code elt from current addres or addr passed as arg
    BipInstr: 0x1800D324B (mov     rcx, r13)
    >>> BipElt.next_code(down=False) # find prev code element
    BipInstr: 0x1800D3242 (mov     r8d, 8)
    >>> BipElt.next_data() # find next data elt from current address or addr passed as arg
    BipData at 0x1800D3284 = 0xCC (size=1)
    >>> BipElt.next_data(down=False) # find previous data element
    BipData at 0x1800D2FE1 = 0xCC (size=1)
    >>> hex(BipElt.next_data_addr(down=False)) # find address of the previous data element
    0x1800d2fe1L
    >>> BipElt.next_unknown() # same for unknown, which are not typed element of IDA and are considered data by Bip
    BipData at 0x180110000 = 0xE (size=1)
    >>> BipElt.next_defined() # opposite of unknown: data or code
    BipInstr: 0x1800D324B (mov     rcx, r13)
    >>> BipElt.search_bytes("49 ? CD", 0x1800D3248) # search for byte sequence (ignore the current position by default)
    BipInstr: 0x1800D324B (mov     rcx, r13)

Xref
~~~~

All elements which inherit from ``BipRefElt`` (``BipInstr``,
``BipData``, ``BipStruct``, ...) and some other (in
particular ``BipFunction``) contain methods which allow
to access xrefs. They are represented by the ``BipXref`` objects which
have a ``src`` (origin of the xref) and a ``dst`` (destination of the xref).

.. code-block:: pycon

    >>> from bip.base import *
    >>> i = BipInstr(0x01800D3063)
    >>> i # example with instruction but works the same with BipData
    BipInstr: 0x1800D3063 (cmp     r15, [rsp+98h+var_58])
    >>> i.xTo # List of xref which point on this instruction
    [<bip.base.xref.BipXref object at 0x0000022B04544438>, <bip.base.xref.BipXref object at 0x0000022B045447F0>]
    >>> i.xTo[0].src # previous instruction
    BipInstr: 0x1800D305E (mov     [rsp+98h+var_78], rsi)
    >>> i.xTo[0].is_ordinaryflow # is this an ordinary flow between to instruction (not jmp or call)
    True
    >>> i.xTo[1].src # jmp to instruction i at 0x1800D3063
    BipInstr: 0x1800D3222 (jmp     loc_1800D3063)
    >>> i.xTo[1].is_jmp # is this xref because of a jmp ?
    True
    >>> i.xEaTo # bypass the xref objects and get the address directly
    [6443315294L, 6443315746L]
    >>> i.xEltTo # bypass the xref objects and get the elements directly, will list BipData if any
    [<bip.base.instr.BipInstr object at 0x0000022B045447F0>, <bip.base.instr.BipInstr object at 0x0000022B04544978>]
    >>> i.xCodeTo # bypass the xref objects and get the instr directly, if a BipData was pointed at this address it will not be listed
    [<bip.base.instr.BipInstr object at 0x0000022B04544438>, <bip.base.instr.BipInstr object at 0x0000022B0291DD30>]
    >>> i.xFrom # same but for coming from this instruction
    [<bip.base.xref.BipXref object at 0x0000022B04544D68>]
    >>> i.xFrom[0]
    <bip.base.xref.BipXref object at 0x0000022B04544438>
    >>> i.xFrom[0].dst # next instruction
    BipInstr: 0x1800D3068 (jz      loc_1800D3227)
    >>> i.xFrom[0].src # current instruction
    BipInstr: 0x1800D3063 (cmp     r15, [rsp+98h+var_58])
    >>> hex(i.xFrom[0].dst_ea) # address of the next instruction
    0x1800D3068L
    >>> i.xFrom[0].is_codepath # this is a normal code path (include jmp and call)
    True
    >>> i.xFrom[0].is_call # is this because of a call ?
    False
    >>> f = BipFunction()
    >>> f
    Func: RtlQueryProcessLockInformation (0x1800D2FF0)
    >>> f.xTo # works also for functions, but only with To, not with the From
    [<bip.base.xref.BipXref object at 0x000001D95529EB00>, <bip.base.xref.BipXref object at 0x000001D95529EB70>, <bip.base.xref.BipXref object at 0x000001D95529EBE0>, <bip.base.xref.BipXref object at 0x000001D95529EC88>]
    >>> f.xEltTo # here we have 3 data references to this function
    [<bip.base.instr.BipInstr object at 0x000001D95529EE48>, <bip.base.data.BipData object at 0x000001D95529EEF0>, <bip.base.data.BipData object at 0x000001D95529EF28>, <bip.base.data.BipData object at 0x000001D95529EF60>]
    >>> f.xCodeTo # but only one instruction
    [<bip.base.instr.BipInstr object at 0x000001D95529EC88>]

Struct
~~~~~~

Manipulating struct (``BipStruct``) and members (``BStructMember``):

.. code-block:: pycon

    >>> from bip.base import *
    >>> st = BipStruct.get("EXCEPTION_RECORD") # Structs are accessed by using get and their name
    >>> st # BipStruct object
    Struct: EXCEPTION_RECORD (size=0x98)
    >>> st.comment = "struct comment"
    >>> st.comment
    struct comment
    >>> st.name
    EXCEPTION_RECORD
    >>> st.size
    152
    >>> st["ExceptionFlags"] # access to the BStructMember by their name
    Member: EXCEPTION_RECORD.ExceptionFlags (offset=0x4, size=0x4)
    >>> st[8] # or by their offset, this is *not* the entry number 8!!!
    Member: EXCEPTION_RECORD.ExceptionRecord (offset=0x8, size=0x8)
    >>> st[2] # offset does not need to be the first one
    Member: EXCEPTION_RECORD.ExceptionCode (offset=0x0, size=0x4)
    >>> st.members # list of members
    [<bip.base.struct.BStructMember object at 0x000001D95529EEF0>, ..., <bip.base.struct.BStructMember object at 0x000001D95536DF28>]
    >>> st[0].name
    ExceptionCode
    >>> st[0].fullname
    EXCEPTION_RECORD.ExceptionCode
    >>> st[0].size
    4
    >>> st[0].struct
    Struct: EXCEPTION_RECORD (size=0x98)
    >>> st[0].comment = "member comment"
    >>> st[0].comment
    member comment
    >>> st[8].xEltTo # BStructMember et BipStruct have xrefs
    [<bip.base.instr.BipInstr object at 0x000001D95536DD30>, <bip.base.instr.BipInstr object at 0x000001D95536D9E8>]
    >>> st[8].xEltTo[0]
    BipInstr: 0x1800A0720 (mov     [rsp+538h+ExceptionRecord.ExceptionRecord], r10)

Creating struct, adding members and nested structure:

.. code-block:: pycon

    >>> from bip.base import *
    >>> st = BipStruct.create("NewStruct") # create a new structure
    >>> st
    Struct: NewStruct (size=0x0)
    >>> st.add("NewField", 4) # add a new member named "NewField" of size 4
    Member: NewStruct.NewField (offset=0x0, size=0x4)
    >>> st.add("NewQword", 8)
    Member: NewStruct.NewQword (offset=0x4, size=0x8)
    >>> st
    Struct: NewStruct (size=0xC)
    >>> st.add("struct_nested", 1)
    Member: NewStruct.struct_nested (offset=0xC, size=0x1)
    >>> st["struct_nested"].type = BipType.from_c("EXCEPTION_RECORD") # changing the type of member struct_nested to struct EXCEPTION_RECORD
    >>> st["struct_nested"]
    Member: NewStruct.struct_nested (offset=0xC, size=0x98)
    >>> st["struct_nested"].is_nested # is this a nested structure ?
    True
    >>> st["struct_nested"].nested_struct # getting the nested structure
    Struct: EXCEPTION_RECORD (size=0x98)

Types
~~~~~

IDA uses extensively types in hexrays but also in the base API for defining
types of data, variables and so on. In Bip the different types inherit from
the same class ``BipType``. This class offers some basic methods common to all
types and subclasses (class starting by ``BType``) can define more specific
ones.

The types should be seen as a recursive structure: a ``void *`` is a
``BTypePtr`` containing a ``BTypeVoid`` structure. For a list of the
different types implemented in Bip see the `documentation <https://synacktiv.github.io/bip/build/html/base/type.html>`_.

.. code-block:: pycon

    >>> from bip.base import *
    >>> pv = BipType.from_c("void *") # from_c is the easiest way to create a type
    >>> pv
    <bip.base.biptype.BTypePtr object at 0x000001D95536DDD8>
    >>> pv.size # ptr on x64 is 8 bytes
    8
    >>> pv.str # C string representation
    void *
    >>> pv.is_named # this type is not named
    False
    >>> pv.pointed # type below the pointer (recursive)
    <bip.base.biptype.BTypeVoid object at 0x000001D95536DF60>
    >>> pv.children # list of type pointed
    [<bip.base.biptype.BTypeVoid object at 0x000001D95536DEB8>]
    >>> d = BipData(0x000180110068)
    >>> d.type # access directly to the type at the address
    <bip.base.biptype.BTypePtr object at 0x000001D95536D9E8>
    >>> d.type.str
    void *
    >>> ps = BipType.from_c("EXCEPTION_RECORD *")
    >>> ps.pointed # type for struct EXCEPTION_RECORD
    <bip.base.biptype.BTypeStruct object at 0x000001D95536DD30>
    >>> ps.pointed.is_named # this one is named
    True
    >>> ps.pointed.name
    EXCEPTION_RECORD
    >>> ps.set_at(d.ea) # set the type ps at address d.ea
    >>> d.type.str # the type has indeed changed
    EXCEPTION_RECORD *
    >>> d.type = pv # rolling it back
    >>> d.type.str
    void *
    >>> BipType.get_at(d.ea) # Possible to directly get the type with get_at(address)
    <bip.base.biptype.BTypePtr object at 0x000001D95536DEB8>

Hexrays
-------

The module ``bip.hexrays`` contains the features linked to the decompiler
provided by IDA.

Functions / local variables

Hexrays 함수는 HxCFunc 객체로 표현되고, 지역 변수는 HxLvar 객체로 표현됩니다:

.. code-block:: pycon

root@kitploit:~
>>> HxCFunc.from_addr() # HxCFunc represents a decompiled function
<bip.hexrays.hx_cfunc.HxCFunc object at 0x00000278AE80C860>
>>> hf = BipFunction().hxfunc # accessible from a "normal function"
>>> hex(hf.ea) # address of the function
0x1800d2ff0L
>>> hf.args # list of the arguments as HxLvar objects
[<bip.hexrays.hx_lvar.HxLvar object at 0x00000278AFDAACF8>]
>>> hf.lvars # list of all local variables (including args)
[<bip.hexrays.hx_lvar.HxLvar object at 0x00000278AFDAAB70>, ..., <bip.hexrays.hx_lvar.HxLvar object at 0x00000278AFDAF4E0>]
>>> lv = hf.lvars[0] # getting the first one
>>> lv
LVAR(name=a1, size=8, type=<bip.base.biptype.BTypeInt object at 0x00000278AFDAAFD0>)
>>> lv.name # getting name of lvar
a1
>>> lv.is_arg # is this variable an argument ?
True
>>> lv.name = "thisisthefirstarg" # changing name of the lvar
>>> lv
>>> lv.type = BipType.from_c("void *") # changing the type
>>> lv.comment = "new comment" # adding a comment
>>> lv.size # getting the size
8

CNode / 방문자~~~~~~~~~~~~~~~~

Hexrays allows to manipulate the AST it produces, this is a particularly useful feature as it allows to make static analysis at a way higher level. Bip defines CNode which represents a node of the AST, each type of node is represented by a subclass of CNode. All types of node have child nodes except CNodeExprFinal which are the leaf of the AST. Two main types of nodes exist CNodeExpr (expressions) and CNodeStmt (statements). Statements correspond to the C Statements: if, while, ... , expressions are everything else. Statements can have children statements or expressions while expressions can only have expression children.

A list of all the different types of nodes and more details on what they do and how to write a visitor is available in the documentation <https://synacktiv.github.io/bip/build/html/hexrays/astnodes.html#ast-node-types>_.

Directly accessing the nodes:

.. code-block:: pycon

root@kitploit:~
>>> hf = HxCFunc.from_addr() # get the HxCFunc
>>> rn = hf.root_node # accessing the root node of the function
>>> rn # root node is always a CNodeStmtBlock
CNodeStmtBlock(ea=0x1800D3006, stmt_children=[<bip.hexrays.cnode.CNodeStmtExpr object at 0x00000278AFDAADD8>, ..., <bip.hexrays.cnode.CNodeStmtReturn object at 0x00000278B16355F8>])
>>> hex(rn.ea) # address of the root node, after the function prolog
0x1800d3006L
>>> rn.has_parent # root node does not have parent
False
>>> rn.expr_children # this node does not have expression statements
[]
>>> ste = rn.stmt_children[0] # getting the first statement children
>>> ste # CNodeStmtExpr contain one child expression
CNodeStmtExpr(ea=0x1800D3006, value=CNodeExprAsg(ea=0x1800D3006, ops=[<bip.hexrays.cnode.CNodeExprVar object at 0x00000278AFDAADD8>, <bip.hexrays.cnode.CNodeExprVar object at 0x00000278B1637080>]))
>>> ste.parent # the parent is the root node
CNodeStmtBlock(ea=0x1800D3006, stmt_children=[<bip.hexrays.cnode.CNodeStmtExpr object at 0x00000278B1637048>, ..., <bip.hexrays.cnode.CNodeStmtReturn object at 0x00000278B16376D8>])
>>> a = ste.value # getting the expression of the node
>>> a # Asg is an assignement
CNodeExprAsg(ea=0x1800D3006, ops=[<bip.hexrays.cnode.CNodeExprVar object at 0x00000278AFDAADD8>, <bip.hexrays.cnode.CNodeExprVar object at 0x00000278B1637080>])
>>> a.first_op # first operand of the assignement is a lvar, lvar are leaf
CNodeExprVar(ea=0xFFFFFFFFFFFFFFFF, value=1)
>>> a.first_op.lvar # get the lvar object
LVAR(name=v1, size=8, type=<bip.base.biptype.BTypeInt object at 0x00000278B16390B8>)
>>> a.ops # list all operands of the expression
[<bip.hexrays.cnode.CNodeExprVar object at 0x00000278AFDAADD8>, <bip.hexrays.cnode.CNodeExprVar object at 0x00000278B1639080>]
>>> a.ops[1] # getting the second operand, also a lvar
CNodeExprVar(ea=0xFFFFFFFFFFFFFFFF, value=0)
>>> hex(a.ops[1].closest_ea) # lvar have no position in the ASM, but possible to take the one of the parents
0x1800d3006L

The previous code show how to get a value and manipulate nodes quickly. To do an analysis it is easier to use visitors on the complete function. HxCFunc.visit_cnode allows to visit all the nodes in a function with a callback, HxCFunc.visit_cnode_filterlist allows to visit only nodes of a certain type by passing a list of the node classes.

This script is an example to visit a function and get the format string passed to a printk function. It locates the call to printk, gets the address of the first argument, gets the string and adds a comment in both hexrays and the assembly:

.. code-block:: python

root@kitploit:~
from bip import *

"""
    Search for all call to printk, if possible gets the string and adds
    it in comments at the level of the call.
"""

def is_call_to_printk(cn):
    """
        Check if the node object represent a call to the function ``printk``.

        :param cn: A :class:`CNodeExprCall` object.
        :return: True if it is a call to printk, False otherwise
    """
    f = cn.caller_func
    return f is not None and f.name == "printk"

def visit_call_printk(cn):
    """
        Visitor for call node which will check if a node is a call to
        ``printk`` and add the string in comment if possible.

        :param cn: A :class:`CNodeExprCall` object.
    """
    # check if it calls to printk
    # For more perf. we would want to use xref to printk and checks of
    #   the address of the node
    if not is_call_to_printk(cn): # not a call to printk: ignore
        return
    if cn.number_args < 1: # not enough args
        print("Not enough args at 0x{:X}".format(cn.closest_ea))
        return
    cnr = cn.get_arg(0).ignore_cast # get the arg
    # if we have a ref (&global) we want the object under
    if isinstance(cnr, CNodeExprRef):
        cnr = cnr.ops[0].ignore_cast
    # if this is not a global object we ignore it
    if not isinstance(cnr, CNodeExprObj):
        print("Not an object at 0x{:X}".format(cn.closest_ea))
        return
    ea = cnr.value # get the address of the object
    s = None
    try:
        s = BipData.get_cstring(ea + 2) # get the string
    except Exception:
        pass
    if s is None or s == "":
        print("Invalid string at 0x{:X}".format(cn.closest_ea))
        return
    s = s.strip() # remove \n
    # add comment both in hexrays and in asm view
    cn.hxcfunc.add_cmt(cn.closest_ea, s)
    GetElt(cn.closest_ea).comment = s

# Final function which takes the address of a function and comments the call
#   to printk
def printk_handler(eafunc):
    hf = HxCFunc.from_addr(eafunc) # get the hexrays function
    hf.visit_cnode_filterlist(visit_call_printk, [CNodeExprCall]) # visit only the call nodes

While visitors are convenient (and "fast"), Bip also exposes methods to directly get the CNode objects as a list. The methods HxCFunc.get_cnode_filter and HxCFunc.get_cnode_filter_type allow to avoid having a visitor function and make it easier to manipulate the hexrays API. It is also worth noting that all visitors functions provided by HxCFunc objects are also available directly in CNode objects to visit only a sub-tree of the full AST.

Plugins

Plugins using Bip should all inherit from the class BipPlugin. Those plugins are different from the IDA plugins and are loaded and called by the BipPluginManager. Each plugin is identified by its class name and those should be unique. Bip can be used with standard plugin but most of the bip.gui implementation is linked to the use of BipPlugin. For more information about plugins and internals see the documentation <https://synacktiv.github.io/bip/build/html/gui/plugin.html#gui-plugins>_.

Here is a simple plugin example:

.. code-block:: python

root@kitploit:~
from bip.gui import * # BipPlugin is defined in the bip.gui module

class ExPlugin(BipPlugin):
    # inherit from BipPlugin, all plugin should be instantiated only once
    # this should be done by the plugin manager, not "by hand"

    @classmethod
    def to_load(cls): # allow to test if the plugin apply, this MUST be a classmethod
        return True # always loading

    @shortcut("Ctrl-H") # add a shortcut as a decorator, will call the method below
    @shortcut("Ctrl-5") # add an other one
    @menu("Bip/MyPluginExample/", "ExPlugin Action!") # add a menu entry named "ExPlugin Action!", default is the method name
    def action_with_shortcut(self):
        print(self) # this is the ExPlugin object
        print("In ExPlugin action !")# code here

bpm = get_plugin_manager() # get the BipPluginManager object
bpm.addld_plugin("ExPlugin", ExPlugin) # ask the BipPluginManager to load the plugin
# plugins in ``bipplugin`` folder will be loaded automatically and do not need those lines

The menu decorator will automatically create the MyPluginExample menu entry in the Bip top level menu entry (which is created by the BipPluginManager), creating an entry in the Edit/Plugins/ directory may not work because of how the entry of this submenu are created by IDA.

A plugin can expose methods which another plugin wants to call or directly from the console. A plugin should not be directly instantiated, it is the BipPluginManager which is in charge of loading it. To get a BipPlugin object, it should be requested to the plugin manager:

.. code-block:: python

root@kitploit:~
from bip.gui import *
bpm = get_plugin_manager() # get the BipPluginManager object
bpm
# <bip.gui.pluginmanager.BipPluginManager object at 0x000001EFE42D68D0>
tp = bpm["TstPlugin"] # get the plugin object name TstPlugin
tp # can also be recuperated by passing directly the class
# <__plugins__tst_plg.TstPlugin object at 0x000001EFE42D69B0>
tp.hello() # calling a method of TstPlugin
# hello

For the previous example with printk we could write the following plugin:

.. code-block:: python

root@kitploit:~
class PrintkComs(BipPlugin):

    def printk_handler(self, eafunc):
        """
            Comment all call to printk in a function with the format string
            pass to the printk. Comments are added in both the hexrays and ASM
            view. Works only if the first argument is a global.

            :param eafunc: The addess of the function in which to add the
                comment.
        """
        try:
            hf = HxCFunc.from_addr(eafunc) # get hexray view of the func
        except Exception:
            print("Fail getting the decompile view for function at 0x{:X}".format(eafunc))
            return
        hf.visit_cnode_filterlist(visit_call_printk, [CNodeExprCall]) # visit only on the call

    @shortcut("Ctrl-H")
    @menu("Bip/PrintkCom/", "Comment printk in current function")
    def printk_current(self):
        """
            Add comment for the current function.
        """
        self.printk_handler(Here())

    @menu("Bip/PrintkCom/", "Comment all printk")
    def printk_all(self):
        """
            Add comment for the all the functions in the IDB.
        """
        # get the function which call printk
        f = BipFunction.get_by_name("printk")
        if f is None:
            print("No function named printk")
            return
        for fu in f.callers:
            print("Renaming for {}".format(fu))
            self.printk_handler(fu.ea)

Similar projects

  • sark <https://sark.readthedocs.io/en/latest/>_: "an object-oriented scripting layer written on top of IDAPython".
  • ida-minsc <https://github.com/arizvisa/ida-minsc>_: "a plugin for IDA Pro that assists a user with scripting the IDAPython plugin that is bundled with the disassembler".
  • FIDL <https://github.com/fireeye/FIDL>_: "FLARE IDA Decompiler Library"

Thanks

Some people to thanks:

  • saph <https://twitter.com/clementberthaux>_: for starting this project.
  • hakril <https://twitter.com/hakril>_: for the inspiration for the project and his insights on designing it.
도구 다운로드