将PIE可执行程序转换成动态链接库
2020-12-24 18:30:00 Author: mp.weixin.qq.com(查看原文) 阅读量:112 收藏

在《IDA flare-emu示例》中提过一句将普通可执行ELF转成.so文件,bluerust就此向我推荐开源项目:

https://github.com/lief-project/LIEF

其原话是,"高级、好用"。bluerust本职工作是说单口相声的,平时嘴贫得不行,还都是用英语砸我,难得用一次汉语,居然才四个字。这我一定得试试。

考虑这样一种场景,某ELF中有未导出函数对in实现某种数学运算产生out,暂时搞不清细节,想快速利用之。简单情形可以直接读到内存里当成shellcode那样的可移动代码用,复杂情形只好具体问题具体分析。

我最早干的是用dlopen()加载传统可执行程序,本文介绍的是将PIE转成动态链接库,二者有所区别。不过来都来了,就过一遍吧。

本文是LIEF官方文档节选某一小段后的中译版,没有原创内容。

Transforming an ELF executable into a library
https://lief.quarkslab.com/doc/latest/tutorials/08_elf_bin2lib.html

本文在x64/Ubuntu 16.04.6 LTS中测试。

安装LIEF:

python3 -m pip install --upgrade pip
pip3 install setuptools --upgrade
pip3 install lief

简单测试LIEF:

import lief

binary  = lief.parse( "/bin/ls" )
print( binary )

一切正常的话,会看到很大一片输出,都是ELF相关信息。


true、ssh都是可执行ELF,但二者的Type不同;true是传统可执行程序,ssh是所谓的位置无关可执行程序(PIE)。PIE与.so一样,其加载基址是浮动的。用gdb调试PIE时,若想断在"Entry point",参看《GDB启动被调试进程时如何尽早断下》,需要一些奇技淫巧。

vi lief_sample_0.py

# -*- encoding: cp936 -*-
#
# python3 lief_sample_0.py /usr/bin/ssh
# python3 lief_sample_0.py /lib/x86_64-linux-gnu/libc.so.6
#

import sys
import lief

filename    = sys.argv[1]
binary      = lief.parse( filename )
print( binary.header.file_type )
n           = len( binary.exported_functions )
print( n )
if ( n > 0 ) :
    print( binary.exported_functions[0] )

$ python3 lief_sample_0.py /usr/bin/ssh
E_TYPE.DYNAMIC
9
mkstemp - 0x67fe0

$ python3 lief_sample_0.py /lib/x86_64-linux-gnu/libc.so.6
E_TYPE.DYNAMIC
2062
putwchar - 0x710f0

PIE与.so的主要区别在于导出符号,ssh只有很少的导出符号,libc.so有很多导出符号。

vi lief_sample_1.c

#if 0

x64/Ubuntu 16.04.6 LTS + gcc 5.4.0

gcc -Wall -pipe -O3 -s -o lief_sample_1_a lief_sample_1.c
gcc -Wall -pipe -O3 -fvisibility=hidden -fPIE -pie -Wl,-strip-all,--hash-style=both -o lief_sample_1_b lief_sample_1.c

#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static __attribute__((noinline)) int check ( char *sth )
{
    if ( strcmp( sth, "magic" ) == 0 )
    {
        return1 );
    }
    return0 );
}

int main int argc, char * argv[] )
{
    if ( argc != 2 )
    {
        printf"Usage: %s <sth>\n", argv[0] );
        return-1 );
    }
    if ( check( argv[1] ) )
    {
        printf"Ok\n" );
    }
    else
    {
        printf"Try again\n" );
    }
    return0 );
}

本例刻意对比不同编译方式:


尽管lief_sample_1_b是PIE,但没有导出符号。
$ ./lief_sample_1_b magic
Ok

$ ./lief_sample_1_b other
Try again

用IDA反汇编lief_sample_1_b,check()符号被抹去,其相对加载基址的内存偏移是0x860,不要用文件偏移。

vi lief_sample_2.py

# -*- encoding: cp936 -*-
#
# python3 lief_sample_2.py <piefile> <funcoff> <funcname> <sofile>
# python3 lief_sample_2.py lief_sample_1_b 0x860 check lief_sample_1_b.so
#

import sys
import lief

