#!/usr/bin/env python """ (c) Immunity, Inc. 2004-2007 U{Immunity Inc.} MOSDEF utils for non-CANVAS users """ __VERSION__ = '1.0' # TODO check: # ----------- # cparse: dInt # spark: prettyprint # x86opcodes: issignedbyte, intel_byte, intel_2byte # pelib: hexdump # mosdef: isprint, strisprint # makeexe: binstring? import sys, os #try: # from internal import * #except: def __ignore(*args, **kargs): return False def __retsamearg(arg): return arg devlog = __ignore isdebug = __ignore warnings_safely_ignore = __ignore warning_restore = __ignore deprecate = __ignore uniqlist = __retsamearg ##################################################### # # # dictionary class that hold floats as integers # # ##################################################### import types class antifloatdict(types.DictType): def __init__(self, arg = {}): if type(arg) == types.DictType: d = {} for item in arg.items(): d.__setitem__(item[0], item[1]) arg = d return types.DictType.__init__(self, arg) def __setitem__(self, itemname, itemvalue): if type(itemvalue) == types.FloatType: itemvalue = int(itemvalue) return types.DictType.__setitem__(self, itemname, itemvalue) def __getitem__(self, itemname): item = types.DictType.__getitem__(self, itemname) if type(item) == types.FloatType: item = int(item) return item def copy(self): return antifloatdict(self) def hasbadchar(word,badchars): try: wordstr=intel_order(word) except: wordstr=str(word) for ch in badchars: if wordstr.count(ch): return 1 return 0 ##################################################### # # # little/big endian management functions # # ##################################################### def check_bits_consistancy(bits): assert not bits % 8, "bits should be sizeof(char) aligned, got %d" % bits def check_string_len(s, l, assertmsg=""): if assertmsg != "": assertmsg += "\n" assert len(s) >= l, "%sexpecting a at_least_%d_chars string, got %d_chars instead.\nstring is: %s" % \ (assertmsg, l, len(s), prettyprint(s)) def split_int_bits(bits, i): check_bits_consistancy(bits) # we cast to uint_bits here to be sure to return (bits/8) x uint8 u = uint_bits(bits, i) r = [] for b in range(0, bits, 8): r += [ (u >> (bits - (b + 8))) & 0xff ] return r # 0x12345678 -> [0x12, 0x34, 0x56, 0x78] def split_int32(int32): return split_int_bits(32, int32) def int2list_bits(bits, i, swap=0): check_bits_consistancy(bits) l = split_int_bits(bits, i) #devlog("int2list: l = %s" % l) lc = [] for n in l: #devlog("int2list: n = 0x%x" % n) lc += [chr(n)] if swap: lc.reverse() return lc def int2list32(int32, swap=0): return int2list_bits(32, int32, swap=swap) #def int2list(int32): # deprecate("use int2list32 instead") # return int2list32(int32) def int2str_bits(bits, i, swap=0): check_bits_consistancy(bits) return "".join(int2list_bits(bits, i, swap=swap)) def int2str32(int32, swap=0): return int2str_bits(32, int32, swap=swap) def int2str16(int16, swap=0): return int2str_bits(16, int16, swap=swap) def int2str32_swapped(int32): return int2str_bits(32, int32, swap=1) def int2str16_swapped(int16): return int2str_bits(16, int16, swap=1) #def int2str(int32): # deprecate("use int2str32 instead") # return int2str32(int32) def str2int_bits(bits, s): check_bits_consistancy(bits) assert type(s) == type(""), "str2int_bits() expects a string argument, got %s" % type(s) nchars = bits / 8 check_string_len(s, nchars, "str2int_bits(%d, s): string=<%s> len=%d" % (bits, s, len(s))) r = 0 warnings_safely_ignore(FutureWarning) for i in range(0, nchars): #print "%d = %x << %d" % (ord(s[i]) << 8*i, ord(s[i]), 8*i) r += ord(s[nchars-i-1]) << 8*i warning_restore() return r def str2int_bits_swapped(bits, s): check_string_len(s, bits/8) return byteswap_bits(bits, str2int_bits(bits, s)) def str2int16(s): return str2int_bits(16, s) def str2int32(s): return str2int_bits(32, s) def str2int64(s): return str2int_bits(64, s) def str2int16_swapped(s): return str2int_bits_swapped(16, s) def str2int32_swapped(s): return str2int_bits_swapped(32, s) def str2int64_swapped(s): return str2int_bits_swapped(64, s) # "\x12\x34\x56\x78" -> 0x12345678 #def str2int32_old(s): # #return str2int_bits(32, s) # assert type(s) == type(""), "str2int32() expects a string argument, got %s" % type(s) # if len(s) < 4: # devlog("str2int32: string=<%s> len=%d" % (s, len(s))) # raise AssertionError, "str2int32 called with a less_than_4_chars string (%d chars)" % len(s) # (a,b,c,d)=(ord(s[0]),ord(s[1]),ord(s[2]),ord(s[3])) # return sint32((a << 24) + (b << 16) + (c << 8) + d) #returns the integer that the 4 byte string represents #Note: If you are getting OverflowError in this function, you need to upgrade to Python #2.2. !! def str2bigendian(astring): """ oppposite of istr2int """ return str2int32(astring) # >>> print "0x%x" % str2littleendian("\x12\x34\x56\x78") # 0x78563412 def str2littleendian(astring): return byteswap_32(str2int32(astring)) def byteswap_bits(bits, i): check_bits_consistancy(bits) r = 0 warnings_safely_ignore(FutureWarning) for b in range(0, bits, 8): r += (((i >> b) & 0xff) << (bits - (b + 8))) warning_restore() return r def byteswap_64(int64): return byteswap_bits(64, int64) def byteswap_32(int32): return byteswap_bits(32, int32) def byteswap_16(int16): return byteswap_bits(16, int16) """ istr2halfword(halfword2bstr(dInt(x))) == byteswap_16(x) """ ##################################################### # # # print crap nicely # # ##################################################### #wee little function for printing strings nicely def hexprint(s): if not type(s) == type(""): return "can not hexdump %s" % type(s) tmp="" for c in s: tmp+="[0x%2.2x]"%ord(c) return tmp goodchars=".()~!#$%^&*()-=_/\\:<>" #let's not mess up our tty def prettyprint(instring): import string if not type(instring) == type(""): devlog("prettyprint got %s and not string" % type(instring)) instring = str(instring) #return "can not prettyprint %s" % type(instring) tmp="" for ch in instring: #if (ch.isalnum() or ch in goodchars) and ord(ch)<127: if ch in string.printable and ch not in ["\x0c"]: tmp+=ch else: value="%2.2x" % ord(ch) tmp+="["+value+"]" return tmp def c_array(data, desc = None): if not type(data) == type(""): devlog("c_array() got %s and not string" % type(data)) return "c_array() can not dump %s" % type(data) if not len(data): return "c_array() got void buffer" ucharbuf = "unsigned char buf[] = \"" for uchar in data: ucharbuf += "\\x%02x" % ord(uchar) ucharbuf += "\"; // %d byte" % len(data) if len(data) > 1: ucharbuf += "s" if desc: ucharbuf += ", %s" % desc return ucharbuf def shellcode_dump(sc, align=0, alignpad=" ", alignmax=16, mode=None): import types assert type(align) == type(0), "error in arguments, expecting an int for 'align'" if not type(sc) in [types.StringType, types.BufferType]: devlog("shellcode_dump() got %s and not string" % type(sc)) return type(sc) if not len(sc): return "void buffer" if mode and mode.upper() == "RISC": align=4 alignmax=4 if align: alignmax *= align buf = "" i = 0 for c in sc: buf += "%02x " % ord(c) if align and (i % align) == (align - 1): buf += alignpad if alignmax and (i % alignmax) == (alignmax - 1): buf += "\n" i += 1 if buf[-1] == "\n": buf = buf[:-1] return buf def dummywrite(fd, data): """ we just want to write some data on any fd, opened or closed. """ import os try: os.write(fd, data) except OSError, errargs: import errno if errargs.errno != errno.EBADF: raise def warnmsg(msg): sys.stderr.write("WARNING: %s\n" % msg) ##################################################### # # # return a binary representation of an integer # # ##################################################### def binary_string_bits(bits, i): binstr = "" for bit in range(0, bits): if i & (long(1) << bit): binstr = "1" + binstr else: binstr = "0" + binstr return binstr def binary_string_int8(int8): return binary_string_bits(8, int8) def binary_string_int16(int16): return binary_string_bits(16, int16) def binary_string_int32(int32): return binary_string_bits(32, int32) def binary_string_int64(int64): return binary_string_bits(64, int64) def binary_string_char(c): return binary_string_int8(c) def binary_string_short(s): return binary_string_int16(s) def binary_string_int(i): return binary_string_int32(i) ##################################################### # # # how to handle python fucking integers # # ##################################################### def dInt(sint): """ Turns sint into an int, hopefully python's int() doesn't handle negatives with base 0 well """ if sint==None or type(sint) in [type( (1,1) ), type( [1]), type( {} ) ]: devlog("Type ERROR: dInt(%s)!"%str(sint)) #should we call bugcheck here? raise TypeError, "type %s for dInt(%s)" % (type(sint), str(sint)) s=str(sint) if s[0:2]=="0x": return long(s,0) else: #if you have long("5.0") it throws a horrible exception #so we convert to float and then back to long to avoid this return long(float(s)) def binary_from_string(astr,bits=None): """ returns [1,0,0,0,0,0,0,0] from "\x80" """ if not bits: #print "Setting bits to 8*length" bits=len(astr)*8 ret=[] for c in astr: #for each character mask=0x80 for i in range(0,8): #for each bit in the character if mask & ord(c): bit=1 else: bit=0 ret+=[bit] if len(ret)==bits: break mask=mask >> 1 return ret def b(mystr): mydict={"1":1,"0":0} tmp=0 for c in mystr: value=mydict[c] tmp=(tmp<<1)+value return tmp # Note: this is a 5m lame function def hexdump(buf): tbl=[] tmp="" hex="" i=0 for a in buf: hex+="%02X "% ord(a) i+=1 if ord(a) >=0x20 and ord(a) <0x7f: tmp+=a else: tmp+="." if i%16 == 0: tbl.append((hex, tmp)) hex="" tmp="" tbl.append((hex, tmp)) return tbl def prettyhexprint(s,length=8): """ A nicely displayed hexdump as a string """ # we are expecting a string here if not type(s) == type(""): return "can not hexdump %s" % type(s) tmp=[] i=1 for c in s: tmp+=["%2.2x "%ord(c)] if i%length==0: tmp+=["\n"] i+=1 return "".join(tmp) # generic functions for integers def sint_is_signed(bits, c): return uint_bits(bits, c) >> (bits - 1) def uint_bits(bits, c): # WARNING i dunno if dInt is safe here c=dInt(c) # [Python < 2.4] FutureWarning: x<> b: b += 1 return b # a.k.a. MACROS for integers def uint8(c): return uint_bits(8, c) def uint16(c): return uint_bits(16, c) def uint32(c): return uint_bits(32, c) def uint64(c): return uint_bits(64, c) def sint16(c): return sint_bits(16, c) def sint32(c): return sint_bits(32, c) def sint64(c): return sint_bits(64, c) def uint8fmt(c): return uintfmt_bits(8, c) def uint16fmt(c): return uintfmt_bits(16, c) def uint32fmt(c): return uintfmt_bits(32, c) def uint64fmt(c): return uintfmt_bits(64, c) def sint16fmt(c): return sintfmt_bits(16, c) def sint32fmt(c): return sintfmt_bits(32, c) def sint64fmt(c): return sintfmt_bits(64, c) def IsInt(str): """ Checks for integer, hex or no """ try: num = int(str,0) return 1 except ValueError: return 0 ##################################################### # # # old functions [ now deprecated ] # # ##################################################### # def signedshort(i): deprecate("use sint16() instead") return sint16(i) def big2int(big): deprecate("use sint32() instead") return sint32(big) def int2uns(small): assert sys.version_info[0] >= 2 and (sys.version_info[0] == 2 and sys.version_info[1] >= 4), \ "\nyou tried to call int2uns() but your python %d.%d is too old to handle it correctly\n" \ "Python versions before 2.4 are fucked up with integers, rely on 2.4 only!" % \ (sys.version_info[0], sys.version_info[1]) deprecate("use uint32() instead") return uint32(small) def istr2halfword(astring): #deprecate("use str2int16_swapped() instead") return str2int16_swapped(astring) def nstr2halfword(astring): #deprecate("use str2int16() instead") return str2int16(astring) #def intel_str2int_old(astring): # if len(astring) < 4: # devlog("intel_str2int: astring=<%s> len=%d" % (astring, len(astring))) # raise AssertionError, "intel_str2int called with a less_than_4_chars string" # # (a,b,c,d)=(ord(astring[0]),ord(astring[1]),ord(astring[2]),ord(astring[3])) # #print "%x:%x:%x:%x"%(a,b,c,d) # result=a # result=result+b*256 # result=result+c*65536 # result=result+d*16777216 # #change 2 int type, if long # result=uint32(result) # return result # def intel_str2int(astring): deprecate("use str2littleendian instead") return str2littleendian(astring) #just a nice short wrapper def istr2int(astring): #devlog("istr2int(%s)" % astring) return str2littleendian(astring) #def halfword2istr(halfword): # data="" # a=halfword & 0xff # b=halfword/256 & 0xff # data+=chr(a)+chr(b) # return data # #def halfword2bstr(halfword): # data="" # a=halfword & 0xff # b=halfword/256 & 0xff # data+=chr(b)+chr(a) # return data # #def short2bigstr(short): # """ # changes an int to a two byte big endian string # """ # data="" # #short=uint16(short) # #print "short=%x /256=%x"%(short,short/256) # data+=chr(short / 256) # data+=chr(short & 0xff) # return data """ >>> print hexprint(halfword2bstr(0x1234)) [0x12][0x34] >>> print hexprint(short2bigstr(0x1234)) [0x12][0x34] >>> print hexprint("".join(int2list(uint16(0x1234))[2:4])) [0x12][0x34] >>> print hexprint(halfword2istr(0x1234)) [0x34][0x12] >>> print hexprint("".join(int2list(byteswap_16(uint16(0x1234)))[2:4])) [0x34][0x12] >>> print uint16fmt(istr2halfword(halfword2bstr(dInt(0x1234)))) 0x3412 >>> print uint16fmt(byteswap_16(0x1234)) 0x3412 >>> print hexprint(halfword2bstr(0x1234)) [0x12][0x34] >>> print hexprint(int2str_bits(16, 0x1234)) [0x12][0x34] >>> print hexprint(halfword2bstr(0x12345678)) [0x56][0x78] >>> print hexprint(int2str_bits(16, 0x12345678)) [0x56][0x78] >>> print hexprint(int2str16(0x1234)) [0x12][0x34] >>> print hexprint(int2str16(0x1234, swap=1)) [0x34][0x12] >>> print hexprint(int2str16_swapped(0x1234)) [0x34][0x12] """ def halfword2istr(halfword): #deprecate("use int2str16_swapped instead") return int2str16_swapped(halfword) def halfword2bstr(halfword): #deprecate("use int2str16 instead") return int2str16(halfword) def short2bigstr(short): return halfword2bstr(short) def intel_short(halfword): return halfword2istr(halfword) def big_short(short): return short2bigstr(short) #def big_order_old(myint): # """ # Opposite of str2bigendian # """ # str="" # a=chr(myint % 256) # myint=myint >> 8 # b=chr(myint % 256) # myint=myint >> 8 # c=chr(myint % 256) # myint=myint >> 8 # d=chr(myint % 256) # # str+="%c%c%c%c" % (d,c,b,a) # return str ##int to intelordered string conversion #def intel_order_old(myint): # #struct.pack is non-intuitive for non-python programers, which is why I do this sort of thing. # #it's for people who wish they were using perl, imo. # str="" # a=chr(myint % 256) # myint=myint >> 8 # b=chr(myint % 256) # myint=myint >> 8 # c=chr(myint % 256) # myint=myint >> 8 # d=chr(myint % 256) # # str+="%c%c%c%c" % (a,b,c,d) # # return str def big_order(int32): """ Opposite of str2bigendian """ #deprecated("use int2str32() instead") return int2str32(int32) def intel_order(int32): """ bijection of str2littleendian() """ #deprecated("use int2str32_swapped() instead") return int2str32_swapped(int32) #def binary_string_long(l): # return binary_string_int64(l) #def print_binary_old(myint): # tmp="" # for i in range(0,32): # if (long(1)< 0: for a in range(0,32): ret = str(num&0x1) + ret num = num >> 1 return ret # ##################################################### # # # test ... # # ##################################################### if __name__=="__main__": warnings_safely_ignore(FutureWarning) def test(funcname): print "testing %s() ..." % funcname print "running tests..." test("split_int32") assert split_int32(0x12345678) == [0x12, 0x34, 0x56, 0x78] test("str2int16") assert str2int16('\x12\x34\x56') == 0x1234 assert nstr2halfword('\x12\x34\x56\x78') == 0x1234 #DEPRECATED test("str2int16_swapped") assert str2int16_swapped('\x12\x34') == 0x3412 assert istr2halfword('\x12\x34') == 0x3412 #DEPRECATED assert str2int16_swapped('\x12\x34\x56\x78') == 0x3412 test("str2littleendian") assert str2littleendian('\x12\x34\x56\x78') == 0x78563412 assert intel_str2int('\x12\x34\x56\x78') == 0x78563412 #DEPRECATED assert istr2int('\x12\x34\x56\x78') == 0x78563412 #DEPRECATED test("str2bigendian/str2int32") assert str2int32('\x12\x34\x56\x78') == 0x12345678 assert str2bigendian('\x12\x34\x56\x78') == 0x12345678 test("int2str16") assert int2str16(0x1234) == '\x12\x34' assert halfword2bstr(0x1234) == '\x12\x34' #DEPRECATED assert short2bigstr(0x1234) == '\x12\x34' #DEPRECATED assert big_short(0x1234) == '\x12\x34' #DEPRECATED test("int2str16_swapped") assert int2str16_swapped(0x1234) == '\x34\x12' assert halfword2istr(0x1234) == '\x34\x12' #DEPRECATED assert intel_short(0x1234) == '\x34\x12' #DEPRECATED assert intel_short(0x12345678) == '\x78\x56' #DEPRECATED test("int2str32") assert int2str32(0x12345678) == '\x12\x34\x56\x78' assert big_order(0x12345678) == '\x12\x34\x56\x78' #DEPRECATED test("int2str32_swapped") assert int2str32_swapped(0x12345678) == '\x78\x56\x34\x12' assert intel_order(0x12345678) == '\x78\x56\x34\x12' #DEPRECATED test("binary_string_int") assert print_binary(0x12345678) == '00010010001101000101011001111000' test("binary_string_int") assert binary_string_short(0x12345678) == '0101011001111000' try: assert int2uns(-1) == 0xffffffffL #DEPRECATED except AssertionError: print "[!] failed: int2uns(-1) == 0xffffffff" assert sys.version_info[0] >= 2, "word, what an old Python you have :/" if sys.version_info[0] == 2 and sys.version_info[1] < 4: print "Python 2.3 integers are fucked up, rely on 2.4 only!" print "your version can not handle int2uns() correctly" pass else: raise test("uint16") assert uint16(0xffff) == 0xffff assert uint16(0x12345678) == 0x5678 test("sint16") assert sint16(0xffff) == -1 assert sint16(0xffff) == sint16(-1) assert signedshort(0xffff) == -1 #DEPRECATED test("sint32") assert sint32(-1) == -1 assert big2int(0x123456789) == 0x23456789 #DEPRECATED test("uintfmt_bits") assert uintfmt_bits(32, 0x12345678) == '0x12345678' assert uintfmt_bits(32, 0x1234) == '0x00001234' assert uintfmt_bits(24, 0x1234) == '0x00001234' assert uintfmt_bits(16, 0x1234) == '0x1234' test("uint16fmt") assert uint16fmt(0x123456) == '0x3456' assert uint16fmt(-0x123456) == '0xcbaa' test("uint32fmt") assert uint32fmt(0x1234) == '0x00001234' test("uint64fmt") assert uint64fmt(0x12345678) == '0x0000000012345678' assert uint64fmt(-1) == '0xffffffffffffffff' test("sint16fmt") assert sint16fmt(0x1234) == '0x1234' assert sint16fmt(-0x1234) == '-0x1234' assert sint16fmt(-0x12345678) == '-0x5678' # TODO check that #assert sint16fmt(0xffff) == '-0x0001' test("sint32fmt") assert sint32fmt(0x1234) == '0x00001234' assert sint32fmt(-0x1234) == '-0x00001234' test("sint64fmt") assert sint64fmt(-1) == '-0x0000000000000001' test("byteswap_32") assert byteswap_32(0x12345678) == 0x78563412 test("byteswap_64") assert byteswap_64(0x1234567890123456) == 0x5634129078563412 #print "0f=%s"%uint8fmt(0xf) assert uint8fmt(0x0f) == '0x0f' print "done."