一、固件分析
我们再解压固件的时候,会出现一种情况,就是没有文件系统,这种情况可能是VXworks,我们通过找寻bzero可以知道可能式VXworks,我们发现没有标准的文件系统
看到这样的固件我们应该如何分析呢?
第一步我们需要先进行binwalk 查看,手机可用信息,我们看到这里有一个entry point (入口地址),继续观察一下是什么架构的,以及哪个文件数据最多,我们先着重分析它,我们找到是arm架构的
我们看到这个里面有相关的特征,bzero是最主要的特征
,
架构的确定方式
在这之前可以使用hexdump 查看一下机器码,通过分析机器码识别可以知道架构类型。另一种方法通过找特征值在ARM32(Cortex-A7)架构的固件,其开头几乎都是异常向量表。第一条指令通常是B Reset(跳转到复位处理程序),后面经常出现LDR R0、MOV SP等设置栈指针和加载初始值的指令。通过识别这些特征,可以初步判断固件为 ARM32 架构。
接着我们将38200拖入ida中,但因为ida分析不出来,我们自己选择框架去查看,在processer type 选择框架
在分析之前,我们需要先将文件的基地址找到,才能进行下一步操作,注意在这里需要将loading size标记为0x0
进入ida观察R0地址这就是对应的基地址
知道基地址之后,退出ida删除残留数据,重新进入,将入口地址修改成刚刚的入口地址(这个是38200对应的地址)
之后选择no,之后都选择ok,注意这个架构是32位的
我们发现这里有函数,但是函数名是什么我们都不知道,
符号表修复思路
这个时候我们使用的方法是什么,先在二进制文件中找寻DCB这个汇编命令,我们看到这里会有字符串,这些字符串我们发现都是一些函数名称,我们可以思考一下,如果我们可以将字符串和对应的偏移量结合起来,那我们就可以将未命名的函数名对应到相应的二字符串命中,但是后来发现有一些问题,从这里可以展开一点思路。
工具源码解析
这里我们可以先分析一下对应的VXworks工具源码
*# coding=utf-8 import logging import struct import idc import ida_nalt 这一长串引用是因为之前的python2脚本必须申明utf-8,第二个就是脚本需要调用ida进行符号表修复 import ida_bytes import ida_funcs import ida_kernwin import idaapi import time
default_check_count = 100 声明检查次数
known_address = [0x80002000, 0x10000, 0x1000, 0xf2003fe4, 0x100000, 0x107fe0]
function_name_key_words = [b'bzero', b'usrInit', b'bfill'] 检索的字母
bzero是VXworks的清零函数一般来说每一个vxworks都会存在这个字符串,属于特征
VXworks启动
VxWorks 的启动和 Linux 完全不一样。
linux的上电过程是先进入bootloader,然后启动uboot,加载内核,通过bootm执行启动程序,进入init,到系统shell,加载各种服务
而VXworks采用更加安全的办法,先启动bootROM只读程序,在进入rominit之后进入usrinit,最后使用kernelint启动,进入到usrroot中进行各种用户启动,所以你看到usrInit,基本就知道这是 VxWorks,这是vxworks特有的方式。
bfill是vxworks早期函数作用是用来填充数据,相当于memset
VxWorks 5.5
vx_5_sym_types = [
0x00, # Undefined Symbol
0x01, # Global (external)
0x02, # Local Absolute
0x03, # Global Absolute 某个固定地址
0x04, # Local .text
0x05, # Global .text
0x06, # Local Data
0x07, # Global Data
0x08, # Local BSS
0x09, # Global BSS
0x12, # Local Common symbol
0x13, # Global Common symbol
0x40, # Local Symbols related to a PowerPC SDA section
0x41, # Global Symbols related to a PowerPC SDA section
0x80, # Local symbols related to a PowerPC SDA2 section
0x81, # Local symbols related to a PowerPC SDA2
0x09, # Global Data
0x10, # Local BSS
0x11, # Global BSS
0x20, # Local Common symbol
0x21, # Global Common symbol
0x40, # Local Symbols
0x41, # Global Symbols
need_create_function = [0x04, 0x05]
class VxTarget(object): definit(self, firmware, vx_version=5, is_big_endian=False, logger=None): """ :param firmware: data of firmware :param vx_version: 5 = VxWorks 5.x; 6= V section ]
VxWorks 6.8
vx_6_sym_types = [ ###### 0x00, # Undefined Symbol ###### 0x01, # Global (external) ###### 0x02, # Local Absolute 0x03, # Global Absolute 0x04, # Local .text 0x05, # Global .text 0x08, # Local DataxWorks 6.x
VXworks版本之间的不同与判断思路来源
在研究VXworks时,或者任何一个东西时,我们需要保持一个良好的习惯,就是去官方找到对他的定义,只有我们对他足够了解才可以去真正识别它
版本5.5
typedef struct symbol { SL_NODE nameHNode; // 链表节点(4字节) char * name; // 符号名指针(4字节) char * value; // 符号值/地址(4字节) UINT16 group; // 分组(2字节) SYM_TYPE type; // 类型(1字节) UINT8 pad; // 填充(1字节) } SYMBOL;
判断框架
:param is_big_endian: True = big endian; False = little endian :param logger: logger for the target (default: None) """ self.is_big_endian = is_big_endian self.vx_version = vx_version self.symbol_table_start = None self.symbol_table_end = None self.string_table = [] self.symbol_table = [] self.symbols = [] self.load_address = None self.firmware = firmware self.has_symbol = None if self.vx_version == 5: self.symbol_interval = 16 判断版本类型 elif self.vx_version == 6: self.symbol_interval = 20 self.start_time = None self.performance_status = []
检查是否有日志系统,使用状态缓存将重要信息储存起来以便之后随时取用 if logger is None: self.logger = logging.getLogger(__name__) self.logger.setLevel(logging.INFO) console_handler = logging.StreamHandler() console_format = logging.Formatter('[%(levelname)-8s][%(module)s.%(funcName)s] %(message)s') console_handler.setFormatter(console_format) self.logger.addHandler(console_handler) else: self.logger = logger self._firmware_info = { "load_address": -1, "has_symbol": False, "vx_version": self._vx_version, "big_endian": self.is_big_endian, "symbol_table_start": None, "symbol_table_end": None, "symbols": None } self.prepare() def reset_timer(self): self.start_time = time.time() def start_timer(self): if self.start_time: return False else: self.start_time = time.time() return self.start_time def get_timer(self): if self.start_time: return time.time() - self.start_time return False def prepare(self): """ Trying to find symbol from image. :return: True if found symbol, False otherwise. """ self.find_symbol_table() if self._has_symbol is False: return None self.logger.debug("has_symbol: {}".format(self._has_symbol)) self.get_symbol_table() 如何进行框架识别,查看变化值,如果是大端前两个字节会变化,反之小端 def _check_vxworks_endian(self): """ Get image endian from image file. :return: """ data1 = self._firmware[self.symbol_table_start + 4:self.symbol_table_start + 4 + self._symbol_interval] data2 = self._firmware[self.symbol_table_start + 4 + self._symbol_interval:self.symbol_table_start + 4 + self._symbol_interval * 2] if data1[0:2] == data2[0:2]: self.logger.info("VxWorks endian: Big endian.") self.is_big_endian = True elif data1[2:4] == data2[2:4]: self.logger.info("VxWorks endian: Little endian.") self.is_big_endian = False else: self.logger.info("VxWorks endian unknown. Assuming little endian.") self.is_big_endian = False 进行符号表校验,保证连续一百个都是符合VXworks格式的符号表 def _check_symbol_format(self, offset): """ Check offset is symbol table. :param offset: offset from image. :return: True if offset is symbol table, False otherwise. """ start_offset = offset end_offset = offset + self._symbol_interval * default_check_count if end_offset > len(self._firmware): return False check_data = self._firmware[start_offset:end_offset] is_big_endian = True is_little_endian = True # check symbol data match struct for i in range(default_check_count): check_data_1 = check_data[i * self._symbol_interval:(i + 1) * self._symbol_interval] if len(check_data_1) < self._symbol_interval: self.logger.debug("check_data_1 length is too small: {}".format(len(check_data_1))) break if self._check_symbol_format_simple(check_data_1) is False: return False if self._vx_version == 5: self.logger.debug("Check VxWorks 5 symbol format") # check is big endian for i in range(9): check_data_1 = check_data[4 + i * self._symbol_interval:6 + i * self._symbol_interval] data2 = check_data[4 + (i + 1) * self._symbol_interval:6 + (i + 1) * self._symbol_interval] if check_data_1 != data2: self.logger.debug("VxWorks binary is not big endian.") is_big_endian = False break # check is little endian for i in range(9): check_data_1 = check_data[6 + i * self._symbol_interval:8 + i * self._symbol_interval] data2 = check_data[6 + (i + 1) * self._symbol_interval:8 + (i + 1) * self._symbol_interval] if check_data_1 != data2: self.logger.debug("VxWorks binary is not little endian.") is_little_endian = False break if is_big_endian and is_little_endian: return False return is_big_endian ^ is_little_endian return True 检查方式找到一个合法数值最少的进行判断,提高速度和准确率 def _check_symbol_format_simple(self, data): """ Check single symbol format is correct. :param data: single symbol data. :return: True if data is symbol, False otherwise. """ if self._vx_version == 5: # Check symbol type is valid sym_type = data[14] if sym_type not in vx_5_sym_types: return False # symbol should end with '\x00' if data[15] != 0: return False官方规定第十五个值为00 # Check symbol group is '\x00\x00' if data[12:14] != b'\x00\x00': return False # symbol_name point should not be zero if data[4:8] == b'\x00\x00\x00\x00': return False # symbol value point should not be zero if data[8:12] == b'\x00\x00\x00\x00': return False return True elif self._vx_version == 6: # Check symbol type is valid sym_type = data[18] if sym_type not in vx_6_sym_types: return False # symbol should end with '\x00' if data[19] != 0: return False # Check symbol group is '\x00\x00' if data[16:18] != b'\x00\x00': return False # symbol_name point should not be zero if data[4:8] == b'\x00\x00\x00\x00': return False # TODO: Need handle this problem # sometime data[8:12] will be '\x00\x00\x00\x00' # if data[8:12] == '\x00\x00\x00\x00': # return False return True return False 找到符号表最开始的地址 def find_symbol_table(self): """ Find symbol table from image. :return: """ self.reset_timer() for offset in range(len(self._firmware) - self._symbol_interval): if self.symbol_table_start is None: # Get first data valid the symbol_format if not self._check_symbol_format_simple(self._firmware[offset: offset + self._symbol_interval]): continue elif self._check_symbol_format(offset): self.logger.info("symbol table start offset: {:010x}".format(offset)) self.symbol_table_start = offset self._has_symbol = True self._firmware_info["has_symbol"] = True break else: break performance_data = "Find symbol table takes {:.3f} seconds".format(self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data)找到之后保存开始地址 if self.symbol_table_start: self.reset_timer() for i in range(self.symbol_table_start, len(self._firmware), self._symbol_interval): check_data = self._firmware[i:i + self._symbol_interval] if len(check_data) < self._symbol_interval: self.logger.debug("check_data length is too small: {}".format(check_data)) break if self._check_symbol_format_simple(check_data): self.symbol_table_end = i + self._symbol_interval self.logger.debug("self.symbol_table_end: {:010x}".format(self.symbol_table_end)) else: self.logger.info("Symbol table end offset: {:010x}".format(self.symbol_table_end)) break else: self.logger.error("Didn't find symbol table in this image") self._has_symbol = False performance_data = "Analyzer symbol table takes {:.3f} seconds".format(self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data) #获取函数名地址(因为我们知道在每个vxworks的4-7字节是函数的地址) def get_symbol_table(self): """ get symbol table data. :return: True if get symbol table data successful, False otherwise. """ if self.symbol_table_start and self.symbol_table_end: self._check_vxworks_endian() else: return False for i in range(self.symbol_table_start, self.symbol_table_end, self._symbol_interval): symbol_name_addr = self._firmware[i + 4:i + 8] symbol_dest_addr = self._firmware[i + 8:i + 12] symbol_flag = self._firmware[i + self._symbol_interval - 2] if self.is_big_endian: unpack_format = '>I' else: unpack_format = '<I' symbol_name_addr = int(struct.unpack(unpack_format, symbol_name_addr)[0]) symbol_dest_addr = int(struct.unpack(unpack_format, symbol_dest_addr)[0]) self.logger.debug("symbol_name_addr: {}; symbol_dest_addr: {}".format(symbol_name_addr, symbol_dest_addr)) self._symbol_table.append({'symbol_name_addr': symbol_name_addr, 'symbol_name_length': None, 'symbol_dest_addr': symbol_dest_addr, 'symbol_flag': symbol_flag, 'offset': i}) #保存元数据并进行排序 self.logger.debug("len(self._symbol_table): %s".format(len(self._symbol_table))) self._symbol_table = sorted(self._symbol_table, key=lambda x: x['symbol_name_addr']) for i in range(len(self._symbol_table) - 1): self._symbol_table[i]['symbol_name_length'] = self._symbol_table[i + 1]['symbol_name_addr'] - \ self._symbol_table[i]['symbol_name_addr'] self.logger.debug("len(self._symbol_table): {}".format(len(self._symbol_table))) return True #使用地址差来计算长度 @staticmethod def _is_printable(c): """ Check Char is printable. :param c: char to check. :return: True if char is printable, False otherwise. """ if type(c) is int: return 32 <= c <= 126 elif type(c) is str: return 32 <= ord(c) <= 126 def _is_func_name(self, string): """ Check target string is match function name format. :param string: string to check. :return: True if string is match function name format, False otherwise. """ # bad_str = [b'\\', b'%', b'+', b',', b'&', b'/', b')', b'(', b'[', b']'] # function name length should less than 512 byte if len(string) > 512: return False for data in bad_str: if data in string: return False for c in string: if self._is_printable(c) is False: return False return True #在string tables中寻找左右他的完整字符串 def _get_prev_string_data(self, offset): """ Get previous string from giving offset. :param offset: offset of image. :return: string data, string start offset, string end offset. """ while offset > 0: if self._firmware[offset] != 0: start_address = offset end_address = offset + 1 while offset > 0: if self._firmware[offset - 1] == 0: start_address = offset break offset -= 1 data = self._firmware[start_address:end_address] self.logger.debug("data: {}; start_address: {:010x}; end_address: {:010x}".format(data, start_address, end_address)) return data, start_address, end_address else: offset -= 1 self.logger.debug("Done looking for previous string data.") return None, None, None def _get_next_string_data(self, offset): """ Get next string from giving offset. :param offset: offset of image. :return: string data, string start offset, string end offset. """ while offset < len(self._firmware): if self._firmware[offset] != 0: start_address = offset end_address = offset while offset <= len(self._firmware): offset += 1 if self._firmware[offset] == 0: end_address = offset break data = self._firmware[start_address:end_address] return data, start_address, end_address else: offset += 1 return None, None, None #找寻string table中的所有字符串 def find_string_table_by_key_function_index(self, key_offset): """ Find string table by VxWorks key function name offset in VxWorks image. :param key_offset: key function name offset in VxWorks image. :return: """ self.logger.debug("Attempting to find string table by key function index with offset {:010x}".format(key_offset)) temp_str_tab_data = [] if len(self._symbol_table) > default_check_count: count = default_check_count else: count = len(self._symbol_table) start_offset = key_offset end_offset = key_offset self.logger.debug("Initializing with start_offset = end_offset = {:010x}".format(key_offset)) while start_offset > 0: if self._is_printable(self._firmware[start_offset]) is True: # get string from offset string, start_address, end_address = self._get_prev_string_data(start_offset) self.logger.debug("string: {}; start_address: {:010x}; end_address: {:010x}".format(string, start_address, end_address)) # check string is function name if self._is_func_name(string) is False: if len(temp_str_tab_data) < count: self.logger.error("Can't find any string table with key index: {}".format(string)) return None, None else: self.logger.info("Found string table start address at {:010x}".format(start_address)) break else: temp_str_tab_data.append((string, start_address, end_address)) # get previous string from offset prev_string, prev_start_address, prev_end_address = self._get_prev_string_data(start_address - 1) self.logger.debug("prev_string: {}, prev_start_address: {:010x}, prev_end_address: {:010x}".format(prev_string, prev_start_address, prev_end_address)) if prev_start_address: # strings interval should less than 4 if 4 < (start_address - prev_end_address): if len(temp_str_tab_data) < count: self.logger.error("Can't find any string table with key index: {}".format(string)) return None, None else: self.logger.info("found string table start address at {:010x}".format(start_address)) break else: start_offset = start_address - 1 self.logger.debug("start_offset: {}".format(start_offset)) else: break else: start_offset -= 1 while end_offset < len(self._firmware): # find first printable char if self._is_printable(self._firmware[end_offset]) is True: # get string from offset string, start_address, end_address = self._get_next_string_data(end_offset) # check string is function name if self._is_func_name(string) is False: if len(temp_str_tab_data) < count: temp_str_tab_data = [] end_offset = end_address continue else: self.logger.info("found string table end at {:010x}".format(end_address)) break else: temp_str_tab_data.append((string, start_address, end_address)) # get next string from offset next_string, next_start_address, next_end_address = self._get_next_string_data(end_address) if next_start_address: # strings interval should less than 4 if 4 < (next_start_address - end_address): if len(temp_str_tab_data) < count: self.logger.error("Can't find any string table with key index.") return None, None else: self.logger.info("Found string table end at {:010x}".format(end_address)) break else: end_offset = end_address else: end_offset += 1 temp_str_tab_data = sorted(temp_str_tab_data, key=lambda x: (x[1])) #将检测到的函数名放进来 table_start_offset = temp_str_tab_data[0][1] table_end_offset = temp_str_tab_data[-1][2] self.logger.info("Found a string table at: {:010x} to {:010x}".format(table_start_offset, table_end_offset)) return table_start_offset, table_end_offset def get_string_table(self, str_start_address, str_end_address): """ Get string table data from VxWorks image with string table start and end offset. :param str_start_address: string table start address. :param str_end_address: string table end address. :return: """ self._string_table = [] offset = str_start_address address = offset str_tab_data = [] while offset <= str_end_address: if self._firmware[offset] == 0: while offset <= str_end_address: offset += 1 if self._firmware[offset] != 0: next_address = offset string = self._firmware[address:next_address] length = next_address - address str_tab_data.append({'address': address, 'string': string, 'length': length}) offset = next_address address = next_address break else: offset += 1 self._string_table = str_tab_data def _check_fix(self, func_index, str_index): """ :param func_index: :param str_index: :return: """ try: fault_count = 0 self.logger.debug("Symbol table's first element: {}".format(self._symbol_table[0])) if len(self._symbol_table) <= default_check_count: count = len(self._symbol_table) self.logger.debug("Length of symbol table, {}, is less than default. Setting iteration count to actual length of table, {}.".format(len(self._symbol_table), count)) else: count = default_check_count self.logger.debug("Length of symbol table, {}, is greater than default. Setting iteration count to default, {}.".format(len(self._symbol_table), count)) for i in range(count): if (func_index >= len(self._symbol_table)) or (str_index >= len(self._string_table)): self.logger.debug("_check_fix False: func_index greater than length of _symbol_table, or str_index greater than length of _string_table.") return False self.logger.debug("str_index: {}; _string_table[str_index]: {}".format(str_index, self._string_table[str_index])) self.logger.debug("func_index: {}; _symbol_table[func_index]: {}".format(func_index, self._symbol_table[func_index])) if i == count - 1: if fault_count < 10: self.logger.debug("_check_fix True") return True else: self.logger.debug("_check_fix False: Too many faults.") return False if self._string_table[str_index]['length'] == self._symbol_table[func_index]['symbol_name_length']: func_index += 1 str_index += 1 self.logger.debug("_check_fix continue") elif self._symbol_table[func_index]['symbol_name_length'] < self._string_table[str_index]['length']: # Sometime Symbol name might point to mid of string. fault_count += 1 func_index += 1 else: self.logger.debug("_check_fix False: symbol_name_length from func_index larger than length from str_index.") return False except Exception as e: self.logger.debug(e) raise def find_loading_address(self): """ Find VxWorks image load address by automatic analysis. :return: Load address if found, None otherwise. """ # Search function keyword in firmware to locate the function string tables. self.reset_timer() if self._has_symbol is False: return None for key_word in function_name_key_words: prefix_keyword = b'\x00_' + key_word + b'\x00' key_word = b'\x00' + key_word + b'\x00' if key_word in self._firmware is False and prefix_keyword in self._firmware is False: self.logger.info("Firmware does not contain a function named {}".format(key_word)) return None performance_data = "Search function keyword in firmware takes {:.3f} seconds".format(self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data) # Search function keyword in firmware to locate the function string tables. self.reset_timer() try: key_function_index = self._firmware.index(b'\x00' + function_name_key_words[0] + b'\x00') self.logger.debug("key_function_index: {}".format(key_function_index)) except Exception as err: # Handler _ prefix symbols self.logger.debug("Exception occurred while loading key_function_index: {}. Continuing...".format(err)) key_function_index = self._firmware.index(b'\x00_' + function_name_key_words[0] + b'\x00') self.logger.debug("key_function_index: {}".format(key_function_index)) str_start_address, str_end_address = self.find_string_table_by_key_function_index(key_function_index) self.get_string_table(str_start_address, str_end_address) performance_data = "Get function string table takes {:.3f} seconds".format(self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data) # TODO: Need improve performance self.reset_timer() self.logger.info("Starting loading address analysis") for str_index in range(len(self._string_table)): for func_index in range(len(self._symbol_table)): self.logger.debug("self._string_table[str_index]['length']: {}".format(self._string_table[str_index]['length'])) self.logger.debug("self._symbol_table[func_index]['symbol_name_length']: {}".format(self._symbol_table[func_index]['symbol_name_length'])) if self._string_table[str_index]['length'] == self._symbol_table[func_index]['symbol_name_length']: if self._check_fix(func_index, str_index) is True: self.logger.debug("self._symbol_table[func_index]['symbol_name_addr']: {}".format(self._symbol_table[func_index]['symbol_name_addr'])) self.logger.debug("self._string_table[str_index]['address']: %s" % self._string_table[str_index]['address']) self.load_address = self._symbol_table[func_index]['symbol_name_addr'] - \ self._string_table[str_index]['address'] self._firmware_info["load_address"] = self.load_address self.logger.info('load address is {:010x}'.format(self.load_address)) performance_data = "Analyze loading address takes {:.3f} seconds".format( self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data) return self.load_address else: continue self.logger.error("We didn't find load address in this firmware, sorry!") performance_data = "Analyze loading address takes {:.3f} seconds".format(self.get_timer()) self._performance_status.append(performance_data) self.logger.debug(performance_data)* def _check_load_address(self, address): """ :param address: :return: """ if not self._has_symbol: return False if len(self._symbol_table) > default_check_count: self.logger.debug("Length of symbol table greater than default. Setting iteration count to default of {}.".format(default_check_count)) count = default_check_count else: count = len(self._symbol_table) self.logger.debug("symbol_table length is {}".format(count)) for i in range(count): offset = self._symbol_table[i]['symbol_name_addr'] - address if offset <= 0: return False # TODO: Need improve, currently use string point to check. string, str_start_address, str_end_address = self._get_next_string_data(offset) if str_start_address != offset: self.logger.info("String {} at offset {} didn't match symbol table.".format(string, offset)) return False self.logger.info('Load address is {:010x}'.format(address)) return True def quick_test(self): """ Using known load address list to match VxWorks image. :return: Load address if match known address, None otherwise. """ if self._has_symbol is False: return None self.logger.debug("has_symbol: {}".format(self._has_symbol)) for address in known_address: if self._check_load_address(address): self.load_address = address self._firmware_info["load_address"] = self.load_address return self.load_address else: self.logger.info('Load address is not {:010x}'.format(address)) def cleanup(self): """ Clean up variables. :return: """ self.is_big_endian = False self.symbol_table_start = None self.symbol_table_end = None self._string_table = [] self._symbol_table = [] self.load_address = None self._has_symbol = None def get_string_from_firmware_by_offset(self, string_offset): symbol_name = "" while True: if self._firmware[string_offset] != 0x00: symbol_name += chr(self._firmware[string_offset]) string_offset += 1 else: break return symbol_name def get_symbols(self): self.symbols = [] if self.load_address: for symbol in self._symbol_table: symbol_name_addr = symbol["symbol_name_addr"] symbol_dest_addr = symbol["symbol_dest_addr"] symbol_flag = symbol["symbol_flag"] symbol_name_firmware_addr = symbol_name_addr - self.load_address symbol_name = self.get_string_from_firmware_by_offset(symbol_name_firmware_addr) self.symbols.append({ "symbol_name": symbol_name, "symbol_name_addr": symbol_name_addr, "symbol_dest_addr": symbol_dest_addr, "symbol_flag": symbol_flag }) return self.symbols else: return None def get_firmware_info(self): symbols = self.get_symbols() self._firmware_info["symbols"] = symbols return self._firmware_info def get_performance_status(self): return self._performance_status def demangle_function(demangle_string): function_return = None function_parameters = None function_name_end = len(demangle_string) # get parameters index = len(demangle_string) - 1 if demangle_string[-1] == ')': # have parameters parentheses_count = 0 while index >= 0: if demangle_string[index] == ')': parentheses_count += 1 elif demangle_string[index] == '(': parentheses_count -= 1 index -= 1 if parentheses_count == 0: break function_parameters = demangle_string[index + 2:-1] function_name_end = index # get function name while index >= 0: if demangle_string[index] == ' ': break else: index -= 1 function_name_start = index function_name = demangle_string[function_name_start + 1:function_name_end + 1] # get function return function_return = demangle_string[:function_name_start] return function_return, function_name, function_parameters def is_vx_symbol_file(file_data, is_big_endian=True): # Check key function names for key_function in function_name_key_words: if key_function not in file_data: print("key function not found") return False if is_big_endian: return struct.unpack('>I', file_data[:4])[0] == len(file_data) else: return struct.unpack('<I', file_data[:4])[0] == len(file_data) 脚本分析总结
首先先进行VXworks的特征匹配(通过bzero等),确定之后进入函数具体分析,定义版本5和版本6符号表的特征,设置日志,将重要信息存储起来,先确定时大端小端,之后检查type类型是否匹配,type后面的字节是否是0,如果是则开始检查符号表的起始地址并进行性能检查(通过计时的方式),这里有一个性能优化动作,当检测起始位置的时候,从第一偏移量开始检测并使用只检查单一字符串格式合法方式进行性能优化,如果检测成功则进行连续检查100次,如果符合就找寻末尾地址,找到之后从左往找寻00,输出字符串。这个脚本用到了两种方法一个是自动找另一个是直接猜
实战操作
通过之前的分析我们知道VXworks会有一位字节代表函数类型,比如是全局变量还是本地变量
重点看后三位字节是否是有序的,VXwoeks的重要修复思路就是使用有序的排列的字符串地址去找映射到内核文件使用的函数地址,简单来说就是第一个是符号表文件中字符串的排列顺序
后面的40***就是他在内核文件(38200)的函数引用的绝对地址了