本文为看雪论坛优秀文章
看雪论坛作者ID:八重嘤
一、工具介绍
二、TX SO加密介绍
tprt = lief.parse(name)
tptext_section = tprt.get_section(".tptext").content
print(len(tptext_section))
offset = tprt.get_section(".tptext").offset
out = b""
for i in range(len(tptext_section)):
out += (tptext_section[i] ^ 0xb8).to_bytes(1,byteorder='little')
print(len(out))
tpp = tprt_bin[:offset]+out+tprt_bin[offset+len(tptext_section):]
with open("tpp.so", 'wb') as fp:
fp.write(tpp)
所以总结一下:
三、代码实现
"""
添加init段,并且调用ADD_FUNC_NAME并传入一个参数(当前So的基址)
"""
INIT_PROC_SIZE = 0x2c
INIT_PROC_CONTENT = [0xC0,0x46,0xFF,0xB5,0x83,0xB0,0x6D,0x46,0x00,0xA3,0x14,0x3B,0x19,0x1C,0x1F,0x68,0xC9,0x1B,0x29,0x60,0x5E,0x68,0x03,0xD0,0x76,0x18,0x6E,0x60,0x28,0x68,0xB0,0x47,0x03,0xB0,0xFF,0xBD]# 腾讯代码里扣出来的嘿嘿
def add_init_proc():
binary = lief.parse(TARGET_BIN)
# 检测.dynamic节的空位是否足够,如果小于3个就要拓展dynamic节内容
free_dynamic_entry = 0
for entry in binary.dynamic_entries:
if entry.tag == lief.ELF.DYNAMIC_TAGS.NULL:
free_dynamic_entry += 1
print("free dynamic entry num:", free_dynamic_entry)
assert free_dynamic_entry > 3
#获取位于segment header 后的 offset,用于添加init_proc
init_proc_offset = binary.header.program_header_offset + \
binary.header.program_header_size * (binary.header.numberof_segments + 2 )
print("init_proc offset:", hex(init_proc_offset))
# 添加init入口
if not binary.has(ELF.DYNAMIC_TAGS.INIT):
# 先用0占位,直接写入偏移,lief工具会有点问题
# 如果不出问题 binary.add(ELF.DynamicEntry(ELF.DYNAMIC_TAGS.INIT,init_proc_offset + 8))
binary.add(ELF.DynamicEntry(ELF.DYNAMIC_TAGS.INIT,0))
else:
init_entry = binary.get(ELF.DYNAMIC_TAGS.INIT)
print("[x] binary has init_proc:", init_entry)
exit(1)
binary.write("libnative-lib.so")
# 手动修复 DynamicEntry 中的 value
outbin = lief.parse("libnative-lib.so")
out_dynamic = outbin.get_section(".dynamic")
ADD_FUNC = outbin.get_symbol(ADD_FUNC_NAME).value
num = 0
for entry in outbin.dynamic_entries:
if entry.tag == ELF.DYNAMIC_TAGS.INIT:
break
num+=1
init_entry_offset = out_dynamic.offset + (num * out_dynamic.entry_size)
print(hex(init_entry_offset))
patch_file("libnative-lib.so",init_entry_offset+4,struct.pack("<I", init_proc_offset + 8 + 1))
global INIT_PROC_CONTENT
#前四位 为 init——proc 的 偏移 ,后四位 为 要调用的 函数地址
print("init_proc_offset :",hex(init_proc_offset))
print("ADD_FUNC :", hex(ADD_FUNC))
INIT_PROC_CONTENTS = list(struct.pack("<I", init_proc_offset)) + list(struct.pack("<I", ADD_FUNC)) + INIT_PROC_CONTENT
patch_file("libnative-lib.so",init_proc_offset,INIT_PROC_CONTENTS)
https://bbs.pediy.com/thread-221821.htm
(2) rel.plt ->got -> text(导出)
//ELF.h 查看rel.plt的格式,r_offset 指向got表的地址,r_info高8位为类型,后24位为
// Relocation entry, without explicit addend.
struct Elf32_Rel {
Elf32_Addr r_offset; // Location (file byte offset, or program virtual addr)
Elf32_Word r_info; // Symbol table index and type of relocation to apply
// These accessors and mutators correspond to the ELF32_R_SYM, ELF32_R_TYPE,
// and ELF32_R_INFO macros defined in the ELF specification:
Elf32_Word getSymbol() const { return (r_info >> 8); }
unsigned char getType() const { return (unsigned char)(r_info & 0x0ff); }
void setSymbol(Elf32_Word s) { setSymbolAndType(s, getType()); }
void setType(unsigned char t) { setSymbolAndType(getSymbol(), t); }
void setSymbolAndType(Elf32_Word s, unsigned char t) {
r_info = (s << 8) + t;
}
};
// Symbol table entries for ELF32.
struct Elf32_Sym {
Elf32_Word st_name; // Symbol name (index into string table)
Elf32_Addr st_value; // Value or address associated with the symbol
Elf32_Word st_size; // Size of the symbol
unsigned char st_info; // Symbol's type and binding attributes
unsigned char st_other; // Must be zero; reserved
Elf32_Half st_shndx; // Which section (header table index) it's defined in
// These accessors and mutators correspond to the ELF32_ST_BIND,
// ELF32_ST_TYPE, and ELF32_ST_INFO macros defined in the ELF specification:
unsigned char getBinding() const { return st_info >> 4; }
unsigned char getType() const { return st_info & 0x0f; }
void setBinding(unsigned char b) { setBindingAndType(b, getType()); }
void setType(unsigned char t) { setBindingAndType(getBinding(), t); }
void setBindingAndType(unsigned char b, unsigned char t) {
st_info = (b << 4) + (t & 0x0f);
}
};
def add_symbol():
binary = lief.parse(TARGET_BIN)
add_bin = lief.parse("libadd.so")
add_got = add_bin.get_section(".got")
add_data = add_bin.get_section(".data")
add_plt = add_bin.get_section(".plt")
add_text = add_bin.get_section(".text")
before_add_load_num = 0
for i in binary.segments:
if i.type == ELF.SEGMENT_TYPES.LOAD:
before_add_load_num+=1
print(before_add_load_num)
"""
添加2个load段,用于将add中内容添加进target
"""
# 第一个可读可执行,用来映射新的.plt节 + .text节
add_RE_seg = lief.ELF.Segment()
add_RE_seg.alignment = 0x1000
add_RE_seg.type = ELF.SEGMENT_TYPES.LOAD
add_RE_seg.add(ELF.SEGMENT_FLAGS.X)
add_RE_seg.add(ELF.SEGMENT_FLAGS.R)
add_RE_seg.content = add_plt.content + add_text.content
print("add_RE_seg.content :", add_RE_seg.content)
binary.add(add_RE_seg)
#第二个load段 可读可写 添加 .got .data
add_RW_seg = lief.ELF.Segment()
add_RW_seg.alignment = 0x1000
add_RW_seg.type = ELF.SEGMENT_TYPES.LOAD
add_RW_seg.add(ELF.SEGMENT_FLAGS.W)
add_RW_seg.add(ELF.SEGMENT_FLAGS.R)
add_RW_seg.content = add_got.content + add_data.content
binary.add(add_RW_seg)
print(add_got.content)
addbin_relplt = add_bin.pltgot_relocations
"""
添加addbin中的relplt,并且向dynsym添加对应的symbol。
此操作改动了dynstr、dynsym、rel.plt。
本来针对原节拓展即可,但lief工具新增了3个load段进行加载新的内容,所以还修改了dynamic
"""
add_sym_value = list()
for add_entry in addbin_relplt:
if binary.has_symbol(add_entry.symbol.name):
sym = binary.get_symbol(add_entry.symbol.name)
else:
sym = binary.add_dynamic_symbol(add_entry.symbol)#后面修复
add_sym_value.append(add_entry.symbol.value - add_text.virtual_address)
print(hex(add_entry.symbol.value),hex(add_entry.symbol.value - add_text.virtual_address))
# 此处lief工具又有问题,写入后value变了,坑爹货
#工具不出问题,此处减掉add中text的虚拟地址,加上intermediate中的新增的text虚拟地址就行了
add_reloc = ELF.Relocation()
add_reloc.type = add_entry.type
add_reloc.symbol = sym
add_reloc.address = add_entry.address - add_got.virtual_address
add_reloc.purpose = ELF.RELOCATION_PURPOSES.PLTGOT
add_reloc = binary.add_pltgot_relocation(add_reloc)
# print("add_reloc - ", add_reloc)
binary.write("intermediate.so")
#辣鸡工具,会导致偏移出问题,所以不得不进行手工修复
inter = lief.parse("intermediate.so")
#获取前面添加的两个load段的虚拟地址
add_RE_seg_virtual_address = 0
add_RW_seg_virtual_address = 0
after_add_load_num = 0
for i in binary.segments:
if i.type == ELF.SEGMENT_TYPES.LOAD:
after_add_load_num += 1
if after_add_load_num > before_add_load_num and after_add_load_num <= before_add_load_num +2:
if i.has(ELF.SEGMENT_FLAGS.X):
add_RE_seg_virtual_address = i.virtual_address
if i.has(ELF.SEGMENT_FLAGS.W):
add_RW_seg_virtual_address = i.virtual_address
print(hex(add_RE_seg_virtual_address))
print(hex(add_RW_seg_virtual_address))
#修复dynsym中新增的symbol,将Elf32_Sym->st_value 指向text段即可,0不修改是指向import func
new_dynsym_content = inter.get_section(".dynsym").content
add_dynsym_start = len(new_dynsym_content) - len(add_sym_value)*16
print("add_dynsym_start:",add_dynsym_start)
modify_dynsym_content = []
inx = 0
for entry_content in [new_dynsym_content[i:i + 16] for i in range(add_dynsym_start, len(new_dynsym_content), 16)]:
entry = DynSymEntry.parse_from_content(entry_content)
if(entry.sym_value != 0):
print(hex(entry.sym_value))
entry.sym_value =add_sym_value[inx] +add_RE_seg_virtual_address +len(add_plt.content)
print(hex(entry.sym_value))
inx += 1
modify_dynsym_content += entry.content
patch_file("intermediate.so", inter.get_section(".dynsym").offset + add_dynsym_start, modify_dynsym_content)
#修复.rel.plt中新增的rel项,指向新增的第二个load段中加载的add.so中的got表
modify_rel_content = []
relplt = binary.get_section(".rel.plt")
add_rel_start = binary.get_section(".rel.plt").size - len(add_sym_value)*8
print("add_rel_start :", hex(add_rel_start))
add_entry_ndx = 0
for rel_content in [relplt.content[i:i + 8] for i in range(add_rel_start, len(relplt.content), 8)]:
rel = RelEntry.parse_from_content(rel_content)
if(rel.offset != 0):
print("offset :", hex(rel.offset))
rel.offset = rel.offset + add_RW_seg_virtual_address
print("offset :", hex(rel.offset))
modify_rel_content += rel.content
add_entry_ndx += 1
patch_file("intermediate.so", inter.get_section(".rel.plt").offset + add_rel_start, modify_rel_content)
# 修复plt表的相关代码
def get_offset(inaddr):
high = inaddr // 0x0100000
mid = (inaddr & 0xfffff)//0x1000
low = (inaddr & 0xfff)
return high,mid,low
#需要fix plt 表,调用外部函数时需要 通过plt表进行跳转
add_plt_size = add_plt.size
print("add_plt_size:",add_plt_size)
PLT_TABLE_HEAD_LEN = 0x14
need_fix_plt_content = add_RE_seg.content[PLT_TABLE_HEAD_LEN:add_plt_size]
print(need_fix_plt_content)
print(hex(add_RW_seg_virtual_address - add_RE_seg_virtual_address))
inx = 0
modify_plt_content = []
for plt_entry in [add_RE_seg.content[i:i+12] for i in range(PLT_TABLE_HEAD_LEN,add_plt_size,12)]:
#+8是因为ADR取地址是取PC的值
got2plt_offset = got_address_list[inx] - (inx*12+add_RE_seg_virtual_address+8 +PLT_TABLE_HEAD_LEN )
inx += 1
print("got -> plt offset :", hex(got2plt_offset))
print(plt_entry)
h,m,l = get_offset(got2plt_offset)
plt_entry[0] = h
plt_entry[4] = m
plt_entry[8] = l & 0xff
plt_entry[9] = (plt_entry[9]&0xf0) + (l >> 8)
print("fix entry",plt_entry,hex(plt_entry[9]))
modify_plt_content += plt_entry
patch_file("intermediate.so", add_RE_seg_offset + PLT_TABLE_HEAD_LEN, modify_plt_content)
看雪ID:八重嘤
https://bbs.pediy.com/user-home-833877.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!