美文网首页
patch elf文件 - 使用lief

patch elf文件 - 使用lief

作者: 喝豆腐脑加糖 | 来源:发表于2019-03-18 19:41 被阅读0次

    lief详细介绍

    import lief
    # ELF
    binary = lief.parse("/usr/bin/ls")
    print(binary)
    
    # PE
    binary = lief.parse("C:\\Windows\\explorer.exe")
    print(binary)
    
    # Mach-O
    binary = lief.parse("/usr/bin/ls")
    print(binary)
    
    # OAT
    binary = lief.parse("android.odex")
    print(binary)
    
    # DEX
    dex = lief.DEX.parse("classes.dex")
    print(dex)
    
    # VDEX
    vdex = lief.VDEX.parse("classes.vdex")
    print(vdex)
    
    # ART
    art = lief.ART.parse("boot.art")
    print(art)
    

    可打印出文件各个区段,头,节区等信息

    header = binary.header
    
    图片.png
    更改入口点和目标体系结构[ARCH]
    header.entrypoint = 0x123
    header.machine_type = lief.ELF.ARCH.AARCH64
    binary.write("ls.modified")  //重建写入新文件
    
    图片.png 图片.png

    我们还可以迭代输出二进制[节区]

    for section in binary.sections:
      print(section.name) # section's name
      print(section.size) # section's size
      print(len(section.content)) # Should match the previous print
    

    要修改该.text部分的内容

    text = binary.get_section(".text")
    text.content = bytes([0x33] * text.size)
    
    图片.png

    使用lief创建简单PE

    创建文件完整脚本

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    # Description:
    # Create a PE which pop a MessageBox
    # with the message "Hello World"
    
    from lief import PE
    
    title   = "LIEF is awesome\0"
    message = "Hello World\0"
    
    data =  list(map(ord, title))
    data += list(map(ord, message))
    code = [
            0x6a, 0x00,                         # push 0x00 uType
            0x68, 0x00, 0x20, 0x40, 0x00,       # push VA(title)
            0x68, 0x10, 0x20, 0x40, 0x00,       # push VA(message)
            0x6a, 0x00,                         # push 0 hWnd
            0xFF, 0x15, 0x54, 0x30, 0x40, 0x00, # call MessageBoxA
            0x6A, 0x00,                         # push 0 uExitCode
            0xFF, 0x15, 0x4C, 0x30, 0x40, 0x00  # call ExitProcess
            ]
    
    binary32 = PE.Binary("pe_from_scratch", PE.PE_TYPE.PE32)
    
    section_text                 = PE.Section(".text")
    section_text.content         = code
    section_text.virtual_address = 0x1000
    
    section_data                 = PE.Section(".data")
    section_data.content         = data
    section_data.virtual_address = 0x2000
    
    section_text = binary32.add_section(section_text, PE.SECTION_TYPES.TEXT)
    section_data = binary32.add_section(section_data, PE.SECTION_TYPES.DATA)
    
    print(section_text)
    print(section_data)
    
    binary32.optional_header.addressof_entrypoint = section_text.virtual_address
    
    kernel32 = binary32.add_library("kernel32.dll")
    kernel32.add_entry("ExitProcess")
    
    user32 = binary32.add_library("user32.dll")
    user32.add_entry("MessageBoxA")
    
    
    ExitProcess_addr = binary32.predict_function_rva("kernel32.dll", "ExitProcess")
    MessageBoxA_addr = binary32.predict_function_rva("user32.dll", "MessageBoxA")
    print("Address of 'ExitProcess': 0x{:06x} ".format(ExitProcess_addr))
    print("Address of 'MessageBoxA': 0x{:06x} ".format(MessageBoxA_addr))
    
    builder = PE.Builder(binary32)
    builder.build_imports(True)
    builder.build()
    builder.write("pe_from_scratch.exe")
    
    

    step - 1

    先创建一个对象

    from lief import PE
     
    binary32 = PE.Binary("pe_from_scratch", PE.PE_TYPE.PE32)
    

    第一个参数是二进制文件的名字,第二个参数是PE文件的类型:PE32或是PE64(PE_TYPE)。Binary的构造器可以自动创建DosHeader,Header,OptionalHeader以及一个空的DataDirectory

    step - 2

    创建区段

    section_text                 = PE.Section(".text")
    section_text.content         = code
    section_text.virtual_address = 0x1000
     
    section_data                 = PE.Section(".data")
    section_data.content         = data
    section_data.virtual_address = 0x2000
    

    step - 3
    MessageBoxA由title和message组成。这两个字符串将存储在.data段中:

    title   = "LIEF is awesome\0"
    message = "Hello World\0"
     
    data =  list(map(ord, title))
    data += list(map(ord, message))
    

    step - 4

    创建text段asm代码

    push 0x00              ; uType
    push "LIEF is awesome" ; Title
    push "Hello World"     ; Message
    push 0                 ; hWnd
    call MessageBoxA       ;
    push 0                 ; uExitCode
    call ExitProcess       ;
    

    我们push的不是ascii,而是字符串所在的虚拟地址。在PE格式中,虚拟地址表示的是相对虚拟地址(如果ASLR不开启的话,是相对于Optional.imagebase)。Binary构造器会默认把imagebase设为0x400000

    字符串的虚拟地址计算如下的如下:

    title:imagebase+virtual_address+0=0x402000
    message:imagebase+virtual_address+len(title)=0x402010
    

    step - 5

    载入dll文件

    MessageBoxA,我们需要将user32.dll放进Imports表中,并将MessageBoxA放进ImportEntry中;
    使用add_library()和add_entry()来实现

    user32 = binary32.add_library("user32.dll")
    user32.add_entry("MessageBoxA")
    

    ExitProcess(kernel32.dll)的导入也是:

    kernel32 = binary32.add_library("kernel32.dll")
    kernel32.add_entry("ExitProcess")
    

    step - 6

    确定导入库的地址

    使用predict_funciton_rva()方法,它会返回由Builder设置的IAT地址:

    ExitProcess_addr = binary32.predict_function_rva("kernel32.dll", "ExitProcess")
    MessageBoxA_addr = binary32.predict_function_rva("user32.dll", 
    "MessageBoxA")
    print("Address of 'ExitProcess': 0x{:06x} ".format(ExitProcess_addr))
    print("Address of 'MessageBoxA': 0x{:06x} ".format(MessageBoxA_addr))
    
    Address of 'ExitProcess': 0x00304c
    Address of 'MessageBoxA': 0x003054
    

    MessageBoxA和ExitProcess的绝对虚拟地址是:

    MessageBoxA: imagebase + 0x306a = 0x40306a
    ExitProcess: imagebase + 0x305c = 0x40305c
    

    汇编代码

    push 0x00              ; uType
    push 0x402000          ; Title
    push 0x402010          ; Message
    push 0                 ; hWnd
    call 0x40306a          ;
    push 0                 ; uExitCode
    call 0x40305c          ;
    

    step - 7
    将Binary对象转化为可执行文件的操作是由Builder类来实现的;导入表不会被重建,所以我们需要手动配置

    builder = lief.PE.Builder(binary32)
    builder.build_imports(True)
    builder.build()
    builder.write("pe_from_scratch.exe")
    

    修改ELF符号
    使用exported_functions和imported_functions对funtion进行枚举

    import lief
    binary  = lief.parse("/usr/bin/ls")
    library = lief.parse("/usr/lib/libc.so.6")
     
    print(binary.imported_functions)
    print(library.exported_functions)
    

    使用lief将下面函数名进行更换

    gcc  jing.c -lm
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
     
    double hashme(double input) {
      return pow(input, 4) + log(input + 3);
    }
     
    int main(int argc, char** argv) {
      if (argc != 2) {
        printf("Usage: %s N\n", argv[0]);
        return EXIT_FAILURE;
      }
     
      double N = (double)atoi(argv[1]);
      double hash = hashme(N);
      printf("%f\n", hash);
     
      return EXIT_SUCCESS;
    }
    
    图片.png

    将pow和log函数用LIEF把当前的函数名和另一个函数名进行互换

    first - 导入文件和库

    import lief
     
    hasme = lief.parse("a.out")
    libm  = lief.parse("/libm.so.6")
    

    second - 改变binary中两个导入函数的函数名:

    hashme_pow_sym = next(filter(lambda e : e.name == "pow", my_binary.imported_symbols))
    hashme_log_sym = next(filter(lambda e : e.name == "log", my_binary.imported_symbols))
     
    hashme_pow_sym.name = "cos"
    hashme_log_sym.name = "sin"
    

    in the end
    把log替换为sin,把pow替换为cos,并重建这两个对象

    !/usr/bin/env python3

    import lief
     
     
    hasme = lief.parse("hasme")
    libm  = lief.parse("/usr/lib/libm.so.6")
     
     
    def swap(obj, a, b):
        symbol_a = next(filter(lambda e : e.name == a, obj.dynamic_symbols))
        symbol_b = next(filter(lambda e : e.name == b, obj.dynamic_symbols))
        b_name = symbol_b.name
        symbol_b.name = symbol_a.name
        symbol_a.name = b_name
     
    hashme_pow_sym = next(filter(lambda e : e.name == "pow", my_binary.imported_symbols))
    hashme_log_sym = next(filter(lambda e : e.name == "log", my_binary.imported_symbols))
     
    hashme_pow_sym.name = "cos"
    hashme_log_sym.name = "sin"
     
     
    swap(libm, "log", "sin")
    swap(libm, "pow", "cos")
     
    hashme.write("hashme.obf")
    libm.write("libm.so.6")
    

    我们在当前目录下构建了一个修改过后的libm,接下来需要在执行binary.obf的时候强制让Linux加载器加载我们修改过后的这个库。要实现这个功能,我们需要把LD_LIBRARY_PATH导出到当前目录:

    $ LD_LIBRARY_PATH=. hashme.obf 123
    228886645.836282
    

    修改过后的动态库中cos对应的地址是pow函数的地址,sin的对应地址是log函数的地址,而修改后的binary中调用的分别是cos和sin函数。调用的是cos函数,而实际执行的是pow函数的功能,这就会对分析人员造成困扰


    相关文章

      网友评论

          本文标题:patch elf文件 - 使用lief

          本文链接:https://www.haomeiwen.com/subject/djhbmqtx.html