patch elf文件 - 使用lief

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函数的功能,这就会对分析人员造成困扰


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容