__author__
=
"Tim 'diff' Strazzere"
__copyright__
=
"Copyright 2016, Red Naga"
__license__
=
"GPL"
__version__
=
"1.2"
__email__
=
[
"strazz@gmail.com"
]
from
idautils
import
*
from
idc
import
*
import
idaapi
import
sys
import
string
DEBUG
=
False
def
info(formatted_string):
print
formatted_string
def
error(formatted_string):
print
'ERROR - %s'
%
formatted_string
def
debug(formatted_string):
if
DEBUG:
print
'DEBUG - %s'
%
formatted_string
VALID_REGS
=
[
'eax'
,
'ebx'
,
'ebp'
,
'rax'
,
'rcx'
,
'r10'
,
'rdx'
]
VALID_DEST
=
[
'esp'
,
'eax'
,
'ecx'
,
'edx'
,
'rsp'
]
def
is_string_load(addr):
patterns
=
[]
if
(GetMnem(addr) !
=
'mov'
and
GetMnem(addr) !
=
'lea'
)
and
(GetOpType(addr,
1
) !
=
2
or
GetOpType(addr,
1
) !
=
5
)
or
GetOpnd(addr,
1
)[
-
4
:]
=
=
'_ptr'
:
return
False
if
idaapi.get_segm_name(GetOperandValue(addr,
1
))
is
None
:
return
False
if
GetOpnd(addr,
0
)
in
VALID_REGS
and
not
(
'['
in
GetOpnd(addr,
1
)
or
'loc_'
in
GetOpnd(addr,
1
))
and
((
'offset '
in
GetOpnd(addr,
1
)
or
'h'
in
GetOpnd(addr,
1
))
or
(
'unk'
=
=
GetOpnd(addr,
1
)[:
3
])):
from_reg
=
GetOpnd(addr,
0
)
addr_2
=
FindCode(addr, SEARCH_DOWN)
try
:
dest_reg
=
GetOpnd(addr_2,
0
)[GetOpnd(addr_2,
0
).index(
'['
)
+
1
:GetOpnd(addr_2,
0
).index(
'['
)
+
4
]
except
ValueError:
return
False
if
GetMnem(addr_2)
=
=
'mov'
and
dest_reg
in
VALID_DEST
and
(
'[%s'
%
dest_reg)
in
GetOpnd(addr_2,
0
)
and
GetOpnd(addr_2,
1
)
=
=
from_reg:
addr_3
=
FindCode(addr_2, SEARCH_DOWN)
if
GetMnem(addr_3)
=
=
'mov'
and
((
'[%s+'
%
dest_reg)
in
GetOpnd(addr_3,
0
)
or
GetOpnd(addr_3,
0
)
in
VALID_DEST)
and
'offset '
not
in
GetOpnd(addr_3,
1
)
and
'dword ptr ds'
not
in
GetOpnd(addr_3,
1
)
and
GetOpType(addr_3,
1
) !
=
1
and
GetOpType(addr_3,
1
) !
=
2
and
GetOpType(addr_3,
1
) !
=
4
:
try
:
dumb_int_test
=
GetOperandValue(addr_3,
1
)
if
dumb_int_test >
0
and
dumb_int_test < sys.maxsize:
return
True
except
ValueError:
return
False
return
False
def
create_string(addr, string_len):
if
idaapi.get_segm_name(addr)
is
None
:
debug(
'Cannot load a string which has no segment - not creating string @ 0x%02x'
%
addr)
return
False
debug(
'Found string load @ 0x%x with length of %d'
%
(addr, string_len))
if
GetStringType(addr)
is
not
None
and
GetString(addr)
is
not
None
and
len
(GetString(addr)) !
=
string_len:
debug(
'It appears that there is already a string present @ 0x%x'
%
addr)
MakeUnknown(addr, string_len, DOUNK_SIMPLE)
if
GetString(addr)
is
None
and
MakeStr(addr, addr
+
string_len):
return
True
else
:
MakeUnknown(addr, string_len, DOUNK_SIMPLE)
if
MakeStr(addr, addr
+
string_len):
return
True
debug(
'Unable to make a string @ 0x%x with length of %d'
%
(addr, string_len))
return
False
def
create_offset(addr):
if
OpOff(addr,
1
,
0
):
return
True
else
:
debug(
'Unable to make an offset for string @ 0x%x '
%
addr)
return
False
def
strings_init():
strings_added
=
0
retry
=
[]
text_seg
=
get_text_seg()
if
text_seg
is
None
:
debug(
'Failed to get text segment'
)
return
strings_added
for
addr
in
Functions(text_seg.startEA, text_seg.endEA):
name
=
GetFunctionName(addr)
end_addr
=
Chunks(addr).
next
()[
1
]
if
(end_addr < addr):
error(
'Unable to find good end for the function %s'
%
name)
pass
debug(
'Found function %s starting/ending @ 0x%x 0x%x'
%
(name, addr, end_addr))
while
addr <
=
end_addr:
if
is_string_load(addr):
if
'rodata'
not
in
idaapi.get_segm_name(addr)
and
'text'
not
in
idaapi.get_segm_name(addr):
debug(
'Should a string be in the %s section?'
%
idaapi.get_segm_name(addr))
string_addr
=
GetOperandValue(addr,
1
)
addr_3
=
FindCode(FindCode(addr, SEARCH_DOWN), SEARCH_DOWN)
string_len
=
GetOperandValue(addr_3,
1
)
if
create_string(string_addr, string_len):
if
create_offset(addr):
strings_added
+
=
1
else
:
retry.append((addr, string_addr, string_len))
addr
=
FindCode(addr_3, SEARCH_DOWN)
else
:
addr
=
FindCode(addr, SEARCH_DOWN)
for
instr_addr, string_addr, string_len
in
retry:
if
create_string(string_addr, string_len):
if
create_offset(instr_addr):
strings_added
+
=
1
else
:
error(
'Unable to make a string @ 0x%x with length of %d for usage in function @ 0x%x'
%
(string_addr, string_len, instr_addr))
return
strings_added
def
get_text_seg():
return
_get_seg([
'.text'
,
'__text'
])
def
get_gopclntab_seg():
return
_get_seg([
'.gopclntab'
,
'__gopclntab'
])
def
_get_seg(possible_seg_names):
seg
=
None
for
seg_name
in
possible_seg_names:
seg
=
idaapi.get_segm_by_name(seg_name)
if
seg:
return
seg
return
seg
def
is_simple_wrapper(addr):
if
GetMnem(addr)
=
=
'xor'
and
GetOpnd(addr,
0
)
=
=
'edx'
and
GetOpnd(addr,
1
)
=
=
'edx'
:
addr
=
FindCode(addr, SEARCH_DOWN)
if
GetMnem(addr)
=
=
'jmp'
and
GetOpnd(addr,
0
)
=
=
'runtime_morestack'
:
return
True
return
False
def
create_runtime_ms():
debug(
'Attempting to find runtime_morestack function for hooking on...'
)
text_seg
=
get_text_seg()
if
text_seg
is
None
:
debug(
'Failed to get text segment'
)
return
None
opcodes
=
'c7 05 03 10 00 00 00 00 00 00'
if
idaapi.get_inf_structure().is_64bit():
opcodes
=
'48 c7 04 25 03 10 00 00 00 00 00 00'
runtime_ms_end
=
idaapi.find_binary(text_seg.startEA, text_seg.endEA, opcodes,
0
, SEARCH_DOWN)
if
runtime_ms_end
=
=
BADADDR:
debug(
'Failed to find opcodes associated with runtime_morestack: %s'
%
opcodes)
return
None
runtime_ms
=
idaapi.get_func(runtime_ms_end)
if
runtime_ms
is
None
:
debug(
'Failed to get runtime_morestack function from address @ 0x%x'
%
runtime_ms_end)
return
None
if
idc.MakeNameEx(runtime_ms.startEA,
"runtime_morestack"
, SN_PUBLIC):
debug(
'Successfully found runtime_morestack'
)
else
:
debug(
'Failed to rename function @ 0x%x to runtime_morestack'
%
runtime_ms.startEA)
return
runtime_ms
def
traverse_xrefs(func):
func_created
=
0
if
func
is
None
:
return
func_created
func_xref
=
idaapi.get_first_cref_to(func.startEA)
while
func_xref !
=
BADADDR:
if
idaapi.get_func(func_xref)
is
None
:
func_end
=
FindCode(func_xref, SEARCH_DOWN)
if
GetMnem(func_end)
=
=
"jmp"
:
func_start
=
GetOperandValue(func_end,
0
)
if
func_start < func_xref:
if
idc.MakeFunction(func_start, func_end):
func_created
+
=
1
else
:
error(
'Error trying to create a function @ 0x%x - 0x%x'
%
(func_start, func_end))
else
:
xref_func
=
idaapi.get_func(func_xref)
if
is_simple_wrapper(xref_func.startEA):
debug(
'Stepping into a simple wrapper'
)
func_created
+
=
traverse_xrefs(xref_func)
if
idaapi.get_func_name(xref_func.startEA)
is
not
None
and
'sub_'
not
in
idaapi.get_func_name(xref_func.startEA):
debug(
'Function @0x%x already has a name of %s; skipping...'
%
(func_xref, idaapi.get_func_name(xref_func.startEA)))
else
:
debug(
'Function @ 0x%x already has a name %s'
%
(xref_func.startEA, idaapi.get_func_name(xref_func.startEA)))
func_xref
=
idaapi.get_next_cref_to(func.startEA, func_xref)
return
func_created
def
find_func_by_name(name):
text_seg
=
get_text_seg()
if
text_seg
is
None
:
return
None
for
addr
in
Functions(text_seg.startEA, text_seg.endEA):
if
name
=
=
idaapi.get_func_name(addr):
return
idaapi.get_func(addr)
return
None
def
runtime_init():
func_created
=
0
if
find_func_by_name(
'runtime_morestack'
)
is
not
None
:
func_created
+
=
traverse_xrefs(find_func_by_name(
'runtime_morestack'
))
func_created
+
=
traverse_xrefs(find_func_by_name(
'runtime_morestack_noctxt'
))
else
:
runtime_ms
=
create_runtime_ms()
func_created
=
traverse_xrefs(runtime_ms)
return
func_created
def
create_pointer(addr, force_size
=
None
):
if
force_size
is
not
4
and
(idaapi.get_inf_structure().is_64bit()
or
force_size
is
8
):
MakeQword(addr)
return
Qword(addr),
8
else
:
MakeDword(addr)
return
Dword(addr),
4
STRIP_CHARS
=
[
'('
,
')'
,
'['
,
']'
,
'{'
,
'}'
,
' '
,
'"'
]
REPLACE_CHARS
=
[
'.'
,
'*'
,
'-'
,
','
,
';'
,
':'
,
'/'
,
'\xb7'
]
def
clean_function_name(
str
):
str
=
filter
(
lambda
x: x
in
string.printable,
str
)
for
c
in
STRIP_CHARS:
str
=
str
.replace(c, '')
for
c
in
REPLACE_CHARS:
str
=
str
.replace(c,
'_'
)
return
str
def
renamer_init():
renamed
=
0
gopclntab
=
get_gopclntab_seg()
if
gopclntab
is
not
None
:
addr
=
gopclntab.startEA
+
8
size, addr_size
=
create_pointer(addr)
addr
+
=
addr_size
early_end
=
addr
+
(size
*
addr_size
*
2
)
while
addr < early_end:
func_offset, addr_size
=
create_pointer(addr)
name_offset, addr_size
=
create_pointer(addr
+
addr_size)
addr
+
=
addr_size
*
2
func_name_addr
=
Dword(name_offset
+
gopclntab.startEA
+
addr_size)
+
gopclntab.startEA
func_name
=
GetString(func_name_addr)
appended
=
clean_func_name
=
clean_function_name(func_name)
debug(
'Going to remap function at 0x%x with %s - cleaned up as %s'
%
(func_offset, func_name, clean_func_name))
if
idaapi.get_func_name(func_offset)
is
not
None
:
if
MakeName(func_offset, clean_func_name):
renamed
+
=
1
else
:
error(
'clean_func_name error %s'
%
clean_func_name)
return
renamed
def
pointer_renamer():
renamed
=
0
text_seg
=
get_text_seg()
if
text_seg
is
None
:
debug(
'Failed to get text segment'
)
return
renamed
for
addr
in
Functions(text_seg.startEA, text_seg.endEA):
name
=
GetFunctionName(addr)
data_ref
=
idaapi.get_first_dref_to(addr)
while
data_ref !
=
BADADDR:
if
'rodata'
in
idaapi.get_segm_name(data_ref):
if
'off_'
in
GetTrueName(data_ref):
if
MakeName(data_ref, (
'%s_ptr'
%
name)):
renamed
+
=
1
else
:
error(
'error attempting to name pointer @ 0x%02x for %s'
%
(data_ref, name))
data_ref
=
idaapi.get_next_dref_to(addr, data_ref)
return
renamed
def
main():
func_added
=
runtime_init()
info(
'Found and successfully created %d functions!'
%
func_added)
idaapi.autoWait()
renamed
=
renamer_init()
info(
'Found and successfully renamed %d functions!'
%
renamed)
pointers_renamed
=
pointer_renamer()
info(
'Found and successfully renamed %d function pointers!'
%
pointers_renamed)
strings_added
=
strings_init()
info(
'Found and successfully created %d strings!'
%
strings_added)
if
__name__
=
=
"__main__"
: main()