首页
社区
课程
招聘
[原创]IDA Hex-Rays Microcode技术入门
发表于: 2025-7-17 10:33 5227

[原创]IDA Hex-Rays Microcode技术入门

scz 活跃值
5
2025-7-17 10:33
5227

☆ 背景介绍

参看

上文提到XorDDoS僵尸网络家族的某样本,此样本已无技术分析价值,仅用于演示逆向工程相关技术。

IDA32反汇编样本,会调dec_conf()解密还原字符串:

上文用r2pipe模块静态分析样本,找到"call dec_conf"指令所在地址,再从附近的汇编指令析取dec_conf()的参数,比如加密字符串的地址、长度。此法要求汇编指令布局具有固定模式,否则要考虑各种情况,实现繁琐。

IDA F5反编译main()时,注意到

Hex-Rays已识别出dec_conf()的参数。IDAPython脚本可调用Microcode API,充分利用Hex-Rays分析结果,从中析取dec_conf()的参数。此法不依赖汇编指令布局,依赖IDA生成的微码。相比汇编指令,微码抽象层级更高。在IDA F5语境中,一般有

利用微码技术析取加密字符串的地址、长度后,由于所涉及的解密算法很简单,可在IDAPython脚本中完成解密。假设已有明文字符串,更直观的需求是,能否将F5结果替换成

本文围绕此需求进一步演示相关技术。

☆ 用Hex-Rays Microcode技术析取函数参数

Microcode API文档不多,若能在某种IDE中调试使用它的IDAPython脚本,对学习API非常有益。参看

用VSCode调试IDAPython脚本时,应避免使用中文目录名,否则可能出现断点不生效的现象。

样本涉及的解密算法很简单,就是异或:

XorDDoS_analyse_b.py

简介整体框架。通过交叉引用获取dec_conf()的主调函数,依次对它们生成微码。为析取函数参数,gen_microcode()必须指定MMAT_CALLS。for_all_ops()对指定函数进行mop遍历,每遇上一个mop都用自定义visit_mop()处理一遍。

visit_mop()是使用微码API的核心,通过op.t识别出函数调用,检查是否在调用dec_conf(),检查是否有三个参数,检查三个参数的类型,要求第二形参必须是全局变量的地址,第三形参必须是整数;这些检查可减少误命中。检查通过后,取加密字符串的地址、长度,对之进行异或解密,用解密结果设置"可重复注释"。

复杂解密逻辑另说,本文只是用此样本演示Microcode API的使用。

☆ 在F5伪码中使用解密后的明文字符串

XorDDoS_analyse_e.py

为了影响F5伪码,visit_mop()不但需要得到明文字符串,还要在微码层面修改调用dec_conf()时所传递的参数,原来传的指针指向密文,现将指针指向明文,代码中的newaddr对应明文。

处理此需求的传统方式是静态Patch样本或者IDA数据库(some.idb)。本文演示另一种技术方案,优劣另说。

在PrivateGetSeg()中创建名为".patch"的段,Private_add_bytes_to_idb()在".patch"中安置明文字符串。newaddr指向".patch"中适当位置,在微码层面使newaddr成为dec_conf()的参数。

此次不能用gen_microcode()生成微码,需要找"Pseudocode-A"窗口对应的微码,并对之修改,否则无法影响F5伪码。find_pseudocode_view()找窗口,找不到就打开一个。对vu.cfunc.mba使用visit_mop(),最后需要vu.refresh_view(False)。

此法未持久化,并不推荐。比如在使用明文字符串的F5窗口再次手工F5,将恢复到密文状态,因为此操作产生新的微码,且未被修改。可在IDAPython提示符中执行decompile(),又能看到使用明文字符串的F5伪码。

☆ idaapi.Hexrays_Hooks示例

上一小节的办法不够正规,脱离明文字符串状态后,得手工执行decompile(),或重新加载XorDDoS_analyse_e.py,太傻。本小节演示idaapi.Hexrays_Hooks技术,脚本加载后,对微码的修改是持久化的,不会脱离明文字符串状态。

