├── .gitignore ├── README.md ├── libcdb.py └── libcdblib ├── __init__.py ├── elf.py ├── getlibc ├── __init__.py └── get_ubuntu_libc.py └── manage.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | 96 | # myself 97 | test.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libcdb 2 | a program to build libc-database 3 | 4 | 5 | 6 | ### How to Use 7 | 8 | First, edit your libcdbroot in libcdb.py. 9 | 10 | #### Add: 11 | 12 | Usage: 13 | 14 | ```bash 15 | ./libcdb.py add libc 16 | ``` 17 | 18 | Add libc to libc-database. 19 | 20 | For example: 21 | 22 | ```bash 23 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py add ../libc.so.6 24 | Add amd64 aad7dbe330f23ea00ca63daf793b766b51aceb5d! 25 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py add ../libc.so.6 26 | This libc:aad7dbe330f23ea00ca63daf793b766b51aceb5d has in libc-database. 27 | ``` 28 | 29 | #### Delete: 30 | 31 | Usage: 32 | 33 | ```bash 34 | ./libcdb.py delete arch buildid 35 | ``` 36 | 37 | Delete libc from libc-database. 38 | 39 | For example: 40 | 41 | ```bash 42 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py delete amd64 aad7dbe330f23ea00ca63daf793b766b51aceb5d 43 | Delete success! 44 | ``` 45 | 46 | #### Search: 47 | 48 | Usage: 49 | 50 | ```bash 51 | ./libcdb.py search arch libcdata 52 | ``` 53 | 54 | Search libc from libc-database. 55 | 56 | For example: 57 | 58 | ```bash 59 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py search powerpc64 "{'system':348160, 'printf':408704}" 60 | ['1061ed71d47749f42fcaa0f2ac39c3eaa8fffa26'] 61 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py search powerpc64 "{'system':348160, 'printf':408704, 'puts':0}" 62 | [] 63 | ``` 64 | 65 | #### Update 66 | 67 | Usage: 68 | 69 | ```bash 70 | ./libcdb.py update 71 | ``` 72 | 73 | Libcdb will auto download all libc from ubuntu. 74 | 75 | For example: 76 | 77 | ```bash 78 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py update 79 | 2018-03-30 02:33:28 URL:http://mirrors.ustc.edu.cn/ubuntu/pool/main/e/eglibc/libc6-amd64_2.15-0ubuntu10.18_i386.deb [4608566/4608566] -> "/home/plusls/libcdbroot/download/libc6-amd64_2.15-0ubuntu10.18_i386.deb" [1] 80 | Add amd64 6a93e9450a6dba5b0d7ef2769c86430fcf231184! 81 | 2018-03-30 02:33:34 URL:http://mirrors.ustc.edu.cn/ubuntu/pool/main/e/eglibc/libc6-amd64_2.15-0ubuntu10_i386.deb [4469586/4469586] -> "/home/plusls/libcdbroot/download/libc6-amd64_2.15-0ubuntu10_i386.deb" [1] 82 | Add amd64 ea980a054cfa815c1155319d035c197d282d785d! 83 | ...... 84 | Update Ubuntu libc success! 85 | plusls@plusls-virtual-machine:~/Desktop/libcdb$ ./libcdb.py update 86 | Update Ubuntu libc success! 87 | ``` 88 | 89 | 90 | 91 | ### To do 92 | 93 | Auto get libc from ~~Ubuntu~~, Debian, CentOS. 94 | 95 | Write web UI. 96 | 97 | -------------------------------------------------------------------------------- /libcdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- # 3 | 4 | import sys 5 | 6 | from libcdblib.manage import add 7 | from libcdblib.manage import delete 8 | from libcdblib.manage import search 9 | from libcdblib.getlibc.get_ubuntu_libc import get_all_ubuntu_libc 10 | 11 | 12 | LIBCDBROOT = '/home/plusls/libcdbroot' 13 | 14 | def update(): 15 | get_all_ubuntu_libc('/home/plusls/libcdbroot') 16 | 17 | 18 | def main(): 19 | argv = sys.argv[1:] 20 | if argv[0] == 'add': 21 | for i in range(1, len(argv)): 22 | add(LIBCDBROOT, argv[i]) 23 | elif argv[0] == 'delete': 24 | for i in range(2, len(argv)): 25 | delete(LIBCDBROOT, argv[1], argv[i]) 26 | print('Delete success!') 27 | elif argv[0] == 'search': 28 | libc_data = eval(argv[2]) 29 | print(search(LIBCDBROOT, argv[1], libc_data)) 30 | elif argv[0] == 'update': 31 | update() 32 | 33 | if __name__ == '__main__': 34 | main() -------------------------------------------------------------------------------- /libcdblib/__init__.py: -------------------------------------------------------------------------------- 1 | """get libc from network""" 2 | 3 | #from .get_ubuntu_libc import GetUbuntuLibc 4 | 5 | #__version__ = "1.1.2.4" 6 | #__author__ = "plusls" 7 | #__all__ = ['GetUbuntuLibc'] 8 | -------------------------------------------------------------------------------- /libcdblib/elf.py: -------------------------------------------------------------------------------- 1 | ''' solve elf file''' 2 | import mmap 3 | import os 4 | 5 | from elftools.elf.elffile import ELFFile 6 | from elftools.elf.sections import SymbolTableSection 7 | 8 | class ELF(ELFFile): 9 | '''Encapsulates information about an ELF file.''' 10 | def __init__(self, path): 11 | # elftools uses the backing file for all reads and writes 12 | # in order to permit writing without being able to write to disk, 13 | # mmap() the file. 14 | 15 | #: :class:`file`: Open handle to the ELF file on disk 16 | self.file = open(path,'rb') 17 | 18 | #: :class:`mmap.mmap`: Memory-mapped copy of the ELF file on disk 19 | self.mmap = mmap.mmap(self.file.fileno(), 0, access=mmap.ACCESS_COPY) 20 | 21 | super(ELF,self).__init__(self.mmap) 22 | 23 | #: :class:`str`: Path to the file 24 | self.path = os.path.abspath(path) 25 | 26 | #: :class:`dotdict` of ``name`` to ``address`` for all symbols in the ELF 27 | self.symbols = {} 28 | 29 | self.buildid = self._get_buildid() 30 | 31 | self.arch = self._get_machine_arch() 32 | 33 | self._populate_symbols() 34 | 35 | @property 36 | def sections(self): 37 | """ 38 | :class:`list`: A list of :class:`elftools.elf.sections.Section` objects 39 | for the segments in the ELF. 40 | """ 41 | return list(self.iter_sections()) 42 | 43 | def _get_buildid(self): 44 | """:class:`str`: GNU Build ID embedded into the binary""" 45 | section = self.get_section_by_name('.note.gnu.build-id') 46 | if section: 47 | raw_build_id = section.data()[16:] 48 | buildid = '' 49 | for ch in raw_build_id: 50 | buildid += '%02x' % ch 51 | return buildid 52 | return '' 53 | 54 | def _populate_symbols(self): 55 | """ 56 | >>> bash = ELF(which('bash')) 57 | >>> bash.symbols['_start'] == bash.entry 58 | True 59 | """ 60 | 61 | # Populate all of the "normal" symbols from the symbol tables 62 | for section in self.sections: 63 | if not isinstance(section, SymbolTableSection): 64 | continue 65 | 66 | for symbol in section.iter_symbols(): 67 | value = symbol.entry.st_value 68 | if not value or not symbol.name: 69 | continue 70 | self.symbols[symbol.name] = value 71 | 72 | def _get_machine_arch(self): 73 | return { 74 | 'EM_X86_64': 'amd64', 75 | 'EM_386' :'i386', 76 | 'EM_486': 'i386', 77 | 'EM_ARM': 'arm', 78 | 'EM_AARCH64': 'aarch64', 79 | 'EM_MIPS': 'mips', 80 | 'EM_PPC': 'powerpc', 81 | 'EM_PPC64': 'powerpc64', 82 | 'EM_SPARC32PLUS': 'sparc', 83 | 'EM_SPARCV9': 'sparc64', 84 | 'EM_IA_64': 'ia64' 85 | }.get(self['e_machine'], self['e_machine']) -------------------------------------------------------------------------------- /libcdblib/getlibc/__init__.py: -------------------------------------------------------------------------------- 1 | """get libc from network""" 2 | 3 | 4 | #__version__ = "1.1.2.4" 5 | #__author__ = "plusls" 6 | #__all__ = ['GetUbuntuLibc'] 7 | -------------------------------------------------------------------------------- /libcdblib/getlibc/get_ubuntu_libc.py: -------------------------------------------------------------------------------- 1 | ''' get ubuntu libc ''' 2 | 3 | import requests 4 | import os 5 | import re 6 | import json 7 | import shutil 8 | import subprocess 9 | from libcdblib.manage import add 10 | from libcdblib.elf import ELF 11 | 12 | 13 | def get_all_ubuntu_libc(libcdb_root, source_url='http://mirrors.ustc.edu.cn/'): 14 | ''' get all ubuntu libc 15 | ''' 16 | 17 | 18 | url1 = source_url + 'ubuntu/pool/main/e/eglibc/' 19 | deb_list1 = re.findall(r'>(libc6.+?\.deb)<', requests.get(url1).content.decode()) 20 | url2 = source_url + 'ubuntu-ports/pool/main/e/eglibc/' 21 | deb_list2 = re.findall(r'>(libc6.+?\.deb)<', requests.get(url2).content.decode()) 22 | url3 = source_url + 'ubuntu/pool/main/g/glibc/' 23 | deb_list3 = re.findall(r'>(libc6.+?\.deb)<', requests.get(url3).content.decode()) 24 | url4 = source_url + 'ubuntu-ports/pool/main/g/glibc/' 25 | deb_list4 = re.findall(r'>(libc6.+?\.deb)<', requests.get(url4).content.decode()) 26 | download_path = libcdb_root + '/download' 27 | 28 | _mkdir(libcdb_root) 29 | _mkdir(download_path) 30 | arch_list = os.listdir(libcdb_root) 31 | buildid_dict = dict() 32 | arch_dict = dict() 33 | for arch in arch_list: 34 | # 不是文件夹就继续 35 | if os.path.isdir('%s/%s' % (libcdb_root, arch)) is False: 36 | continue 37 | try: 38 | fp = open('%s/%s/build-id.json' % (libcdb_root, arch), 'r') 39 | buidid_json_str = fp.read() 40 | fp.close() 41 | arch_dict[arch] = json.loads(buidid_json_str) 42 | buildid_dict.update(arch_dict[arch]) 43 | except FileNotFoundError: 44 | pass 45 | try: 46 | fp = open('%s/void-pkg.json' % (libcdb_root, ), 'r') 47 | buidid_json_str = fp.read() 48 | fp.close() 49 | arch_dict['void-pkg'] = json.loads(buidid_json_str) 50 | buildid_dict.update(arch_dict['void-pkg']) 51 | except FileNotFoundError: 52 | pass 53 | 54 | _download_deb(libcdb_root, url1, deb_list1, buildid_dict, arch_dict) 55 | _download_deb(libcdb_root, url2, deb_list2, buildid_dict, arch_dict) 56 | _download_deb(libcdb_root, url3, deb_list3, buildid_dict, arch_dict) 57 | _download_deb(libcdb_root, url4, deb_list4, buildid_dict, arch_dict) 58 | print('Update Ubuntu libc success!') 59 | def _download_deb(libcdb_root, url, deb_list, buidid_dict, arch_dict): 60 | download_path = libcdb_root + '/download' 61 | # print(deb_list) 62 | for deb in deb_list: 63 | if deb not in buidid_dict: 64 | if os.system('wget -nvc %s%s -O %s/%s' % (url, deb, download_path, deb)) != 0: 65 | print('%s download faild!' % deb) 66 | continue 67 | _mkdir('%s/%s' % (download_path, deb[:-4])) 68 | if os.system('cd %s/%s && ar -x ../%s -o data.tar.gz 2>/dev/null && tar -xf data.tar.gz 2>/dev/null' % (download_path, deb[:-4], deb)) != 0: 69 | if os.system('cd %s/%s && ar -x ../%s -o data.tar.xz && tar -xf data.tar.xz' % (download_path, deb[:-4], deb)) != 0: 70 | print('%s unpack faild!' % deb) 71 | continue 72 | libc_path_list = subprocess.check_output(['find', '%s/%s' % (download_path, deb[:-4]), '-name', 'libc-*.so']).decode().split('\n') 73 | 74 | # 记录空包 75 | if len(libc_path_list) == 1: 76 | if 'void-pkg' not in arch_dict: 77 | arch_dict['void-pkg'] = dict() 78 | arch_dict['void-pkg'][deb] = [] 79 | fp = open('%s/void-pkg.json' % (libcdb_root, ), 'w') 80 | fp.write(json.dumps(arch_dict['void-pkg'])) 81 | fp.close() 82 | 83 | # 循环添加libc 84 | for libc_path in libc_path_list: 85 | if libc_path == '': 86 | continue 87 | add(libcdb_root, libc_path) 88 | libc = ELF(libc_path) 89 | if libc.arch not in arch_dict: 90 | arch_dict[libc.arch] = dict() 91 | if deb not in arch_dict[ELF(libc_path).arch]: 92 | arch_dict[libc.arch][deb] = [] 93 | arch_dict[libc.arch][deb].append(libc.buildid) 94 | fp = open('%s/%s/build-id.json' % (libcdb_root, libc.arch), 'w') 95 | fp.write(json.dumps(arch_dict[libc.arch])) 96 | fp.close() 97 | # 清理垃圾 98 | shutil.rmtree('%s/%s' % (download_path, deb[:-4])) 99 | 100 | def _mkdir(path): 101 | ''' mkdir ''' 102 | try: 103 | os.mkdir(path) 104 | except FileExistsError as e: 105 | if os.path.isdir(e.filename) is False: 106 | raise e 107 | -------------------------------------------------------------------------------- /libcdblib/manage.py: -------------------------------------------------------------------------------- 1 | ''' manage libc database ''' 2 | import os 3 | import shutil 4 | import copy 5 | import json 6 | from libcdblib.elf import ELF 7 | 8 | def add(libcdb_root, libc_file): 9 | '''add libc 10 | libcdb_root: libcdb的位置 11 | libc_file libc的位置''' 12 | libcdb_root = os.path.abspath(libcdb_root) 13 | libc_file = os.path.abspath(libc_file) 14 | libc = ELF(libc_file) 15 | 16 | # init libcdb 17 | _mkdir(libcdb_root) 18 | _mkdir('%s/%s' % (libcdb_root, libc.arch)) 19 | _mkdir('%s/%s/libcdb' % (libcdb_root, libc.arch)) 20 | _mkdir('%s/%s/libcdb/%s' % (libcdb_root, libc.arch, libc.buildid[:2])) 21 | if os.path.exists('%s/%s/libcdb/%s/%s.so' % (libcdb_root, libc.arch, libc.buildid[:2], libc.buildid[2:])) is True: 22 | print('This libc:%s has in libc-database.' % libc.buildid) 23 | return 24 | shutil.copyfile(libc_file, '%s/%s/libcdb/%s/%s.so' % (libcdb_root, libc.arch, libc.buildid[:2], libc.buildid[2:])) 25 | 26 | _add_fun_offset_data(libcdb_root, libc) 27 | print('Add %s %s!' % (libc.arch, libc.buildid)) 28 | 29 | 30 | def search(libcdb_root, arch, libc_data): 31 | '''search libc in database 32 | libcdb_root: libcdb root 33 | libc_data: libcdata 34 | return the list of build-id 35 | for example: 36 | search(libcdb_root, {'system', 0xaabb, 'printf', '0xccdd'}) 37 | ''' 38 | addr_list = [] 39 | for fun, addr in libc_data.items(): 40 | addr_list.append([fun, addr]) 41 | addr_list.sort(key=lambda addr_data: addr_data[1]) 42 | (result, base_addr_list) = _search_by_12bit(libcdb_root, arch, addr_list[0][0], addr_list[0][1] % 0x1000) 43 | #print(result) 44 | if not result: 45 | return [] 46 | base_result = copy.deepcopy(result) 47 | result = set() 48 | for base_addr in base_addr_list: 49 | tmp_result = copy.deepcopy(base_result) 50 | for i in range(1, len(addr_list)): 51 | tmp_result &= _search_addr(libcdb_root, arch, addr_list[i][0], addr_list[i][1] - addr_list[0][1] + base_addr) 52 | #print('result=', result) 53 | #print(tmp_result, addr_list[i][0]) 54 | result |= tmp_result 55 | 56 | return list(result) 57 | 58 | 59 | def delete(libcdb_root, arch, buildid): 60 | '''delete libc''' 61 | libcdb_root = os.path.abspath(libcdb_root) 62 | libc_file = '%s/%s/libcdb/%s/%s.so' % (libcdb_root, arch, buildid[:2], buildid[2:]) 63 | libc = ELF(libc_file) 64 | _delete_fun_dir(libcdb_root, libc) 65 | os.remove(libc_file) 66 | if not os.listdir('%s/%s/libcdb/%s' % (libcdb_root, arch, buildid[:2])): 67 | os.rmdir('%s/%s/libcdb/%s' % (libcdb_root, arch, buildid[:2])) 68 | if not os.listdir('%s/%s/libcdb' % (libcdb_root, arch)): 69 | os.rmdir('%s/%s/libcdb' % (libcdb_root, arch)) 70 | if not os.listdir('%s/%s' % (libcdb_root, arch)): 71 | os.rmdir('%s/%s' % (libcdb_root, arch)) 72 | if not os.listdir(libcdb_root): 73 | os.rmdir(libcdb_root) 74 | 75 | 76 | def _search_addr(libcdb_root, arch, fun, addr): 77 | ''' search addr 78 | ''' 79 | result = set() 80 | dst_path = '%s/%s/fun/%s.json' % (libcdb_root, arch, fun) 81 | 82 | if os.path.exists(dst_path) is False: 83 | return result 84 | fp = open(dst_path, 'r') 85 | offset_data = json.loads(fp.read()) 86 | fp.close() 87 | for fun_addr in offset_data.keys(): 88 | if int(fun_addr, 16) == addr: 89 | for buildid in offset_data[fun_addr]: 90 | result.add(buildid) 91 | return result 92 | 93 | def _search_by_12bit(libcdb_root, arch, fun, addr): 94 | ''' search by 12bit 95 | ''' 96 | result = [set(), []] 97 | dst_path = '%s/%s/fun/%s.json' % (libcdb_root, arch, fun) 98 | 99 | if os.path.exists(dst_path) is False: 100 | return result 101 | fp = open(dst_path, 'r') 102 | offset_data = json.loads(fp.read()) 103 | fp.close() 104 | 105 | for fun_addr in offset_data.keys(): 106 | if int(fun_addr, 16) % 0x1000 == addr: 107 | result[1].append(int(fun_addr, 16)) 108 | for buildid in offset_data[fun_addr]: 109 | result[0].add(buildid) 110 | return result 111 | 112 | def _delete_fun_dir(libcdb_root, libc): 113 | ''' delete fun offset data 114 | ''' 115 | for fun, addr in libc.symbols.items(): 116 | addr_hex = '%x' % addr 117 | fp = open('%s/%s/fun/%s.json' % (libcdb_root, libc.arch, fun), 'r') 118 | offset_data = json.loads(fp.read()) 119 | fp.close() 120 | offset_data[addr_hex].remove(libc.buildid) 121 | if not offset_data[addr_hex]: 122 | del offset_data[addr_hex] 123 | if offset_data: 124 | fp = open('%s/%s/fun/%s.json' % (libcdb_root, libc.arch, fun), 'w') 125 | fp.write(json.dumps(offset_data)) 126 | fp.close() 127 | else: 128 | os.remove('%s/%s/fun/%s.json' % (libcdb_root, libc.arch, fun)) 129 | if not os.listdir('%s/%s/fun' % (libcdb_root, libc.arch)): 130 | os.rmdir('%s/%s/fun' % (libcdb_root, libc.arch)) 131 | 132 | 133 | 134 | def _add_fun_offset_data(libcdb_root, libc): 135 | ''' add fun offset data 136 | ''' 137 | _mkdir('%s/%s/fun' % (libcdb_root, libc.arch)) 138 | 139 | for fun, addr in libc.symbols.items(): 140 | try: 141 | fp = open('%s/%s/fun/%s.json' % (libcdb_root, libc.arch, fun), 'r') 142 | offset_data = json.loads(fp.read()) 143 | fp.close() 144 | except FileNotFoundError: 145 | offset_data = dict() 146 | addr_hex = '%x' % addr 147 | if addr_hex not in offset_data: 148 | offset_data[addr_hex] = [] 149 | 150 | # 去重 151 | if libc.buildid not in offset_data[addr_hex]: 152 | offset_data[addr_hex].append(libc.buildid) 153 | fp = open('%s/%s/fun/%s.json' % (libcdb_root, libc.arch, fun), 'w') 154 | fp.write(json.dumps(offset_data)) 155 | fp.close() 156 | 157 | def _mkdir(path): 158 | ''' mkdir ''' 159 | try: 160 | os.mkdir(path) 161 | except FileExistsError as e: 162 | if os.path.isdir(e.filename) is False: 163 | raise e 164 | --------------------------------------------------------------------------------