This article deals with another way to reuse ASM instruction based on GDB script. The idea is to launch a executable in a sandbox and control its flow.
GDB provides python API. We can use it thougth Cygwin on Windows. In the laster version GDB in Cygwin don’t contain the gdb server. But we can get an old package (6.8) provide the server. It’s still available on some mirror:
More information about “Scripting GDB using Python” is available on the GDB website: http://sourceware.org/gdb/onlinedocs/gdb/Python.html
We will script gdb in order to decoded string in the main module flame
md5 : bdc9e04388bda8527b398a8c34667e18
In the main module of flamer, we can distinguish two functions which seem to be decoders:
This two functions are very similar, receive a structure as argument and return a string. The argument in function decode1 will be load into ebx and the argument will be load into esi in decode2.
To obtain a list of argument addresses, we can research in the code each function call and read addresse pushed on the stack. In this case, the argument are pushed in two way:
We make the following script to gather addresses into a file. The script use the disassembler library diStorm and pefile.
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import distorm3
import pefile
addrs_f = open('listAddrDecode1.txt', 'w')
def getAddrArg(instruction,i,i_match):
for j in range(i-1,i_match,-1):
(offset,size,instr,hexdump) = instruction[j]
if instr[:4]=='PUSH':
res = ''
res = instr.split(' ')[2]
if res[:2] != "0x":
return None
return res
elif instr[:16] == "MOV DWORD [ESP],":
return instr.split(' ')[3]
return None
def main():
pe = pefile.PE("bdc9e04388bda8527b398a8c34667e18")
pe_memory_mapped = pe.get_memory_mapped_image()
baseAddr = pe.OPTIONAL_HEADER.ImageBase
sect_text = pe.sections[0]
# Load code section
code = pe_memory_mapped[\
sect_text.PointerToRawData:\
sect_text.PointerToRawData+sect_text.SizeOfRawData]
instruction = distorm3.Decode(baseAddr+sect_text.PointerToRawData,\
code,distorm3.Decode32Bits)
nb_instruction = len(instruction)
i_match = 0
addrs=[]
for i in range(len(instruction)):
(offset,size,instr,hexdump) = instruction[i]
# Stop on decode1 call
if instr == 'CALL 0x1000e431':
# Attempt to get the argument
addr=getAddrArg(instruction,i,i_match)
if addr not in addrs:
addrs.append(addr)
i_match=i
addrs.sort()
for addr in addrs:
addrs_f.write("%s\n"%addr)
if __name__=="__main__":
main()
We make a GDB script which reads a addresses file, call decode1 function save the result.
#/usr/bin/python
# -*- coding:UTF-8 -*-
import gdb
import struct
class flameDecode1(gdb.Command):
def __init__(self):
super(flameDecode1, self).__init__("flameDecode1",\
gdb.COMMAND_OBSCURE,\
gdb.COMPLETE_NONE,\
True)
def invoke(sel, arg, from_tty):
addrs_f = open("listAddrDecode1.txt","r")
result_f = open("resultDecode1.txt","w")
# Breakpoint after mov argument into ebx
gdb.Breakpoint("*0x1000E442").silent=True
# Breakpoint at the end of decode1 function
gdb.Breakpoint("*0x1000E476").silent=True
inferior = gdb.selected_inferior()
addrs = addrs_f.readlines()
for addr in addrs:
addr = addr.replace("\n",'')
#addr = addr.split(' ')[0]
# Set EIP at the beginning of decode1 function
gdb.execute("set $eip=0x1000E431")
gdb.execute("c")
# Replace ebx with the desired argument
gdb.execute("set $ebx=%s"%addr)
gdb.execute("c")
string=""
i=0
# Save the decoded string
result_addr=int(str(gdb.parse_and_eval("$eax")))
while i<64:
res=inferior.read_memory(result_addr, 1)
if ord(res[0]) == 0:
break
else:
string+=res[0]
i+=1
result_addr+=1
string=string.replace("\n","\\n")
string=string.replace("\t","\\t")
string=string.replace("\r","\\r")
result_f.write("%s %s\n"%(addr,string))
flameDecode1()
Before start gdb server in the sandbox, we make sure that the binary hasn’t DLL flaged with LordPE.
We start the gdb server:
user@malware-lu ~
$ gdbserver.exe host:1234 /cygdrive/c/bdc9e04388bda8527b398a8c34667e18
Process /cygdrive/c/bdc9e04388bda8527b398a8c34667e18 created; pid = 732
Listening on port 1234
We connect to the remote target and execute the GDB script:
(gdb) target remote 192.168.56.101:1234
Remote debugging using 192.168.56.101:1234
0x7c91120f in ?? ()
(gdb) source script/gdb_flame_decode1.py
(gdb) flameDecode1
Breakpoint 1 at 0x1000e442
Breakpoint 2 at 0x1000e476
(gdb) shell head resultDecode1.txt
0x10256c54 vsmon.exe
0x10256c74 zlclient.exe
0x10256c98 ProductName
0x10256cb8 ZoneAlarm
0x10256cd8 vsmon.exe
0x10256cf8 zlclient.exe
0x10256d1c CurrentVersion
0x10256d40 SOFTWARE\Zone Labs\ZoneAlarm
0x10256d74 SOFTWARE\Zone Labs\ZoneAlarm\Registration\
0x10256db4 SOFTWARE\Zone Labs\ZoneAlarm\Registration\