XorDDoS_analyse_c.py

安装Hexrays_Hooks,重载calls_done(),每次F5都会触发此函数,在其中对微码使用visit_mop(),确保每次F5得到的都是修改过的微码,从而实现持久化。

☆ 其他讨论

bluerust展示过另一种持久化技术,实现idaapi.optinsn_t的派生类,重载func(),在其中触发visit_minsn(),继而触发visit_mop()。这种方案可能更正规,好奇的不妨一试。

增强阅读,可查看ida_hexrays.py中下列函数的注释,再问问AI

pass:infected

创建: 2025-07-07 16:31
更新: 2025-07-16 17:31
 
目录:
 
    ☆ 背景介绍
    ☆ 用Hex-Rays Microcode技术析取函数参数
    ☆ 在F5伪码中使用解密后的明文字符串
    ☆ idaapi.Hexrays_Hooks示例
    ☆ 其他讨论
创建: 2025-07-07 16:31
更新: 2025-07-16 17:31
 
目录:
 
    ☆ 背景介绍
    ☆ 用Hex-Rays Microcode技术析取函数参数
    ☆ 在F5伪码中使用解密后的明文字符串
    ☆ idaapi.Hexrays_Hooks示例
    ☆ 其他讨论
《Angr符号执行练习--XorDDoS某样本字符串解密》
https://scz.617.cn/unix/202504242226.txt
 
XorDDoS僵尸网络家族的某样本
https://0a9K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4k6A6M7Y4g2K6N6r3!0@1j5h3I4Q4x3X3g2U0L8$3@1`./gui/file/0e9e859d22b009e869322a509c11e342
《Angr符号执行练习--XorDDoS某样本字符串解密》
https://scz.617.cn/unix/202504242226.txt
 
