From 4faa526acd41470169a9581c8e0e75ec269e9fa2 Mon Sep 17 00:00:00 2001 From: t3ls Date: Fri, 18 Dec 2020 17:39:28 +0800 Subject: [PATCH 1/3] remove prettytable file --- prettytable.py | 1475 ----------------------------------------------- prettytable.pyc | Bin 48922 -> 0 bytes 2 files changed, 1475 deletions(-) delete mode 100644 prettytable.py delete mode 100644 prettytable.pyc diff --git a/prettytable.py b/prettytable.py deleted file mode 100644 index 8abb952..0000000 --- a/prettytable.py +++ /dev/null @@ -1,1475 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009-2013, Luke Maurits -# All rights reserved. -# With contributions from: -# * Chris Clark -# * Klein Stephane -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -__version__ = "0.7.2" - -import copy -import csv -import random -import re -import sys -import textwrap -import itertools -import unicodedata - -py3k = sys.version_info[0] >= 3 -if py3k: - unicode = str - basestring = str - itermap = map - iterzip = zip - uni_chr = chr - from html.parser import HTMLParser -else: - itermap = itertools.imap - iterzip = itertools.izip - uni_chr = unichr - from HTMLParser import HTMLParser - -if py3k and sys.version_info[1] >= 2: - from html import escape -else: - from cgi import escape - -# hrule styles -FRAME = 0 -ALL = 1 -NONE = 2 -HEADER = 3 - -# Table styles -DEFAULT = 10 -MSWORD_FRIENDLY = 11 -PLAIN_COLUMNS = 12 -RANDOM = 20 - -_re = re.compile("\033\[[0-9;]*m") - -def _get_size(text): - lines = text.split("\n") - height = len(lines) - width = max([_str_block_width(line) for line in lines]) - return (width, height) - -class PrettyTable(object): - - def __init__(self, field_names=None, **kwargs): - - """Return a new PrettyTable instance - - Arguments: - - encoding - Unicode encoding scheme used to decode any encoded input - field_names - list or tuple of field names - fields - list or tuple of field names to include in displays - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - header - print a header showing field names (True or False) - header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: FRAME, HEADER, ALL, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - vertical_char - single character string used to draw vertical lines - horizontal_char - single character string used to draw horizontal lines - junction_char - single character string used to draw line junctions - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - valign - default valign for each row (None, "t", "m" or "b") - reversesort - True or False to sort in descending or ascending order""" - - self.encoding = kwargs.get("encoding", "UTF-8") - - # Data - self._field_names = [] - self._align = {} - self._valign = {} - self._max_width = {} - self._rows = [] - if field_names: - self.field_names = field_names - else: - self._widths = [] - - # Options - self._options = "start end fields header border sortby reversesort sort_key attributes format hrules vrules".split() - self._options.extend("int_format float_format padding_width left_padding_width right_padding_width".split()) - self._options.extend("vertical_char horizontal_char junction_char header_style valign xhtml print_empty".split()) - for option in self._options: - if option in kwargs: - self._validate_option(option, kwargs[option]) - else: - kwargs[option] = None - - self._start = kwargs["start"] or 0 - self._end = kwargs["end"] or None - self._fields = kwargs["fields"] or None - - if kwargs["header"] in (True, False): - self._header = kwargs["header"] - else: - self._header = True - self._header_style = kwargs["header_style"] or None - if kwargs["border"] in (True, False): - self._border = kwargs["border"] - else: - self._border = True - self._hrules = kwargs["hrules"] or FRAME - self._vrules = kwargs["vrules"] or ALL - - self._sortby = kwargs["sortby"] or None - if kwargs["reversesort"] in (True, False): - self._reversesort = kwargs["reversesort"] - else: - self._reversesort = False - self._sort_key = kwargs["sort_key"] or (lambda x: x) - - self._int_format = kwargs["int_format"] or {} - self._float_format = kwargs["float_format"] or {} - self._padding_width = kwargs["padding_width"] or 1 - self._left_padding_width = kwargs["left_padding_width"] or None - self._right_padding_width = kwargs["right_padding_width"] or None - - self._vertical_char = kwargs["vertical_char"] or self._unicode("|") - self._horizontal_char = kwargs["horizontal_char"] or self._unicode("-") - self._junction_char = kwargs["junction_char"] or self._unicode("+") - - if kwargs["print_empty"] in (True, False): - self._print_empty = kwargs["print_empty"] - else: - self._print_empty = True - self._format = kwargs["format"] or False - self._xhtml = kwargs["xhtml"] or False - self._attributes = kwargs["attributes"] or {} - - def _unicode(self, value): - if not isinstance(value, basestring): - value = str(value) - if not isinstance(value, unicode): - value = unicode(value, self.encoding, "strict") - return value - - def _justify(self, text, width, align): - excess = width - _str_block_width(text) - if align == "l": - return text + excess * " " - elif align == "r": - return excess * " " + text - else: - if excess % 2: - # Uneven padding - # Put more space on right if text is of odd length... - if _str_block_width(text) % 2: - return (excess//2)*" " + text + (excess//2 + 1)*" " - # and more space on left if text is of even length - else: - return (excess//2 + 1)*" " + text + (excess//2)*" " - # Why distribute extra space this way? To match the behaviour of - # the inbuilt str.center() method. - else: - # Equal padding on either side - return (excess//2)*" " + text + (excess//2)*" " - - def __getattr__(self, name): - - if name == "rowcount": - return len(self._rows) - elif name == "colcount": - if self._field_names: - return len(self._field_names) - elif self._rows: - return len(self._rows[0]) - else: - return 0 - else: - raise AttributeError(name) - - def __getitem__(self, index): - - new = PrettyTable() - new.field_names = self.field_names - for attr in self._options: - setattr(new, "_"+attr, getattr(self, "_"+attr)) - setattr(new, "_align", getattr(self, "_align")) - if isinstance(index, slice): - for row in self._rows[index]: - new.add_row(row) - elif isinstance(index, int): - new.add_row(self._rows[index]) - else: - raise Exception("Index %s is invalid, must be an integer or slice" % str(index)) - return new - - if py3k: - def __str__(self): - return self.__unicode__() - else: - def __str__(self): - return self.__unicode__().encode(self.encoding) - - def __unicode__(self): - return self.get_string() - - ############################## - # ATTRIBUTE VALIDATORS # - ############################## - - # The method _validate_option is all that should be used elsewhere in the code base to validate options. - # It will call the appropriate validation method for that option. The individual validation methods should - # never need to be called directly (although nothing bad will happen if they *are*). - # Validation happens in TWO places. - # Firstly, in the property setters defined in the ATTRIBUTE MANAGMENT section. - # Secondly, in the _get_options method, where keyword arguments are mixed with persistent settings - - def _validate_option(self, option, val): - if option in ("field_names"): - self._validate_field_names(val) - elif option in ("start", "end", "max_width", "padding_width", "left_padding_width", "right_padding_width", "format"): - self._validate_nonnegative_int(option, val) - elif option in ("sortby"): - self._validate_field_name(option, val) - elif option in ("sort_key"): - self._validate_function(option, val) - elif option in ("hrules"): - self._validate_hrules(option, val) - elif option in ("vrules"): - self._validate_vrules(option, val) - elif option in ("fields"): - self._validate_all_field_names(option, val) - elif option in ("header", "border", "reversesort", "xhtml", "print_empty"): - self._validate_true_or_false(option, val) - elif option in ("header_style"): - self._validate_header_style(val) - elif option in ("int_format"): - self._validate_int_format(option, val) - elif option in ("float_format"): - self._validate_float_format(option, val) - elif option in ("vertical_char", "horizontal_char", "junction_char"): - self._validate_single_char(option, val) - elif option in ("attributes"): - self._validate_attributes(option, val) - else: - raise Exception("Unrecognised option: %s!" % option) - - def _validate_field_names(self, val): - # Check for appropriate length - if self._field_names: - try: - assert len(val) == len(self._field_names) - except AssertionError: - raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._field_names))) - if self._rows: - try: - assert len(val) == len(self._rows[0]) - except AssertionError: - raise Exception("Field name list has incorrect number of values, (actual) %d!=%d (expected)" % (len(val), len(self._rows[0]))) - # Check for uniqueness - try: - assert len(val) == len(set(val)) - except AssertionError: - raise Exception("Field names must be unique!") - - def _validate_header_style(self, val): - try: - assert val in ("cap", "title", "upper", "lower", None) - except AssertionError: - raise Exception("Invalid header style, use cap, title, upper, lower or None!") - - def _validate_align(self, val): - try: - assert val in ["l","c","r"] - except AssertionError: - raise Exception("Alignment %s is invalid, use l, c or r!" % val) - - def _validate_valign(self, val): - try: - assert val in ["t","m","b",None] - except AssertionError: - raise Exception("Alignment %s is invalid, use t, m, b or None!" % val) - - def _validate_nonnegative_int(self, name, val): - try: - assert int(val) >= 0 - except AssertionError: - raise Exception("Invalid value for %s: %s!" % (name, self._unicode(val))) - - def _validate_true_or_false(self, name, val): - try: - assert val in (True, False) - except AssertionError: - raise Exception("Invalid value for %s! Must be True or False." % name) - - def _validate_int_format(self, name, val): - if val == "": - return - try: - assert type(val) in (str, unicode) - assert val.isdigit() - except AssertionError: - raise Exception("Invalid value for %s! Must be an integer format string." % name) - - def _validate_float_format(self, name, val): - if val == "": - return - try: - assert type(val) in (str, unicode) - assert "." in val - bits = val.split(".") - assert len(bits) <= 2 - assert bits[0] == "" or bits[0].isdigit() - assert bits[1] == "" or bits[1].isdigit() - except AssertionError: - raise Exception("Invalid value for %s! Must be a float format string." % name) - - def _validate_function(self, name, val): - try: - assert hasattr(val, "__call__") - except AssertionError: - raise Exception("Invalid value for %s! Must be a function." % name) - - def _validate_hrules(self, name, val): - try: - assert val in (ALL, FRAME, HEADER, NONE) - except AssertionError: - raise Exception("Invalid value for %s! Must be ALL, FRAME, HEADER or NONE." % name) - - def _validate_vrules(self, name, val): - try: - assert val in (ALL, FRAME, NONE) - except AssertionError: - raise Exception("Invalid value for %s! Must be ALL, FRAME, or NONE." % name) - - def _validate_field_name(self, name, val): - try: - assert (val in self._field_names) or (val is None) - except AssertionError: - raise Exception("Invalid field name: %s!" % val) - - def _validate_all_field_names(self, name, val): - try: - for x in val: - self._validate_field_name(name, x) - except AssertionError: - raise Exception("fields must be a sequence of field names!") - - def _validate_single_char(self, name, val): - try: - assert _str_block_width(val) == 1 - except AssertionError: - raise Exception("Invalid value for %s! Must be a string of length 1." % name) - - def _validate_attributes(self, name, val): - try: - assert isinstance(val, dict) - except AssertionError: - raise Exception("attributes must be a dictionary of name/value pairs!") - - ############################## - # ATTRIBUTE MANAGEMENT # - ############################## - - def _get_field_names(self): - return self._field_names - """The names of the fields - - Arguments: - - fields - list or tuple of field names""" - def _set_field_names(self, val): - val = [self._unicode(x) for x in val] - self._validate_option("field_names", val) - if self._field_names: - old_names = self._field_names[:] - self._field_names = val - if self._align and old_names: - for old_name, new_name in zip(old_names, val): - self._align[new_name] = self._align[old_name] - for old_name in old_names: - if old_name not in self._align: - self._align.pop(old_name) - else: - for field in self._field_names: - self._align[field] = "c" - if self._valign and old_names: - for old_name, new_name in zip(old_names, val): - self._valign[new_name] = self._valign[old_name] - for old_name in old_names: - if old_name not in self._valign: - self._valign.pop(old_name) - else: - for field in self._field_names: - self._valign[field] = "t" - field_names = property(_get_field_names, _set_field_names) - - def _get_align(self): - return self._align - def _set_align(self, val): - self._validate_align(val) - for field in self._field_names: - self._align[field] = val - align = property(_get_align, _set_align) - - def _get_valign(self): - return self._valign - def _set_valign(self, val): - self._validate_valign(val) - for field in self._field_names: - self._valign[field] = val - valign = property(_get_valign, _set_valign) - - def _get_max_width(self): - return self._max_width - def _set_max_width(self, val): - self._validate_option("max_width", val) - for field in self._field_names: - self._max_width[field] = val - max_width = property(_get_max_width, _set_max_width) - - def _get_fields(self): - """List or tuple of field names to include in displays - - Arguments: - - fields - list or tuple of field names to include in displays""" - return self._fields - def _set_fields(self, val): - self._validate_option("fields", val) - self._fields = val - fields = property(_get_fields, _set_fields) - - def _get_start(self): - """Start index of the range of rows to print - - Arguments: - - start - index of first data row to include in output""" - return self._start - - def _set_start(self, val): - self._validate_option("start", val) - self._start = val - start = property(_get_start, _set_start) - - def _get_end(self): - """End index of the range of rows to print - - Arguments: - - end - index of last data row to include in output PLUS ONE (list slice style)""" - return self._end - def _set_end(self, val): - self._validate_option("end", val) - self._end = val - end = property(_get_end, _set_end) - - def _get_sortby(self): - """Name of field by which to sort rows - - Arguments: - - sortby - field name to sort by""" - return self._sortby - def _set_sortby(self, val): - self._validate_option("sortby", val) - self._sortby = val - sortby = property(_get_sortby, _set_sortby) - - def _get_reversesort(self): - """Controls direction of sorting (ascending vs descending) - - Arguments: - - reveresort - set to True to sort by descending order, or False to sort by ascending order""" - return self._reversesort - def _set_reversesort(self, val): - self._validate_option("reversesort", val) - self._reversesort = val - reversesort = property(_get_reversesort, _set_reversesort) - - def _get_sort_key(self): - """Sorting key function, applied to data points before sorting - - Arguments: - - sort_key - a function which takes one argument and returns something to be sorted""" - return self._sort_key - def _set_sort_key(self, val): - self._validate_option("sort_key", val) - self._sort_key = val - sort_key = property(_get_sort_key, _set_sort_key) - - def _get_header(self): - """Controls printing of table header with field names - - Arguments: - - header - print a header showing field names (True or False)""" - return self._header - def _set_header(self, val): - self._validate_option("header", val) - self._header = val - header = property(_get_header, _set_header) - - def _get_header_style(self): - """Controls stylisation applied to field names in header - - Arguments: - - header_style - stylisation to apply to field names in header ("cap", "title", "upper", "lower" or None)""" - return self._header_style - def _set_header_style(self, val): - self._validate_header_style(val) - self._header_style = val - header_style = property(_get_header_style, _set_header_style) - - def _get_border(self): - """Controls printing of border around table - - Arguments: - - border - print a border around the table (True or False)""" - return self._border - def _set_border(self, val): - self._validate_option("border", val) - self._border = val - border = property(_get_border, _set_border) - - def _get_hrules(self): - """Controls printing of horizontal rules after rows - - Arguments: - - hrules - horizontal rules style. Allowed values: FRAME, ALL, HEADER, NONE""" - return self._hrules - def _set_hrules(self, val): - self._validate_option("hrules", val) - self._hrules = val - hrules = property(_get_hrules, _set_hrules) - - def _get_vrules(self): - """Controls printing of vertical rules between columns - - Arguments: - - vrules - vertical rules style. Allowed values: FRAME, ALL, NONE""" - return self._vrules - def _set_vrules(self, val): - self._validate_option("vrules", val) - self._vrules = val - vrules = property(_get_vrules, _set_vrules) - - def _get_int_format(self): - """Controls formatting of integer data - Arguments: - - int_format - integer format string""" - return self._int_format - def _set_int_format(self, val): -# self._validate_option("int_format", val) - for field in self._field_names: - self._int_format[field] = val - int_format = property(_get_int_format, _set_int_format) - - def _get_float_format(self): - """Controls formatting of floating point data - Arguments: - - float_format - floating point format string""" - return self._float_format - def _set_float_format(self, val): -# self._validate_option("float_format", val) - for field in self._field_names: - self._float_format[field] = val - float_format = property(_get_float_format, _set_float_format) - - def _get_padding_width(self): - """The number of empty spaces between a column's edge and its content - - Arguments: - - padding_width - number of spaces, must be a positive integer""" - return self._padding_width - def _set_padding_width(self, val): - self._validate_option("padding_width", val) - self._padding_width = val - padding_width = property(_get_padding_width, _set_padding_width) - - def _get_left_padding_width(self): - """The number of empty spaces between a column's left edge and its content - - Arguments: - - left_padding - number of spaces, must be a positive integer""" - return self._left_padding_width - def _set_left_padding_width(self, val): - self._validate_option("left_padding_width", val) - self._left_padding_width = val - left_padding_width = property(_get_left_padding_width, _set_left_padding_width) - - def _get_right_padding_width(self): - """The number of empty spaces between a column's right edge and its content - - Arguments: - - right_padding - number of spaces, must be a positive integer""" - return self._right_padding_width - def _set_right_padding_width(self, val): - self._validate_option("right_padding_width", val) - self._right_padding_width = val - right_padding_width = property(_get_right_padding_width, _set_right_padding_width) - - def _get_vertical_char(self): - """The charcter used when printing table borders to draw vertical lines - - Arguments: - - vertical_char - single character string used to draw vertical lines""" - return self._vertical_char - def _set_vertical_char(self, val): - val = self._unicode(val) - self._validate_option("vertical_char", val) - self._vertical_char = val - vertical_char = property(_get_vertical_char, _set_vertical_char) - - def _get_horizontal_char(self): - """The charcter used when printing table borders to draw horizontal lines - - Arguments: - - horizontal_char - single character string used to draw horizontal lines""" - return self._horizontal_char - def _set_horizontal_char(self, val): - val = self._unicode(val) - self._validate_option("horizontal_char", val) - self._horizontal_char = val - horizontal_char = property(_get_horizontal_char, _set_horizontal_char) - - def _get_junction_char(self): - """The charcter used when printing table borders to draw line junctions - - Arguments: - - junction_char - single character string used to draw line junctions""" - return self._junction_char - def _set_junction_char(self, val): - val = self._unicode(val) - self._validate_option("vertical_char", val) - self._junction_char = val - junction_char = property(_get_junction_char, _set_junction_char) - - def _get_format(self): - """Controls whether or not HTML tables are formatted to match styling options - - Arguments: - - format - True or False""" - return self._format - def _set_format(self, val): - self._validate_option("format", val) - self._format = val - format = property(_get_format, _set_format) - - def _get_print_empty(self): - """Controls whether or not empty tables produce a header and frame or just an empty string - - Arguments: - - print_empty - True or False""" - return self._print_empty - def _set_print_empty(self, val): - self._validate_option("print_empty", val) - self._print_empty = val - print_empty = property(_get_print_empty, _set_print_empty) - - def _get_attributes(self): - """A dictionary of HTML attribute name/value pairs to be included in the tag when printing HTML - - Arguments: - - attributes - dictionary of attributes""" - return self._attributes - def _set_attributes(self, val): - self._validate_option("attributes", val) - self._attributes = val - attributes = property(_get_attributes, _set_attributes) - - ############################## - # OPTION MIXER # - ############################## - - def _get_options(self, kwargs): - - options = {} - for option in self._options: - if option in kwargs: - self._validate_option(option, kwargs[option]) - options[option] = kwargs[option] - else: - options[option] = getattr(self, "_"+option) - return options - - ############################## - # PRESET STYLE LOGIC # - ############################## - - def set_style(self, style): - - if style == DEFAULT: - self._set_default_style() - elif style == MSWORD_FRIENDLY: - self._set_msword_style() - elif style == PLAIN_COLUMNS: - self._set_columns_style() - elif style == RANDOM: - self._set_random_style() - else: - raise Exception("Invalid pre-set style!") - - def _set_default_style(self): - - self.header = True - self.border = True - self._hrules = FRAME - self._vrules = ALL - self.padding_width = 1 - self.left_padding_width = 1 - self.right_padding_width = 1 - self.vertical_char = "|" - self.horizontal_char = "-" - self.junction_char = "+" - - def _set_msword_style(self): - - self.header = True - self.border = True - self._hrules = NONE - self.padding_width = 1 - self.left_padding_width = 1 - self.right_padding_width = 1 - self.vertical_char = "|" - - def _set_columns_style(self): - - self.header = True - self.border = False - self.padding_width = 1 - self.left_padding_width = 0 - self.right_padding_width = 8 - - def _set_random_style(self): - - # Just for fun! - self.header = random.choice((True, False)) - self.border = random.choice((True, False)) - self._hrules = random.choice((ALL, FRAME, HEADER, NONE)) - self._vrules = random.choice((ALL, FRAME, NONE)) - self.left_padding_width = random.randint(0,5) - self.right_padding_width = random.randint(0,5) - self.vertical_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - self.horizontal_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - self.junction_char = random.choice("~!@#$%^&*()_+|-=\{}[];':\",./;<>?") - - ############################## - # DATA INPUT METHODS # - ############################## - - def add_row(self, row): - - """Add a row to the table - - Arguments: - - row - row of data, should be a list with as many elements as the table - has fields""" - - if self._field_names and len(row) != len(self._field_names): - raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" %(len(row),len(self._field_names))) - if not self._field_names: - self.field_names = [("Field %d" % (n+1)) for n in range(0,len(row))] - self._rows.append(list(row)) - - def del_row(self, row_index): - - """Delete a row to the table - - Arguments: - - row_index - The index of the row you want to delete. Indexing starts at 0.""" - - if row_index > len(self._rows)-1: - raise Exception("Cant delete row at index %d, table only has %d rows!" % (row_index, len(self._rows))) - del self._rows[row_index] - - def add_column(self, fieldname, column, align="c", valign="t"): - - """Add a column to the table. - - Arguments: - - fieldname - name of the field to contain the new column of data - column - column of data, should be a list with as many elements as the - table has rows - align - desired alignment for this column - "l" for left, "c" for centre and "r" for right - valign - desired vertical alignment for new columns - "t" for top, "m" for middle and "b" for bottom""" - - if len(self._rows) in (0, len(column)): - self._validate_align(align) - self._validate_valign(valign) - self._field_names.append(fieldname) - self._align[fieldname] = align - self._valign[fieldname] = valign - for i in range(0, len(column)): - if len(self._rows) < i+1: - self._rows.append([]) - self._rows[i].append(column[i]) - else: - raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows))) - - def clear_rows(self): - - """Delete all rows from the table but keep the current field names""" - - self._rows = [] - - def clear(self): - - """Delete all rows and field names from the table, maintaining nothing but styling options""" - - self._rows = [] - self._field_names = [] - self._widths = [] - - ############################## - # MISC PUBLIC METHODS # - ############################## - - def copy(self): - return copy.deepcopy(self) - - ############################## - # MISC PRIVATE METHODS # - ############################## - - def _format_value(self, field, value): - if isinstance(value, int) and field in self._int_format: - value = self._unicode(("%%%sd" % self._int_format[field]) % value) - elif isinstance(value, float) and field in self._float_format: - value = self._unicode(("%%%sf" % self._float_format[field]) % value) - return self._unicode(value) - - def _compute_widths(self, rows, options): - if options["header"]: - widths = [_get_size(field)[0] for field in self._field_names] - else: - widths = len(self.field_names) * [0] - for row in rows: - for index, value in enumerate(row): - fieldname = self.field_names[index] - if fieldname in self.max_width: - widths[index] = max(widths[index], min(_get_size(value)[0], self.max_width[fieldname])) - else: - widths[index] = max(widths[index], _get_size(value)[0]) - self._widths = widths - - def _get_padding_widths(self, options): - - if options["left_padding_width"] is not None: - lpad = options["left_padding_width"] - else: - lpad = options["padding_width"] - if options["right_padding_width"] is not None: - rpad = options["right_padding_width"] - else: - rpad = options["padding_width"] - return lpad, rpad - - def _get_rows(self, options): - """Return only those data rows that should be printed, based on slicing and sorting. - - Arguments: - - options - dictionary of option settings.""" - - # Make a copy of only those rows in the slice range - rows = copy.deepcopy(self._rows[options["start"]:options["end"]]) - # Sort if necessary - if options["sortby"]: - sortindex = self._field_names.index(options["sortby"]) - # Decorate - rows = [[row[sortindex]]+row for row in rows] - # Sort - rows.sort(reverse=options["reversesort"], key=options["sort_key"]) - # Undecorate - rows = [row[1:] for row in rows] - return rows - - def _format_row(self, row, options): - return [self._format_value(field, value) for (field, value) in zip(self._field_names, row)] - - def _format_rows(self, rows, options): - return [self._format_row(row, options) for row in rows] - - ############################## - # PLAIN TEXT STRING METHODS # - ############################## - - def get_string(self, **kwargs): - - """Return string representation of table in current state. - - Arguments: - - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - fields - names of fields (columns) to include - header - print a header showing field names (True or False) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - vertical_char - single character string used to draw vertical lines - horizontal_char - single character string used to draw horizontal lines - junction_char - single character string used to draw line junctions - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - reversesort - True or False to sort in descending or ascending order - print empty - if True, stringify just the header for an empty table, if False return an empty string """ - - options = self._get_options(kwargs) - - lines = [] - - # Don't think too hard about an empty table - # Is this the desired behaviour? Maybe we should still print the header? - if self.rowcount == 0 and (not options["print_empty"] or not options["border"]): - return "" - - # Get the rows we need to print, taking into account slicing, sorting, etc. - rows = self._get_rows(options) - - # Turn all data in all rows into Unicode, formatted as desired - formatted_rows = self._format_rows(rows, options) - - # Compute column widths - self._compute_widths(formatted_rows, options) - - # Add header or top of border - self._hrule = self._stringify_hrule(options) - if options["header"]: - lines.append(self._stringify_header(options)) - elif options["border"] and options["hrules"] in (ALL, FRAME): - lines.append(self._hrule) - - # Add rows - for row in formatted_rows: - lines.append(self._stringify_row(row, options)) - - # Add bottom of border - if options["border"] and options["hrules"] == FRAME: - lines.append(self._hrule) - - return self._unicode("\n").join(lines) - - def _stringify_hrule(self, options): - - if not options["border"]: - return "" - lpad, rpad = self._get_padding_widths(options) - if options['vrules'] in (ALL, FRAME): - bits = [options["junction_char"]] - else: - bits = [options["horizontal_char"]] - # For tables with no data or fieldnames - if not self._field_names: - bits.append(options["junction_char"]) - return "".join(bits) - for field, width in zip(self._field_names, self._widths): - if options["fields"] and field not in options["fields"]: - continue - bits.append((width+lpad+rpad)*options["horizontal_char"]) - if options['vrules'] == ALL: - bits.append(options["junction_char"]) - else: - bits.append(options["horizontal_char"]) - if options["vrules"] == FRAME: - bits.pop() - bits.append(options["junction_char"]) - return "".join(bits) - - def _stringify_header(self, options): - - bits = [] - lpad, rpad = self._get_padding_widths(options) - if options["border"]: - if options["hrules"] in (ALL, FRAME): - bits.append(self._hrule) - bits.append("\n") - if options["vrules"] in (ALL, FRAME): - bits.append(options["vertical_char"]) - else: - bits.append(" ") - # For tables with no data or field names - if not self._field_names: - if options["vrules"] in (ALL, FRAME): - bits.append(options["vertical_char"]) - else: - bits.append(" ") - for field, width, in zip(self._field_names, self._widths): - if options["fields"] and field not in options["fields"]: - continue - if self._header_style == "cap": - fieldname = field.capitalize() - elif self._header_style == "title": - fieldname = field.title() - elif self._header_style == "upper": - fieldname = field.upper() - elif self._header_style == "lower": - fieldname = field.lower() - else: - fieldname = field - bits.append(" " * lpad + self._justify(fieldname, width, self._align[field]) + " " * rpad) - if options["border"]: - if options["vrules"] == ALL: - bits.append(options["vertical_char"]) - else: - bits.append(" ") - # If vrules is FRAME, then we just appended a space at the end - # of the last field, when we really want a vertical character - if options["border"] and options["vrules"] == FRAME: - bits.pop() - bits.append(options["vertical_char"]) - if options["border"] and options["hrules"] != NONE: - bits.append("\n") - bits.append(self._hrule) - return "".join(bits) - - def _stringify_row(self, row, options): - - for index, field, value, width, in zip(range(0,len(row)), self._field_names, row, self._widths): - # Enforce max widths - lines = value.split("\n") - new_lines = [] - for line in lines: - if _str_block_width(line) > width: - line = textwrap.fill(line, width) - new_lines.append(line) - lines = new_lines - value = "\n".join(lines) - row[index] = value - - row_height = 0 - for c in row: - h = _get_size(c)[1] - if h > row_height: - row_height = h - - bits = [] - lpad, rpad = self._get_padding_widths(options) - for y in range(0, row_height): - bits.append([]) - if options["border"]: - if options["vrules"] in (ALL, FRAME): - bits[y].append(self.vertical_char) - else: - bits[y].append(" ") - - for field, value, width, in zip(self._field_names, row, self._widths): - - valign = self._valign[field] - lines = value.split("\n") - dHeight = row_height - len(lines) - if dHeight: - if valign == "m": - lines = [""] * int(dHeight / 2) + lines + [""] * (dHeight - int(dHeight / 2)) - elif valign == "b": - lines = [""] * dHeight + lines - else: - lines = lines + [""] * dHeight - - y = 0 - for l in lines: - if options["fields"] and field not in options["fields"]: - continue - - bits[y].append(" " * lpad + self._justify(l, width, self._align[field]) + " " * rpad) - if options["border"]: - if options["vrules"] == ALL: - bits[y].append(self.vertical_char) - else: - bits[y].append(" ") - y += 1 - - # If vrules is FRAME, then we just appended a space at the end - # of the last field, when we really want a vertical character - for y in range(0, row_height): - if options["border"] and options["vrules"] == FRAME: - bits[y].pop() - bits[y].append(options["vertical_char"]) - - if options["border"] and options["hrules"]== ALL: - bits[row_height-1].append("\n") - bits[row_height-1].append(self._hrule) - - for y in range(0, row_height): - bits[y] = "".join(bits[y]) - - return "\n".join(bits) - - ############################## - # HTML STRING METHODS # - ############################## - - def get_html_string(self, **kwargs): - - """Return string representation of HTML formatted version of table in current state. - - Arguments: - - start - index of first data row to include in output - end - index of last data row to include in output PLUS ONE (list slice style) - fields - names of fields (columns) to include - header - print a header showing field names (True or False) - border - print a border around the table (True or False) - hrules - controls printing of horizontal rules after rows. Allowed values: ALL, FRAME, HEADER, NONE - vrules - controls printing of vertical rules between columns. Allowed values: FRAME, ALL, NONE - int_format - controls formatting of integer data - float_format - controls formatting of floating point data - padding_width - number of spaces on either side of column data (only used if left and right paddings are None) - left_padding_width - number of spaces on left hand side of column data - right_padding_width - number of spaces on right hand side of column data - sortby - name of field to sort rows by - sort_key - sorting key function, applied to data points before sorting - attributes - dictionary of name/value pairs to include as HTML attributes in the
tag - xhtml - print
tags if True,
tags if false""" - - options = self._get_options(kwargs) - - if options["format"]: - string = self._get_formatted_html_string(options) - else: - string = self._get_simple_html_string(options) - - return string - - def _get_simple_html_string(self, options): - - lines = [] - if options["xhtml"]: - linebreak = "
" - else: - linebreak = "
" - - open_tag = [] - open_tag.append("") - lines.append("".join(open_tag)) - - # Headers - if options["header"]: - lines.append(" ") - for field in self._field_names: - if options["fields"] and field not in options["fields"]: - continue - lines.append(" " % escape(field).replace("\n", linebreak)) - lines.append(" ") - - # Data - rows = self._get_rows(options) - formatted_rows = self._format_rows(rows, options) - for row in formatted_rows: - lines.append(" ") - for field, datum in zip(self._field_names, row): - if options["fields"] and field not in options["fields"]: - continue - lines.append(" " % escape(datum).replace("\n", linebreak)) - lines.append(" ") - - lines.append("
%s
%s
") - - return self._unicode("\n").join(lines) - - def _get_formatted_html_string(self, options): - - lines = [] - lpad, rpad = self._get_padding_widths(options) - if options["xhtml"]: - linebreak = "
" - else: - linebreak = "
" - - open_tag = [] - open_tag.append("") - lines.append("".join(open_tag)) - - # Headers - if options["header"]: - lines.append(" ") - for field in self._field_names: - if options["fields"] and field not in options["fields"]: - continue - lines.append(" %s" % (lpad, rpad, escape(field).replace("\n", linebreak))) - lines.append(" ") - - # Data - rows = self._get_rows(options) - formatted_rows = self._format_rows(rows, options) - aligns = [] - valigns = [] - for field in self._field_names: - aligns.append({ "l" : "left", "r" : "right", "c" : "center" }[self._align[field]]) - valigns.append({"t" : "top", "m" : "middle", "b" : "bottom"}[self._valign[field]]) - for row in formatted_rows: - lines.append(" ") - for field, datum, align, valign in zip(self._field_names, row, aligns, valigns): - if options["fields"] and field not in options["fields"]: - continue - lines.append(" %s" % (lpad, rpad, align, valign, escape(datum).replace("\n", linebreak))) - lines.append(" ") - lines.append("") - - return self._unicode("\n").join(lines) - -############################## -# UNICODE WIDTH FUNCTIONS # -############################## - -def _char_block_width(char): - # Basic Latin, which is probably the most common case - #if char in xrange(0x0021, 0x007e): - #if char >= 0x0021 and char <= 0x007e: - if 0x0021 <= char <= 0x007e: - return 1 - # Chinese, Japanese, Korean (common) - if 0x4e00 <= char <= 0x9fff: - return 2 - # Hangul - if 0xac00 <= char <= 0xd7af: - return 2 - # Combining? - if unicodedata.combining(uni_chr(char)): - return 0 - # Hiragana and Katakana - if 0x3040 <= char <= 0x309f or 0x30a0 <= char <= 0x30ff: - return 2 - # Full-width Latin characters - if 0xff01 <= char <= 0xff60: - return 2 - # CJK punctuation - if 0x3000 <= char <= 0x303e: - return 2 - # Backspace and delete - if char in (0x0008, 0x007f): - return -1 - # Other control characters - elif char in (0x0000, 0x001f): - return 0 - # Take a guess - return 1 - -def _str_block_width(val): - - return sum(itermap(_char_block_width, itermap(ord, _re.sub("", val)))) - -############################## -# TABLE FACTORIES # -############################## - -def from_csv(fp, field_names = None, **kwargs): - - dialect = csv.Sniffer().sniff(fp.read(1024)) - fp.seek(0) - reader = csv.reader(fp, dialect) - - table = PrettyTable(**kwargs) - if field_names: - table.field_names = field_names - else: - if py3k: - table.field_names = [x.strip() for x in next(reader)] - else: - table.field_names = [x.strip() for x in reader.next()] - - for row in reader: - table.add_row([x.strip() for x in row]) - - return table - -def from_db_cursor(cursor, **kwargs): - - if cursor.description: - table = PrettyTable(**kwargs) - table.field_names = [col[0] for col in cursor.description] - for row in cursor.fetchall(): - table.add_row(row) - return table - -class TableHandler(HTMLParser): - - def __init__(self, **kwargs): - HTMLParser.__init__(self) - self.kwargs = kwargs - self.tables = [] - self.last_row = [] - self.rows = [] - self.max_row_width = 0 - self.active = None - self.last_content = "" - self.is_last_row_header = False - - def handle_starttag(self,tag, attrs): - self.active = tag - if tag == "th": - self.is_last_row_header = True - - def handle_endtag(self,tag): - if tag in ["th", "td"]: - stripped_content = self.last_content.strip() - self.last_row.append(stripped_content) - if tag == "tr": - self.rows.append( - (self.last_row, self.is_last_row_header)) - self.max_row_width = max(self.max_row_width, len(self.last_row)) - self.last_row = [] - self.is_last_row_header = False - if tag == "table": - table = self.generate_table(self.rows) - self.tables.append(table) - self.rows = [] - self.last_content = " " - self.active = None - - - def handle_data(self, data): - self.last_content += data - - def generate_table(self, rows): - """ - Generates from a list of rows a PrettyTable object. - """ - table = PrettyTable(**self.kwargs) - for row in self.rows: - if len(row[0]) < self.max_row_width: - appends = self.max_row_width - len(row[0]) - for i in range(1,appends): - row[0].append("-") - - if row[1] == True: - self.make_fields_unique(row[0]) - table.field_names = row[0] - else: - table.add_row(row[0]) - return table - - def make_fields_unique(self, fields): - """ - iterates over the row and make each field unique - """ - for i in range(0, len(fields)): - for j in range(i+1, len(fields)): - if fields[i] == fields[j]: - fields[j] += "'" - -def from_html(html_code, **kwargs): - """ - Generates a list of PrettyTables from a string of HTML code. Each in - the HTML becomes one PrettyTable object. - """ - - parser = TableHandler(**kwargs) - parser.feed(html_code) - return parser.tables - -def from_html_one(html_code, **kwargs): - """ - Generates a PrettyTables from a string of HTML code which contains only a - single
- """ - - tables = from_html(html_code, **kwargs) - try: - assert len(tables) == 1 - except AssertionError: - raise Exception("More than one
in provided HTML code! Use from_html instead.") - return tables[0] - -############################## -# MAIN (TEST FUNCTION) # -############################## - -def main(): - - x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"]) - x.sortby = "Population" - x.reversesort = True - x.int_format["Area"] = "04d" - x.float_format = "6.1f" - x.align["City name"] = "l" # Left align city names - x.add_row(["Adelaide", 1295, 1158259, 600.5]) - x.add_row(["Brisbane", 5905, 1857594, 1146.4]) - x.add_row(["Darwin", 112, 120900, 1714.7]) - x.add_row(["Hobart", 1357, 205556, 619.5]) - x.add_row(["Sydney", 2058, 4336374, 1214.8]) - x.add_row(["Melbourne", 1566, 3806092, 646.9]) - x.add_row(["Perth", 5386, 1554769, 869.4]) - print(x) - -if __name__ == "__main__": - main() diff --git a/prettytable.pyc b/prettytable.pyc deleted file mode 100644 index 7f45e579301fa43d2652f0c1b0b5f5575ddec4f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48922 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 From 5679cd8f8700980451978c12b2ba61baf9369cf4 Mon Sep 17 00:00:00 2001 From: t3ls Date: Fri, 18 Dec 2020 17:41:26 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E9=87=8D=E5=86=99=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BB=A5=E5=85=BC=E5=AE=B9ida7.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mipsAudit.py | 260 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 168 insertions(+), 92 deletions(-) mode change 100644 => 100755 mipsAudit.py diff --git a/mipsAudit.py b/mipsAudit.py old mode 100644 new mode 100755 index 79a47a4..9ea02da --- a/mipsAudit.py +++ b/mipsAudit.py @@ -7,8 +7,24 @@ # https://github.com/wangzery/SearchOverflow/blob/master/SearchOverflow.py from idaapi import * +import idaapi +import idc from prettytable import PrettyTable +if idaapi.IDA_SDK_VERSION > 700: + import ida_search + from idc import ( + print_operand + ) + from ida_bytes import ( + get_strlit_contents + ) +else: + from idc import ( + GetOpnd as print_operand, + GetString + ) + def get_strlit_contents(*args): return GetString(args[0]) DEBUG = True @@ -71,23 +87,74 @@ "printf":0 } + +try: + class MipsAudit_Menu_Context(idaapi.action_handler_t): + def __init__(self): + idaapi.action_handler_t.__init__(self) + + @classmethod + def get_name(self): + return self.__name__ + + @classmethod + def get_label(self): + return self.label + + @classmethod + def register(self, plugin, label): + self.plugin = plugin + self.label = label + instance = self() + return idaapi.register_action(idaapi.action_desc_t( + self.get_name(), # Name. Acts as an ID. Must be unique. + instance.get_label(), # Label. That's what users see. + instance # Handler. Called when activated, and for updating + )) + + @classmethod + def unregister(self): + """Unregister the action. + After unregistering the class cannot be used. + """ + idaapi.unregister_action(self.get_name()) + + @classmethod + def activate(self, ctx): + # dummy method + return 1 + + @classmethod + def update(self, ctx): + if ctx.form_type == idaapi.BWN_DISASM: + return idaapi.AST_ENABLE_FOR_WIDGET + return idaapi.AST_DISABLE_FOR_WIDGET + + class MIPS_Searcher(MipsAudit_Menu_Context): + def activate(self, ctx): + self.plugin.run() + return 1 + +except: + pass + + def printFunc(func_name): string1 = "========================================" - string2 = "========== Aduiting " + func_name + " " + string2 = "========== Auditing " + func_name + " " strlen = len(string1) - len(string2) return string1 + "\n" + string2 + '=' * strlen + "\n" + string1 def getFuncAddr(func_name): - func_addr = LocByName(func_name) + func_addr = idc.get_name_ea_simple(func_name) if func_addr != BADADDR: - print printFunc(func_name) - # print func_name + " Addr : 0x %x" % func_addr + print(printFunc(func_name)) return func_addr return False def getFormatString(addr): op_num = 1 - # GetOpType Return value + # idc.get_operand_type Return value #define o_void 0 // No Operand ---------- #define o_reg 1 // General Register (al, ax, es, ds...) reg #define o_mem 2 // Direct Memory Reference (DATA) addr @@ -103,15 +170,15 @@ def getFormatString(addr): #define o_idpspec4 12 // IDP specific type #define o_idpspec5 13 // IDP specific type # 如果第二个不是立即数则下一个 - if(GetOpType(addr ,op_num) != 5): + if(idc.get_operand_type(addr ,op_num) != 5): op_num = op_num + 1 - if GetOpType(addr ,op_num) != 5: + if idc.get_operand_type(addr ,op_num) != 5: return "get fail" - op_string = GetOpnd(addr, op_num).split(" ")[0].split("+")[0].split("-")[0].replace("(", "") - string_addr = LocByName(op_string) + op_string = print_operand(addr, op_num).split(" ")[0].split("+")[0].split("-")[0].replace("(", "") + string_addr = idc.get_name_ea_simple(op_string) if string_addr == BADADDR: return "get fail" - string = str(GetString(string_addr)) + string = str(get_strlit_contents(string_addr, -1, STRTYPE_TERMCHR)) return [string_addr, string] @@ -121,14 +188,14 @@ def getArgAddr(start_addr, regNum): count = 0 reg = "$a" + str(regNum) # try to get in the next - next_addr = Rfirst(start_addr) - if next_addr != BADADDR and reg == GetOpnd(next_addr, 0): + next_addr = get_first_cref_from(start_addr) + if next_addr != BADADDR and reg == print_operand(next_addr, 0): return next_addr # try to get before - before_addr = RfirstB(start_addr) + before_addr = get_first_cref_to(start_addr) while before_addr != BADADDR: - if reg == GetOpnd(before_addr, 0): - Mnemonics = GetMnem(before_addr) + if reg == print_operand(before_addr, 0): + Mnemonics = print_insn_mnem(before_addr) if Mnemonics[0:2] in mipscondition: pass elif Mnemonics[0:1] == "j": @@ -138,7 +205,7 @@ def getArgAddr(start_addr, regNum): count = count + 1 if count > scan_deep: break - before_addr = RfirstB(before_addr) + before_addr = get_first_cref_to(before_addr) return BADADDR @@ -146,22 +213,22 @@ def getArg(start_addr, regNum): mipsmov = ["move", "lw", "li", "lb", "lui", "lhu", "lbu", "la"] arg_addr = getArgAddr(start_addr, regNum) if arg_addr != BADADDR: - Mnemonics = GetMnem(arg_addr) + Mnemonics = print_insn_mnem(arg_addr) if Mnemonics[0:3] == "add": - if GetOpnd(arg_addr, 2) == "": - arg = GetOpnd(arg_addr, 0) + "+" + GetOpnd(arg_addr, 1) + if print_operand(arg_addr, 2) == "": + arg = print_operand(arg_addr, 0) + "+" + print_operand(arg_addr, 1) else: - arg = GetOpnd(arg_addr, 1) + "+" + GetOpnd(arg_addr, 2) + arg = print_operand(arg_addr, 1) + "+" + print_operand(arg_addr, 2) elif Mnemonics[0:3] == "sub": - if GetOpnd(arg_addr, 2) == "": - arg = GetOpnd(arg_addr, 0) + "-" + GetOpnd(arg_addr, 1) + if print_operand(arg_addr, 2) == "": + arg = print_operand(arg_addr, 0) + "-" + print_operand(arg_addr, 1) else: - arg = GetOpnd(arg_addr, 1) + "-" + GetOpnd(arg_addr, 2) + arg = print_operand(arg_addr, 1) + "-" + print_operand(arg_addr, 2) elif Mnemonics in mipsmov: - arg = GetOpnd(arg_addr, 1) + arg = print_operand(arg_addr, 1) else: arg = GetDisasm(arg_addr).split("#")[0] - MakeComm(arg_addr, "addr: 0x%x " % start_addr + "-------> arg" + str((int(regNum)+1)) + " : " + arg) + set_cmt(arg_addr, "addr: 0x%x " % start_addr + "-------> arg" + str((int(regNum)+1)) + " : " + arg, 0) return arg else: return "get fail" @@ -181,11 +248,10 @@ def audit(func_name): elif func_name in format_function_offset_dict: arg_num = format_function_offset_dict[func_name] + 1 else: - print "The %s function didn't write in the describe arg num of function array,please add it to,such as add to `two_arg_function` arary" % func_name + print("The %s function didn't write in the describe arg num of function array,please add it to,such as add to `two_arg_function` arary" % func_name) return - # mispcall = ["jal", "jalr", "bal", "jr"] table_head = ["func_name", "addr"] - for num in xrange(0,arg_num): + for num in range(0,arg_num): table_head.append("arg"+str(num+1)) if func_name in format_function_offset_dict: table_head.append("format&value[string_addr, num of '%', fmt_arg...]") @@ -193,47 +259,31 @@ def audit(func_name): table = PrettyTable(table_head) # get first call - call_addr = RfirstB(func_addr) + call_addr = get_first_cref_to(func_addr) while call_addr != BADADDR: - # set color ———— green (red=0x0000ff,blue = 0xff0000) - SetColor(call_addr, CIC_ITEM, 0x00ff00) - # set break point - # AddBpt(call_addr) - # DelBpt(call_addr) - - # if you want to use condition - # SetBptCnd(ea, 'strstr(GetString(Dword(esp+4),-1, 0), "SAEXT.DLL") != -1') - Mnemonics = GetMnem(call_addr) - # print "Mnemonics : %s" % Mnemonics - # if Mnemonics in mispcall: + idc.set_color(call_addr, idc.CIC_ITEM, 0x00ff00) + Mnemonics = print_insn_mnem(call_addr) if Mnemonics[0:1] == "j" or Mnemonics[0:1] == "b": - # print func + " addr : 0x%x" % call_addr if func_name in format_function_offset_dict: info = auditFormat(call_addr, func_name, arg_num) else: info = auditAddr(call_addr, func_name, arg_num) table.add_row(info) - call_addr = RnextB(func_addr, call_addr) - print table - # data_addr = DfirstB(func_addr) - # while data_addr != BADADDR: - # Mnemonics = GetMnem(data_addr) - # if DEBUG: - # print "Data Mnemonics : %s" % GetMnem(data_addr) - # print "Data addr : 0x %s" % data_addr - # data_addr = DnextB(func_addr, data_addr) + call_addr = get_next_cref_to(func_addr, call_addr) + print(table) def auditAddr(call_addr, func_name, arg_num): addr = "0x%x" % call_addr ret_list = [func_name, addr] # local buf size - local_buf_size = GetFunctionAttr(call_addr , FUNCATTR_FRSIZE) + local_buf_size = idc.get_func_attr(call_addr , idc.FUNCATTR_FRSIZE) if local_buf_size == BADADDR : + print("debug 236") local_buf_size = "get fail" else: local_buf_size = "0x%x" % local_buf_size # get arg - for num in xrange(0,arg_num): + for num in range(0,arg_num): ret_list.append(getArg(call_addr, num)) ret_list.append(local_buf_size) return ret_list @@ -242,13 +292,14 @@ def auditFormat(call_addr, func_name, arg_num): addr = "0x%x" % call_addr ret_list = [func_name, addr] # local buf size - local_buf_size = GetFunctionAttr(call_addr , FUNCATTR_FRSIZE) + local_buf_size = idc.get_func_attr(call_addr , idc.FUNCATTR_FRSIZE) if local_buf_size == BADADDR : + print("debug 252") local_buf_size = "get fail" else: local_buf_size = "0x%x" % local_buf_size # get arg - for num in xrange(0,arg_num): + for num in range(0,arg_num): ret_list.append(getArg(call_addr, num)) arg_addr = getArgAddr(call_addr, format_function_offset_dict[func_name]) string_and_addr = getFormatString(arg_addr) @@ -264,61 +315,86 @@ def auditFormat(call_addr, func_name, arg_num): # mips arg reg is from a0 to a3 if fmt_num > 3: fmt_num = fmt_num - format_function_offset_dict[func_name] - 1 - for num in xrange(0,fmt_num): + for num in range(0,fmt_num): if arg_num + num > 3: break format_and_value.append(getArg(call_addr, arg_num + num)) ret_list.append(format_and_value) - # format_string = str(getFormatString(arg_addr)[1]) - - # print " format String: " + format_string - # ret_list.append([string_addr]) ret_list.append(local_buf_size) return ret_list def mipsAudit(): # the word create with figlet - start = ''' - _ _ _ _ _ - _ __ ___ (_)_ __ ___ / \ _ _ __| (_) |_ -| '_ ` _ \| | '_ \/ __| / _ \| | | |/ _` | | __| -| | | | | | | |_) \__ \/ ___ \ |_| | (_| | | |_ -|_| |_| |_|_| .__/|___/_/ \_\__,_|\__,_|_|\__| - |_| - code by giantbranch 2018.05 - ''' - print start - print "Auditing dangerous functions ......" + print("Auditing dangerous functions ......") for func_name in dangerous_functions: audit(func_name) - print "Auditing attention function ......" + print("Auditing attention function ......") for func_name in attention_function: audit(func_name) - print "Auditing command execution function ......" + print("Auditing command execution function ......") for func_name in command_execution_function: audit(func_name) - print "Finished! Enjoy the result ~" - -# 判断架构的代码,以后或许用得上 -# info = idaapi.get_inf_structure() - -# if info.is_64bit(): -# bits = 64 -# elif info.is_32bit(): -# bits = 32 -# else: -# bits = 16 - -# try: -# is_be = info.is_be() -# except: -# is_be = info.mf -# endian = "big" if is_be else "little" + print("Finished! Enjoy the result ~") + +m_initialized = False + +class MipsAudit_Plugin_t(idaapi.plugin_t): + comment = "MIPS Audit plugin for IDA Pro" + help = "todo" + wanted_name = "mipsAudit" + wanted_hotkey = "Ctrl-Alt-M" + flags = idaapi.PLUGIN_KEEP + + def init(self): + global m_initialized + + # register popup menu handlers + try: + MIPS_Searcher.register(self, "mipsAudit") + except: + pass + + if m_initialized is False: + m_initialized = True + idaapi.register_action(idaapi.action_desc_t( + "mipsAudit", + "Find MIPS Audit func", + MIPS_Searcher(), + None, + None, + 0)) + idaapi.attach_action_to_menu("Search", "mipsAudit", idaapi.SETMENU_APP) + print("=" * 80) + start = ''' + _ _ _ _ _ + _ __ ___ (_)_ __ ___ / \ _ _ __| (_) |_ + | '_ ` _ \| | '_ \/ __| / _ \| | | |/ _` | | __| + | | | | | | | |_) \__ \/ ___ \ |_| | (_| | | |_ + |_| |_| |_|_| .__/|___/_/ \_\__,_|\__,_|_|\__| + |_| + code by giantbranch 2018.05 + edit by t3ls 2020.12 + ''' + print(start) + print("=" * 80) + + return idaapi.PLUGIN_KEEP + + def term(self): + pass + + def run(self, arg): + info = idaapi.get_inf_structure() + if 'mips' in info.procName: + mipsAudit() + else: + print('mipsAudit is not supported on the current arch') -# print 'Processor: {}, {}bit, {} endian'.format(info.procName, bits, endian) -# # Result: Processor: mipsr, 32bit, big endian +def PLUGIN_ENTRY(): + return MipsAudit_Plugin_t() -mipsAudit() \ No newline at end of file +if __name__ == '__main__': + mipsAudit() \ No newline at end of file From bb729e9447e5a5c4e0a55fef8b82b1f37e5fe937 Mon Sep 17 00:00:00 2001 From: t3ls Date: Fri, 18 Dec 2020 17:52:24 +0800 Subject: [PATCH 3/3] update readme --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 7441235..a8fea0d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # IDAPython mipsAudit +## 更新 2020.12 by t3ls + +- 重写了一些函数调用以兼容 IDA 7.5,已测试插件可正常运行在 IDA 7.0, 7.2, 7.5 版本上 + +- 依赖 `prettytable`,`pip3 install prettytable --target="D:\Program Files\IDA 7.5\python\3"` + +- 使用方式修改为: + + 1. 将 `mipsAudit.py` 拷贝到 `D:\Program Files\IDA 7.5\plugins` 目录 + + 2. 启动后在 Edit - Plugins 下点击 mipsAudit 即可(快捷键 Ctrl+Alt+M) + ## 简介 这是一个简单的IDAPython脚本。 @@ -57,6 +69,8 @@ command_execution_function = [ ## 使用 + + File - Script file ![1561006651468](./1561006651468.png)