piefile     = sys.argv[1]
funcoff     = int( sys.argv[2], 0 )
funcname    = sys.argv[3]
sofile      = sys.argv[4]
binary      = lief.parse( piefile )
binary.add_exported_function( funcoff, funcname )
try :
    #
    # glibc >= 2.29 deny calls to dlopen with PIE binaries. so we remove
    # DF_1_PIE flag.
    #
    binary[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove( lief.ELF.DYNAMIC_FLAGS_1.PIE )
except AttributeError :
    pass
binary.write( sofile )

用LIEF将PIE转成.so:

python3 lief_sample_2.py lief_sample_1_b 0x860 check lief_sample_1_b.so

意思是,PIE加载后偏移0x860处的代码为之起名check(),按此要求生成.so。

$ python3 lief_sample_0.py lief_sample_1_b.so
E_TYPE.DYNAMIC
1
check - 0x4860

对比lief_sample_1_b、lief_sample_1_b.so:

$ readelf --dyn-syms lief_sample_1_b

Symbol table '.dynsym' contains 13 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     00000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     10000000000000668     0 SECTION LOCAL  DEFAULT   12
...
    120000000000201040     0 NOTYPE  GLOBAL DEFAULT   27 __bss_start

$ readelf --dyn-syms lief_sample_1_b.so

Symbol table '.dynsym' contains 14 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     00000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     10000000000004668     0 SECTION LOCAL  DEFAULT   12
...
    120000000000205040     0 NOTYPE  GLOBAL DEFAULT   27 __bss_start
    130000000000004860 0x7bb22900 FUNC    GLOBAL DEFAULT   15 check

$ readelf -s lief_sample_1_b.so | grep "FUNC    GLOBAL"
     30000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     40000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     60000000000000000     0 FUNC    GLOBAL DEFAULT  UND __printf_chk@GLIBC_2.3.4 (3)
    130000000000004860 0x7bb22900 FUNC    GLOBAL DEFAULT   15 check

$ nm -D lief_sample_1_b.so | grep " T "
0000000000004860 T check

$ objdump -T lief_sample_1_b.so | grep "g    DF"
0000000000004860 g    DF .text  000000007bb22900              check

常规ELF工具已经看到lief_sample_1_b.so中的导出符号check,IDA反汇编更显眼。

lief_sample_1_b.so现在可以当成可执行程序用,也可当成动态链接库用。

$ chmod +x lief_sample_1_b.so

$ ./lief_sample_1_b.so magic
Ok

$ ./lief_sample_1_b.so other
Try again

下面写个程序dlopen()打开lief_sample_1_b.so,调用其中的check()。

vi lief_sample_3.c

#if 0

x64/Ubuntu 16.04.6 LTS + gcc 5.4.0

gcc -Wall -pipe -O3 -s -o lief_sample_3 lief_sample_3.c -ldl

#endif

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef int ( *some_t ) char * );

int main int argc, char * argv[] )
{
    char   *sofile,
           *funcname,
           *sth;
    void   *so;
    some_t  some;
    int     someret;

    if ( argc != 4 )
    {
        printf"Usage: %s <sofile> <funcname> <sth>\n", argv[0] );
        return-1 );
    }
    sofile      = argv[1];
    funcname    = argv[2];
    sth         = argv[3];
    so          = dlopen( sofile, RTLD_LAZY );
    if ( !so )
    {
        fprintfstderr"dlopen error: %s\n", dlerror() );
        return-1 );
    }
    some        = ( some_t )dlsym( so, funcname );
    someret     = some( sth );
    printf"%s(\"%s\")=%d\n", funcname, sth, someret );
    if ( someret )
    {
        printf"Ok\n" );
    }
    else
    {
        printf"Try again\n" );
    }
    return0 );
}

$ ./lief_sample_3 ./lief_sample_1_b.so check magic
check("magic")=1
Ok

$ ./lief_sample_3 ./lief_sample_1_b.so check other
check("other")=0
Try again

lief_sample_2.py负责PIE转.so。更多编程细节参看:

LIEF API
https://lief.quarkslab.com/doc/latest/api/index.html

ELF API
https://lief.quarkslab.com/doc/latest/api/python/elf.html


文章来源: http://mp.weixin.qq.com/s?__biz=MzUzMjQyMDE3Ng==&mid=2247484560&idx=1&sn=0644d39ff3a2ce050fc7cf35a4c758f7&chksm=fab2c7afcdc54eb917a14bf713c4a8d03695145a3edd289a5b735f6b9e736593056c82c6f8a4#rd
如有侵权请联系:admin#unsafe.sh