XorDDoS僵尸网络家族的某样本
https://472K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4k6A6M7Y4g2K6N6r3!0@1j5h3I4Q4x3X3g2U0L8$3@1`./gui/file/0e9e859d22b009e869322a509c11e342
0804CFA3 C7 44 24 08 0B 00 00 00     mov     dword ptr [esp+8], 0Bh
0804CFAB C7 44 24 04 B1 2F 0B 08     mov     dword ptr [esp+4], offset aM7a4nqNa_0 ; "m7A4nQ_/nA"
0804CFB3 8D 85 B3 EA FF FF           lea     eax, [ebp+var_154D]
0804CFB9 89 04 24                    mov     [esp], eax
0804CFBC E8 67 B2 FF FF              call    dec_conf
0804CFC1 C7 44 24 08 07 00 00 00     mov     dword ptr [esp+8], 7
0804CFC9 C7 44 24 04 BC 2F 0B 08     mov     dword ptr [esp+4], offset aMN3_0 ; "m [(n3"
0804CFD1 8D 85 B3 E9 FF FF           lea     eax, [ebp+var_164D]
0804CFD7 89 04 24                    mov     [esp], eax
0804CFDA E8 49 B2 FF FF              call    dec_conf
0804CFA3 C7 44 24 08 0B 00 00 00     mov     dword ptr [esp+8], 0Bh
0804CFAB C7 44 24 04 B1 2F 0B 08     mov     dword ptr [esp+4], offset aM7a4nqNa_0 ; "m7A4nQ_/nA"
0804CFB3 8D 85 B3 EA FF FF           lea     eax, [ebp+var_154D]
0804CFB9 89 04 24                    mov     [esp], eax
0804CFBC E8 67 B2 FF FF              call    dec_conf
0804CFC1 C7 44 24 08 07 00 00 00     mov     dword ptr [esp+8], 7
0804CFC9 C7 44 24 04 BC 2F 0B 08     mov     dword ptr [esp+4], offset aMN3_0 ; "m [(n3"
0804CFD1 8D 85 B3 E9 FF FF           lea     eax, [ebp+var_164D]
0804CFD7 89 04 24                    mov     [esp], eax
0804CFDA E8 49 B2 FF FF              call    dec_conf
dec_conf(v23, "m7A4nQ_/nA", 11);
dec_conf(v22, "m [(n3", 7);
dec_conf(v21, "m6_6n3", 7);
dec_conf(v19, aM4s4nacNZv, 18);
dec_conf(v18, aMN4C, 17);
dec_conf(v17, "m.[$n3", 7);
dec_conf(v16, a6f6, 512);
dec_conf(v20, "m4S4nAC/nA", 11);
dec_conf(v23, "m7A4nQ_/nA", 11);
dec_conf(v22, "m [(n3", 7);
dec_conf(v21, "m6_6n3", 7);
dec_conf(v19, aM4s4nacNZv, 18);
dec_conf(v18, aMN4C, 17);
dec_conf(v17, "m.[$n3", 7);
dec_conf(v16, a6f6, 512);
dec_conf(v20, "m4S4nAC/nA", 11);
汇编指令 => 微码 => cfunc
汇编指令 => 微码 => cfunc
dec_conf(v23, "/usr/bin/", 11);
dec_conf(v22, "/bin/", 7);
dec_conf(v21, "/tmp/", 7);
dec_conf(v19, "/var/run/gcc.pid", 18);
dec_conf(v18, "/lib/libudev.so", 17);
dec_conf(v17, "/lib/", 7);
dec_conf(v16, "ebfK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4m8U0k6r3!0%4L8W2)9J5k6h3N6V1k6r3!0K6i4K6u0W2j5$3!0E0i4K6y4m8z5o6l9^5x3q4)9J5c8X3y4X3k6#2)9J5k6i4u0S2M7R3`.`.", 512);
dec_conf(v20, "/var/run/", 11);
dec_conf(v23, "/usr/bin/", 11);
dec_conf(v22, "/bin/", 7);
dec_conf(v21, "/tmp/", 7);
dec_conf(v19, "/var/run/gcc.pid", 18);
dec_conf(v18, "/lib/libudev.so", 17);
dec_conf(v17, "/lib/", 7);
dec_conf(v16, "f31K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4m8U0k6r3!0%4L8W2)9J5k6h3N6V1k6r3!0K6i4K6u0W2j5$3!0E0i4K6y4m8z5o6l9^5x3q4)9J5c8X3y4X3k6#2)9J5k6i4u0S2M7R3`.`.", 512);
dec_conf(v20, "/var/run/", 11);
《用VSCode+debugpy调试IDAPython插件(简略版)》
https://scz.617.cn/python/202507021618.txt
《用VSCode+debugpy调试IDAPython插件(简略版)》
https://scz.617.cn/python/202507021618.txt
char *__cdecl encrypt_code(char *buf, int size)
{
    char *p;
    int i;
 
    p = buf;
    for ( i = 0; i < size; ++i )
        *p++ ^= xorkeys[i % 16];
    return buf;
}
char *__cdecl encrypt_code(char *buf, int size)
{
    char *p;
    int i;
 
    p = buf;
    for ( i = 0; i < size; ++i )
        *p++ ^= xorkeys[i % 16];
    return buf;
}
080CF3E8 42 42 32 46 41 33 36 41…xorkeys db 'BB2FA36AAA9541F0'
080CF3E8 42 42 32 46 41 33 36 41…xorkeys db 'BB2FA36AAA9541F0'
#!/usr/bin/env python
# -*- coding: cp936 -*-
 
import traceback
import idaapi, idautils
 
#
# Wrap a function in try-except block
#
def catch ( func ) :
 
    def wrapper ( *args, **kwargs ) :
        try :
            return func( *args, **kwargs )
        except Exception as e :
            traceback.print_exc()
            print( e )
 
    return wrapper
 
