Created
December 4, 2017 14:33
-
-
Save ca0s/9626af622e7b1ed9d7cd1ffe8dabdd05 to your computer and use it in GitHub Desktop.
Simple RTF tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python2 | |
import argparse | |
import olefile | |
import struct | |
import sys | |
def parse_objdata(obj): | |
cobj = obj.lstrip('\\') | |
cobj = cobj.lstrip("objdata ") | |
hobj = cobj.decode("hex") | |
hversion = hobj[:4] | |
hunk1 = hobj[4:8] | |
hsignlen = hobj[8:12] | |
p = 0 | |
oleversion = hobj[p : p + 4] | |
p += 4 | |
formatid = hobj[p : p + 4] | |
p += 4 | |
classname_len = struct.unpack("<I", hobj[p : p + 4])[0] | |
p += 4 | |
classname = hobj[p : p + classname_len] | |
p += classname_len | |
topicname_len = struct.unpack("<I", hobj[p : p + 4])[0] | |
p += 4 | |
topicname = hobj[p : p + topicname_len] | |
p += topicname_len | |
itemname_len = struct.unpack("<I", hobj[p : p + 4])[0] | |
p += 4 | |
itemname = hobj[p : p + itemname_len] | |
p += itemname_len | |
datasize = struct.unpack("<I", hobj[p : p + 4])[0] | |
p += 4 | |
data = hobj[p : p + datasize] | |
p += datasize | |
ending = hobj[p : ] | |
return { | |
'version': oleversion, | |
'formatid': formatid, | |
'classname_len': classname_len, | |
'classname': classname, | |
'topicname_len': topicname_len, | |
'topicname': topicname, | |
'itemname_len': itemname_len, | |
'itemname': itemname, | |
'datalen': datasize, | |
'data': data, | |
'ending': ending | |
} | |
def create_objdata(data): | |
buf = 'objdata ' | |
# version | |
buf += data['version'].encode("hex") | |
#formatid | |
buf += data['formatid'].encode("hex") | |
# classname_len | |
buf += struct.pack("<I", len(data['classname'])).encode("hex") | |
# classname | |
buf += data['classname'].encode("hex") | |
# topicname_len + empty topicname | |
buf += '00000000' | |
# itemname_len + empty itemname | |
buf += '00000000' | |
# data len | |
buf += struct.pack("<I", len(data['data'])).encode("hex") | |
# data | |
buf += data['data'].encode("hex") | |
# ending | |
buf += data['ending'].encode("hex") | |
return buf | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='RTF object mod tool') | |
parser.add_argument('--input', type=str, help='Input RTF file', required=True) | |
parser.add_argument('--output', type=str, help='Output RTF file') | |
parser.add_argument('--list', help='List objets in input RTF file', action='store_true') | |
parser.add_argument('--dump', help='Print the contents of the RTF file in a human-friendly format', action='store_true') | |
parser.add_argument('--replace', type=int, help='Object index to replace') | |
parser.add_argument('--withfile', type=str, help='File path of the new contents') | |
args = parser.parse_args() | |
if args.replace: | |
if (not args.output) or (not args.withfile): | |
parser.print_help() | |
sys.exit(-1) | |
try: | |
srcfd = open(args.input, "rb") | |
except: | |
print 'Could not open input file' | |
sys.exit(-1) | |
src = srcfd.read() | |
srcfd.close() | |
if args.replace: | |
try: | |
rplfd = open(args.withfile, "rb") | |
except: | |
print 'Could not open replacement file' | |
sys.exit(-1) | |
rpl = rplfd.read() | |
rplfd.close() | |
outbuf = '' | |
objcount = 0 | |
level = 0 | |
acc = '' | |
def handle_acc(): | |
global args, acc, level, rpl, objcount, outbuf | |
ret = acc | |
if acc.startswith("objdata"): | |
objdata = parse_objdata(acc) | |
if args.list: | |
print '[+] Found object - index %s' % objcount | |
print '\tSignLen: %s - Sign: %s - DataLen: %s' % (objdata['classname_len'], objdata['classname'], objdata['datalen']) | |
print '\tReal data len: %s - Data starts with: %s' % (len(objdata['data']), objdata['data'][:16].encode("hex")) | |
ole = olefile.OleFileIO(objdata['data']) | |
try: | |
for s in ole.listdir(): | |
sname = s[0] | |
print '\t\tStream/storage: %s (%s)' % (sname, sname.encode("hex")) | |
sdata = ole.openstream(sname).read() | |
print '\t\tStarts with: %s' % sdata[:10].encode("hex") | |
except: | |
print '\t\tOLE file seems to be malformed' | |
if args.replace == objcount: | |
no = dict(objdata) | |
no['datalen'] = len(rpl) | |
no['data'] = rpl | |
newobj = create_objdata(no) | |
ret = newobj | |
objcount += 1 | |
if acc != '': | |
if args.dump: | |
print level * '\t' + '\\' + acc | |
acc = '' | |
outbuf += ret | |
return ret | |
for b in src: | |
if b == '{': | |
handle_acc() | |
level += 1 | |
outbuf += b | |
elif b == '\\': | |
handle_acc() | |
outbuf += b | |
elif b == '}': | |
handle_acc() | |
outbuf += b | |
level -= 1 | |
if level == 0: | |
break | |
else: | |
acc += b | |
if args.replace: | |
try: | |
fo = open(args.output, "wb") | |
fo.write(outbuf) | |
fo.close() | |
print '[+] Replaced' | |
except: | |
print 'Could not open output file' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment