mirror of
https://github.com/vee1e/flare-floss.git
synced 2026-09-01 17:57:06 +00:00
Replace the header from source code files using the following script:
```Python
for dir_path, dir_names, file_names in os.walk("flare-floss"):
for file_name in file_names:
try:
file_path = f"{dir_path}/{file_name}"
f = open(file_path, "rb+")
content = f.read()
m = re.search(OLD_HEADER, content)
if not m:
continue
print(f"{file_path}: {m.group('year')}")
content = content.replace(m.group(0), NEW_HEADER % m.group("year"))
f.seek(0)
f.write(content)
except:
continue
```
Some files had the copyright headers inside a `"""` comment and needed
manual changes before applying the script.
The old header had the confusing sentence `All rights reserved`, which
does not make sense for an open source license. Replace the header by
the default Google header that corrects this issue and keep floss
consistent with other Google projects.
234 lines
7.7 KiB
Python
234 lines
7.7 KiB
Python
# Copyright 2017 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
#!/usr/bin/env python3
|
|
"""
|
|
Run FLOSS to automatically extract obfuscated strings and apply them to the
|
|
currently loaded module in IDA Pro.
|
|
|
|
author: Willi Ballenthin
|
|
email: willi.ballenthin@gmail.com
|
|
"""
|
|
import os
|
|
import time
|
|
import logging
|
|
from typing import List, Union
|
|
from pathlib import Path
|
|
|
|
import idc
|
|
import viv_utils
|
|
|
|
import floss
|
|
import floss.main
|
|
import floss.utils
|
|
import floss.render
|
|
import floss.identify
|
|
import floss.stackstrings
|
|
import floss.tightstrings
|
|
import floss.string_decoder
|
|
from floss.results import AddressType, StackString, TightString, DecodedString
|
|
|
|
logger = logging.getLogger("floss.idaplugin")
|
|
|
|
|
|
MIN_LENGTH = 4
|
|
|
|
|
|
def append_comment(ea: int, s: str, repeatable: bool = False) -> None:
|
|
"""
|
|
add the given string as a (possibly repeating) comment to the given address.
|
|
does not add the comment if it already exists.
|
|
adds the comment on its own line.
|
|
|
|
Args:
|
|
ea: the address at which to add the comment.
|
|
s: the comment text.
|
|
repeatable: if True, set a repeatable comment.
|
|
|
|
"""
|
|
# see: http://blogs.norman.com/2011/security-research/improving-ida-analysis-of-x64-exception-handling
|
|
|
|
if repeatable:
|
|
cmt = idc.get_cmt(ea, True)
|
|
else:
|
|
cmt = idc.get_cmt(ea, False)
|
|
|
|
if not cmt:
|
|
cmt = s # no existing comment
|
|
else:
|
|
if s in cmt: # ignore duplicates
|
|
return
|
|
cmt = cmt + "\n" + s
|
|
|
|
if repeatable:
|
|
idc.set_cmt(ea, cmt, True)
|
|
else:
|
|
idc.set_cmt(ea, cmt, False)
|
|
|
|
|
|
def append_lvar_comment(fva: int, frame_offset: int, s: str, repeatable: bool = False) -> None:
|
|
"""
|
|
add the given string as a (possibly repeatable) stack variable comment to the given function.
|
|
does not add the comment if it already exists.
|
|
adds the comment on its own line.
|
|
|
|
Args:
|
|
fva: the address of the function with the stack variable.
|
|
frame_offset: the offset into the stack frame at which the variable is found.
|
|
s: the comment text.
|
|
repeatable: if True, set a repeatable comment.
|
|
|
|
"""
|
|
|
|
stack = idc.get_func_attr(fva, idc.FUNCATTR_FRAME)
|
|
if not stack:
|
|
raise RuntimeError("failed to find stack frame for function: 0x%x" % fva)
|
|
|
|
lvar_offset = (
|
|
idc.get_func_attr(fva, idc.FUNCATTR_FRSIZE) - frame_offset
|
|
) # alternative: idc.get_frame_lvar_size(fva) - frame_offset
|
|
if not lvar_offset:
|
|
raise RuntimeError("failed to compute local variable offset: 0x%x 0x%x %s" % (fva, stack, s))
|
|
|
|
if lvar_offset <= 0:
|
|
raise RuntimeError("failed to compute positive local variable offset: 0x%x 0x%x %s" % (fva, stack, s))
|
|
|
|
string = idc.get_member_cmt(stack, lvar_offset, repeatable)
|
|
if not string:
|
|
string = s
|
|
else:
|
|
if s in string: # ignore duplicates
|
|
return
|
|
string = string + "\n" + s
|
|
|
|
if not idc.set_member_cmt(stack, lvar_offset, string, repeatable):
|
|
raise RuntimeError("failed to set comment: 0x%08x 0x%08x 0x%08x: %s" % (fva, stack, lvar_offset, s))
|
|
|
|
|
|
def apply_decoded_strings(decoded_strings: List[DecodedString]) -> None:
|
|
for ds in decoded_strings:
|
|
if not ds.string:
|
|
continue
|
|
|
|
if ds.address_type == AddressType.GLOBAL:
|
|
logger.info("decoded string at global address 0x%x: %s", ds.address, ds.string)
|
|
append_comment(ds.address, ds.string)
|
|
else:
|
|
logger.info("decoded string for function call at 0x%x: %s", ds.decoded_at, ds.string)
|
|
append_comment(ds.decoded_at, ds.string)
|
|
|
|
|
|
def apply_stack_strings(
|
|
stack_strings: List[StackString], tight_strings: List[TightString], lvar_cmt: bool = True, cmt: bool = True
|
|
) -> None:
|
|
"""
|
|
lvar_cmt: apply stack variable comment
|
|
cmt: apply regular comment
|
|
"""
|
|
strings = stack_strings + tight_strings
|
|
for s in strings:
|
|
if not s.string:
|
|
continue
|
|
|
|
logger.info(
|
|
"decoded stack/tight string in function 0x%x (pc: 0x%x): %s", s.function, s.program_counter, s.string
|
|
)
|
|
if lvar_cmt:
|
|
try:
|
|
# TODO this often fails due to wrong frame offset
|
|
append_lvar_comment(s.function, s.frame_offset, s.string)
|
|
except RuntimeError as e:
|
|
logger.warning("failed to apply stack/tight string: %s", str(e))
|
|
if cmt:
|
|
append_comment(s.program_counter, s.string)
|
|
|
|
|
|
def ignore_floss_logs():
|
|
logging.getLogger("floss.api_hooks").setLevel(logging.WARNING)
|
|
logging.getLogger("floss.function_argument_getter").setLevel(logging.WARNING)
|
|
logging.getLogger("viv_utils").setLevel(logging.CRITICAL)
|
|
logging.getLogger("viv_utils.emulator_drivers").setLevel(logging.ERROR)
|
|
floss.utils.set_vivisect_log_level(logging.CRITICAL)
|
|
|
|
|
|
def main(argv=None):
|
|
logging.basicConfig(level=logging.INFO)
|
|
logging.getLogger().setLevel(logging.INFO)
|
|
ignore_floss_logs()
|
|
|
|
idb_path = Path(idc.get_idb_path())
|
|
fpath = idb_path.with_suffix("")
|
|
viv_path = fpath.with_suffix(".viv")
|
|
if viv_path.exists():
|
|
logger.info("loading vivisect workspace from %r", str(viv_path))
|
|
vw = viv_utils.getWorkspace(str(viv_path))
|
|
else:
|
|
logger.info("loading vivisect workspace from IDB...")
|
|
vw = viv_utils.loadWorkspaceFromIdb()
|
|
logger.info("loaded vivisect workspace")
|
|
|
|
selected_functions = set(vw.getFunctions())
|
|
|
|
time0 = time.time()
|
|
|
|
logger.info("identifying decoding functions...")
|
|
decoding_function_features, library_functions = floss.identify.find_decoding_function_features(
|
|
vw, selected_functions, disable_progress=True
|
|
)
|
|
|
|
logger.info("extracting stackstrings...")
|
|
selected_functions = floss.identify.get_functions_without_tightloops(decoding_function_features)
|
|
stack_strings = floss.stackstrings.extract_stackstrings(
|
|
vw, selected_functions, MIN_LENGTH, verbosity=floss.render.Verbosity.VERBOSE, disable_progress=True
|
|
)
|
|
logger.info("decoded %d stack strings", len(stack_strings))
|
|
|
|
logger.info("extracting tightstrings...")
|
|
tightloop_functions = floss.identify.get_functions_with_tightloops(decoding_function_features)
|
|
tight_strings = floss.tightstrings.extract_tightstrings(
|
|
vw,
|
|
tightloop_functions,
|
|
min_length=MIN_LENGTH,
|
|
verbosity=floss.render.Verbosity.VERBOSE,
|
|
disable_progress=True,
|
|
)
|
|
logger.info("decoded %d tight strings", len(tight_strings))
|
|
|
|
apply_stack_strings(stack_strings, tight_strings)
|
|
|
|
logger.info("decoding strings...")
|
|
|
|
top_functions = floss.identify.get_top_functions(decoding_function_features, 20)
|
|
fvas_to_emulate = floss.identify.get_function_fvas(top_functions)
|
|
fvas_tight_functions = floss.identify.get_tight_function_fvas(decoding_function_features)
|
|
fvas_to_emulate = floss.identify.append_unique(fvas_to_emulate, fvas_tight_functions)
|
|
decoded_strings = floss.string_decoder.decode_strings(
|
|
vw,
|
|
fvas_to_emulate,
|
|
MIN_LENGTH,
|
|
verbosity=floss.render.Verbosity.VERBOSE,
|
|
disable_progress=True,
|
|
)
|
|
logger.info("decoded %d strings", len(decoded_strings))
|
|
apply_decoded_strings(decoded_strings)
|
|
|
|
time1 = time.time()
|
|
logger.debug("finished execution after %f seconds", (time1 - time0))
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|