#
# Visit all mop_t
#
class MopVisitor ( idaapi.mop_visitor_t ) :
 
    def __init__ ( self, target, xorkey, keysize ) :
        super().__init__()
        self.target     = target
        self.xorkey     = xorkey
        self.keysize    = keysize
 
    @catch
    def visit_mop ( self, op, var_type, is_target ) :
        del var_type, is_target
        # logging.debug( "Operand: %s", op.dstr() )
        #
        # mop_f   list of arguments
        #
        if op.t != idaapi.mop_f :
            return 0
 
        call_info   = op.f
        if call_info.callee != self.target :
            return 0
 
        #
        # mop_a   绝对地址
        # mop_n   immediate number constant
        #
        if len( call_info.args ) < 3 or \
           call_info.args[0].t != idaapi.mop_a or \
           call_info.args[1].t != idaapi.mop_a or \
           call_info.args[2].t != idaapi.mop_n :
            return 0
 
        addr        = call_info.args[1].a
        #
        # mop_v   global variable
        # mop_S   local stack variable
        #
        if call_info.args[1].a.t != idaapi.mop_v :
            return 0
 
        addr        = call_info.args[1].a.g
        size        = call_info.args[2].nnn.value
        dec_buf     = bytearray()
        for i in range( size ) :
            enc_byte    = idaapi.get_byte( addr + i )
            key_byte    = idaapi.get_byte( self.xorkey + ( i % self.keysize ) )
            dec_buf.append( enc_byte ^ key_byte )
 
        try :
            dec_str = dec_buf.split( b'\0' )[0].decode( 'latin-1' )
        except UnicodeDecodeError :
            dec_str = f"Unprintable data: {dec_buf.hex()}"
 
        print( f"{addr:#x}: {dec_str}" )
        #
        # repeatable comment
        #
        idaapi.set_cmt( addr, dec_str, 1 )
 
        #
        # 返回0表示继续遍历
        #
        return 0
 
def main () :
    target      = idaapi.get_name_ea( 0, "dec_conf" )
    if target == idaapi.BADADDR :
        print( "Not found dec_conf()" )
        return
 
    xorkey      = 0x80cf3e8
    keysize     = 16
    callerset   = set()
    for ref in idautils.XrefsTo( target ) :
        f   = idaapi.get_func( ref.frm )
        #
        # 可能多个交叉引用位于同一主调函数,对此只gen_microcode()一次
        #
        if f and f.start_ea not in callerset :
            callerset.add( f.start_ea )
            if not idaapi.is_code( idaapi.get_flags( f.start_ea ) ) :
                continue
 
            hf  = idaapi.hexrays_failure_t()
            mbr = idaapi.mba_ranges_t( f )
            try :
                #
                # 对指定函数生成微码(Hex-Rays Microcode),本例是dec_conf()
                # 的主调函数
                #
                mba = idaapi.gen_microcode( mbr, hf, None, idaapi.DECOMP_WARNINGS, idaapi.MMAT_CALLS )
                if mba :
                    #
                    # 在微码中析取指定函数的调用参数,与抽象层级更高的微码
                    # 打交道,而非与汇编代码直接打交道
                    #
                    visitor = MopVisitor( target, xorkey, keysize )
                    mba.for_all_ops( visitor )
            except idaapi.DecompilationFailure :
                print( f"Decompilation failed for func at {f.start_ea:#x}" )
 
if "__main__" == __name__ :
    if '__idacode__' in globals() and __idacode__ :
        dbg.bp( __idacode__, 'Hit breakpoint' )
    main()
#!/usr/bin/env python
# -*- coding: cp936 -*-
 
import traceback
import idaapi, idautils
 
#
# Wrap a function in try-except block
#
def catch ( func ) :
 
    def wrapper ( *args, **kwargs ) :
        try :
            return func( *args, **kwargs )
        except Exception as e :
            traceback.print_exc()
            print( e )
 
    return wrapper
 
