Skip to content

Instantly share code, notes, and snippets.

@ca0s
Created December 4, 2017 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ca0s/9626af622e7b1ed9d7cd1ffe8dabdd05 to your computer and use it in GitHub Desktop.
Save ca0s/9626af622e7b1ed9d7cd1ffe8dabdd05 to your computer and use it in GitHub Desktop.
Simple RTF tool
#!/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