#
# Visit all mop_t
#
class MopVisitor ( idaapi.mop_visitor_t ) :
 
    def __init__ ( self, target, xorkey, keysize ) :
        super().__init__()
        self.target     = target
        self.xorkey     = xorkey
        self.keysize    = keysize
 
    @catch
    def visit_mop ( self, op, var_type, is_target ) :
        del var_type, is_target
        # logging.debug( "Operand: %s", op.dstr() )
        #
        # mop_f   list of arguments
        #
        if op.t != idaapi.mop_f :
            return 0
 
        call_info   = op.f
        if call_info.callee != self.target :
            return 0
 
        #
        # mop_a   绝对地址
        # mop_n   immediate number constant
        #
        if len( call_info.args ) < 3 or \
           call_info.args[0].t != idaapi.mop_a or \
           call_info.args[1].t != idaapi.mop_a or \
           call_info.args[2].t != idaapi.mop_n :
            return 0
 
        addr        = call_info.args[1].a
        #
        # mop_v   global variable
        # mop_S   local stack variable
        #
        if call_info.args[1].a.t != idaapi.mop_v :
            return 0
 
        addr        = call_info.args[1].a.g
        size        = call_info.args[2].nnn.value
        dec_buf     = bytearray()
        for i in range( size ) :
            enc_byte    = idaapi.get_byte( addr + i )
            key_byte    = idaapi.get_byte( self.xorkey + ( i % self.keysize ) )
            dec_buf.append( enc_byte ^ key_byte )
 
        try :
            dec_str = dec_buf.split( b'\0' )[0].decode( 'latin-1' )
        except UnicodeDecodeError :
            dec_str = f"Unprintable data: {dec_buf.hex()}"
 
        print( f"{addr:#x}: {dec_str}" )
        #
        # repeatable comment
        #
        idaapi.set_cmt( addr, dec_str, 1 )
 
        #
        # 返回0表示继续遍历
        #
        return 0
 
def main () :
    target      = idaapi.get_name_ea( 0, "dec_conf" )
    if target == idaapi.BADADDR :
        print( "Not found dec_conf()" )
        return
 
    xorkey      = 0x80cf3e8
    keysize     = 16
    callerset   = set()
    for ref in idautils.XrefsTo( target ) :
        f   = idaapi.get_func( ref.frm )
        #
        # 可能多个交叉引用位于同一主调函数,对此只gen_microcode()一次
        #
        if f and f.start_ea not in callerset :
            callerset.add( f.start_ea )
            if not idaapi.is_code( idaapi.get_flags( f.start_ea ) ) :
                continue
 
            hf  = idaapi.hexrays_failure_t()
            mbr = idaapi.mba_ranges_t( f )
            try :
                #
                # 对指定函数生成微码(Hex-Rays Microcode),本例是dec_conf()
                # 的主调函数
                #
                mba = idaapi.gen_microcode( mbr, hf, None, idaapi.DECOMP_WARNINGS, idaapi.MMAT_CALLS )
                if mba :
                    #
                    # 在微码中析取指定函数的调用参数,与抽象层级更高的微码
                    # 打交道,而非与汇编代码直接打交道
                    #
                    visitor = MopVisitor( target, xorkey, keysize )
                    mba.for_all_ops( visitor )
            except idaapi.DecompilationFailure :
                print( f"Decompilation failed for func at {f.start_ea:#x}" )
 
if "__main__" == __name__ :
    if '__idacode__' in globals() and __idacode__ :
        dbg.bp( __idacode__, 'Hit breakpoint' )
    main()
#!/usr/bin/env python
# -*- coding: cp936 -*-
 
import traceback
import idaapi, idautils
 
#
# 不要与已知段名冲突
#
def PrivateGetSeg ( name ) :
    seg             = idaapi.get_segm_by_name( name )
    if seg :
        return seg
    seg_start       = idaapi.inf_get_max_ea()
    seg_start       = ( seg_start + 0xf ) & ~0xf
    seg_size        = 0x1000
    seg             = idaapi.segment_t()
    seg.start_ea    = seg_start
    seg.end_ea      = seg_start + seg_size
    seg.perm        = idaapi.SEGPERM_READ
    seg.bitness     = 2 if idaapi.get_inf_structure().is_64bit() else 1
    idaapi.add_segm_ex( seg, name, "CONST", idaapi.ADDSEG_NOSREG )
    return seg
 
def Private_add_bytes_to_idb ( segname, off, buf ) :
    seg = PrivateGetSeg( segname )
    if not seg :
        return idaapi.BADADDR
    if off < seg.start_ea or off + len( buf ) > seg.end_ea :
        return idaapi.BADADDR
    idaapi.patch_bytes( off, buf )
    return off + len( buf )
 
def catch ( func ) :
 
    def wrapper ( *args, **kwargs ) :
        try :
            return func( *args, **kwargs )
        except Exception as e :
            traceback.print_exc()
            print( e )
 
    return wrapper
 
class MopVisitor ( idaapi.mop_visitor_t ) :
 
    def __init__ ( self, target, xorkey, keysize, addrdict, seg ) :
        super().__init__()
        # self.mba        = mba
        self.target     = target
        self.xorkey     = xorkey
        self.keysize    = keysize
        self.addrdict   = addrdict
        self.seg        = seg
 
    @catch
    def visit_mop ( self, op, var_type, is_target ) :
        del var_type, is_target
        if op.t != idaapi.mop_f :
            return 0
 
        call_info   = op.f
        if call_info.callee != self.target :
            return 0
 
        if len( call_info.args ) < 3 or \
           call_info.args[0].t != idaapi.mop_a or \
           call_info.args[1].t != idaapi.mop_a or \
           call_info.args[2].t != idaapi.mop_n :
            return 0
 
        addr        = call_info.args[1].a
        if call_info.args[1].a.t != idaapi.mop_v :
            return 0
 
        addr        = call_info.args[1].a.g
        seg         = PrivateGetSeg( self.seg["name"] )
        if seg and addr >= seg.start_ea and addr < seg.end_ea :
            return 0
        if addr not in self.addrdict :
            size    = call_info.args[2].nnn.value
            dec_buf = bytearray()
            for i in range( size ) :
                enc_byte    = idaapi.get_byte( addr + i )
                key_byte    = idaapi.get_byte( self.xorkey + ( i % self.keysize ) )
                dec_buf.append( enc_byte ^ key_byte )
 
            dec_buf = dec_buf.split( b'\0' )[0]
            try :
                dec_str = dec_buf.decode( 'latin-1' )
            except UnicodeDecodeError :
                dec_str = f"Unprintable data: {dec_buf.hex()}"
 
            print( f"{self.curins.ea:#x} {addr:#x} {dec_str}" )
            idaapi.set_cmt( addr, dec_str, 1 )
 
            dec_buf.append( 0 )
            off     = Private_add_bytes_to_idb( self.seg["name"], self.seg["off"], bytes( dec_buf ) )
            if idaapi.BADADDR == off :
                return 0
            idaapi.create_strlit( self.seg["off"], len( dec_buf ), idaapi.STRTYPE_C )
            self.addrdict[addr] \
                    = self.seg["off"]
            self.seg["off"] \
                    = off
 
        newaddr     = idaapi.mop_addr_t()
        newaddr.make_gvar( self.addrdict[addr] )
        call_info.args[1].a.assign( newaddr )
        return 0
 
def find_pseudocode_view ( func_ea, auto_open=False ) :
    widget  = idaapi.find_widget( "Pseudocode-A" )
    if widget and idaapi.get_widget_type( widget ) == idaapi.BWN_PSEUDOCODE :
        vu  = idaapi.get_widget_vdui( widget )
        if vu and vu.cfunc.entry_ea == func_ea :
            return vu
 
    if auto_open :
        idaapi.open_pseudocode( func_ea, idaapi.OPF_REUSE )
        return find_pseudocode_view( func_ea, False )
 
    return None
 
#
# 因为main()、decompile_func()用同一套,只好放在全局变量中
#
addrdict    = {}
seg         = {}
seg["name"] = ".patch"
seg["off"= PrivateGetSeg( seg["name"] ).start_ea
 
def decompile_func ( func_ea ) :
    target  = idaapi.get_name_ea( 0, "dec_conf" )
    if target == idaapi.BADADDR :
        print( "Not found dec_conf()" )
        return
 
    xorkey  = 0x80cf3e8
    keysize = 16
    vu      = find_pseudocode_view( func_ea, True )
    if not vu :
        return
 
    mba     = vu.cfunc.mba
    if not mba :
        return
    visitor = MopVisitor( target, xorkey, keysize, addrdict, seg )
    mba.for_all_ops( visitor )
    #
    # false means to rebuild ctree without regenerating microcode
    #
    vu.refresh_view( False )
 
def decompile () :
    ea      = idaapi.get_screen_ea()
    func    = idaapi.get_func( ea )
    if func :
        decompile_func( func.start_ea )
 
def main () :
    target      = idaapi.get_name_ea( 0, "dec_conf" )
    if target == idaapi.BADADDR :
        print( "Not found dec_conf()" )
        return
 
    xorkey      = 0x80cf3e8
    keysize     = 16
    callerset   = set()
    for ref in idautils.XrefsTo( target ) :
        f   = idaapi.get_func( ref.frm )
        if f and f.start_ea not in callerset :
            callerset.add( f.start_ea )
            if not idaapi.is_code( idaapi.get_flags( f.start_ea ) ) :
                continue
            decompile_func( f.start_ea )
 
if "__main__" == __name__ :
    if '__idacode__' in globals() and __idacode__ :
        dbg.bp( __idacode__, 'Hit breakpoint' )
    main()
#!/usr/bin/env python
# -*- coding: cp936 -*-
 
import traceback
import idaapi, idautils
 
#
# 不要与已知段名冲突
#
def PrivateGetSeg ( name ) :
    seg             = idaapi.get_segm_by_name( name )
    if seg :
        return seg
    seg_start       = idaapi.inf_get_max_ea()
    seg_start       = ( seg_start + 0xf ) & ~0xf
    seg_size        = 0x1000
    seg             = idaapi.segment_t()
    seg.start_ea    = seg_start
    seg.end_ea      = seg_start + seg_size
    seg.perm        = idaapi.SEGPERM_READ
    seg.bitness     = 2 if idaapi.get_inf_structure().is_64bit() else 1
    idaapi.add_segm_ex( seg, name, "CONST", idaapi.ADDSEG_NOSREG )
    return seg
 
def Private_add_bytes_to_idb ( segname, off, buf ) :
    seg = PrivateGetSeg( segname )
    if not seg :
        return idaapi.BADADDR
    if off < seg.start_ea or off + len( buf ) > seg.end_ea :
        return idaapi.BADADDR
    idaapi.patch_bytes( off, buf )
    return off + len( buf )
 
def catch ( func ) :
 
    def wrapper ( *args, **kwargs ) :
        try :
            return func( *args, **kwargs )
        except Exception as e :
            traceback.print_exc()
            print( e )
 
    return wrapper
 
class MopVisitor ( idaapi.mop_visitor_t ) :
 
    def __init__ ( self, target, xorkey, keysize, addrdict, seg ) :
        super().__init__()
        # self.mba        = mba
        self.target     = target
        self.xorkey     = xorkey
        self.keysize    = keysize
        self.addrdict   = addrdict
        self.seg        = seg
 
    @catch
    def visit_mop ( self, op, var_type, is_target ) :
        del var_type, is_target
        if op.t != idaapi.mop_f :
            return 0
 
        call_info   = op.f
        if call_info.callee != self.target :
            return 0
 
        if len( call_info.args ) < 3 or \
           call_info.args[0].t != idaapi.mop_a or \
           call_info.args[1].t != idaapi.mop_a or \
           call_info.args[2].t != idaapi.mop_n :
            return 0
 
        addr        = call_info.args[1].a
        if call_info.args[1].a.t != idaapi.mop_v :
            return 0
 
        addr        = call_info.args[1].a.g
        seg         = PrivateGetSeg( self.seg["name"] )
        if seg and addr >= seg.start_ea and addr < seg.end_ea :
            return 0
        if addr not in self.addrdict :
            size    = call_info.args[2].nnn.value
            dec_buf = bytearray()
            for i in range( size ) :
                enc_byte    = idaapi.get_byte( addr + i )
                key_byte    = idaapi.get_byte( self.xorkey + ( i % self.keysize ) )
                dec_buf.append( enc_byte ^ key_byte )
 
            dec_buf = dec_buf.split( b'\0' )[0]
            try :
                dec_str = dec_buf.decode( 'latin-1' )
            except UnicodeDecodeError :
                dec_str = f"Unprintable data: {dec_buf.hex()}"

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 9
支持
分享
最新回复 (6)
雪    币: 624
活跃值: (5258)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
这些插件支持arm么
2025-7-23 12:05
0
雪    币: 7239
活跃值: (5782)
能力值: ( LV12,RANK:280 )
在线值:
发帖
回帖
粉丝
3
gtict 这些插件支持arm么
没拿ARM实测过,但我觉得,从原理上讲,微码是CPU无关的,应该适用于ARM。你试试呗
2025-7-23 14:21
0
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
能私聊吗
2025-7-24 22:46
0
雪    币: 1277
活跃值: (6673)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
5
scz 没拿ARM实测过,但我觉得,从原理上讲,微码是CPU无关的,应该适用于ARM。你试试呗

ida的ir 太难看了。真的。感觉有点反人类。


操作microcode有一个做了一点逆向的。

d9dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6X3L8%4u0#2L8g2)9J5k6i4u0W2N6X3g2J5M7$3f1@1P5h3!0#2i4K6u0W2L8%4u0Y4i4K6u0r3N6q4)9J5c8X3S2W2P5r3g2^5N6q4)9J5k6r3q4Q4x3X3c8H3L8s2g2Y4K9h3&6Q4x3X3c8X3L8%4u0Q4x3X3c8W2P5s2c8W2L8X3c8A6L8X3N6Q4x3X3c8Z5k6i4S2J5j5i4W2K6i4K6u0V1y4#2)9J5k6o6m8Q4x3X3c8$3K9h3q4Q4x3X3c8E0K9h3y4J5L8$3y4G2k6r3g2Q4x3X3c8F1L8%4N6Q4x3X3c8%4K9i4c8Z5i4K6u0V1x3K6u0Q4x3X3c8T1K9i4c8Q4x3X3c8K6N6i4m8H3L8%4u0@1i4K6u0r3x3e0l9$3x3K6q4Q4x3V1j5K6
对应的代码在
399K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6V1K9i4y4B7N6s2q4*7i4K6u0r3d9r3g2^5k6i4S2@1
感觉用来学些microcode很不错。


还有一个

2aaK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2W2L8r3q4K6N6r3W2U0i4K6u0W2j5$3!0Q4x3V1k6K6k6h3y4#2M7X3W2@1P5g2)9J5k6r3I4S2j5Y4y4Q4x3V1k6A6L8Y4c8J5L8$3c8#2j5%4c8A6L8$3&6Q4x3X3c8@1L8#2)9J5k6r3S2W2P5s2u0S2P5i4y4Q4x3X3c8V1k6h3y4G2L8i4m8A6L8r3q4@1K9h3!0F1i4K6u0V1K9h3&6@1k6i4u0F1j5h3I4K6

最后于 2025-7-25 11:15 被IamHuskar编辑 ,原因:
2025-7-25 10:16
0
雪    币: 149
活跃值: (632)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
前来学习,我顶
2025-7-29 20:05
0
雪    币: 13
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
太牛了,巨佬
2025-10-12 23:53
0
游客
登录 | 注册 方可回帖
返回