Skip to content

Commit

Permalink
add feature dump memory of application
Browse files Browse the repository at this point in the history
  • Loading branch information
noobpk committed May 5, 2021
1 parent 76fb2e7 commit 26df0ba
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 3 deletions.
23 changes: 21 additions & 2 deletions hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
''')

print ("\033[1;34m[*]___author___: @noobpk\033[1;37m")
print ("\033[1;34m[*]___version___: 3.3a\033[1;37m")
print ("\033[1;34m[*]___version___: 3.4\033[1;37m")
print ("")

def check_platform():
Expand Down Expand Up @@ -90,15 +90,26 @@ def handle_del_log():
except Exception as e:
logger.error("[x_x] Something went wrong when clear error log. Please clear error log manual.\n Message - {0}".format(e))

def dump_memory(option, process):
try:
if option != "-h":
cmd = shlex.split("python3 " + "lib/dump-memory/fridump.py " + "-U " + option + ' ' + '"' + process + '"')
else:
cmd = shlex.split("python3 " + "lib/dump-memory/fridump.py " + option)
subprocess.call(cmd)
sys.exit(0)
except Exception as e:
logger.error("[x_x] Something went wrong, please check your error message.\n Message - {0}".format(e))

def main():
try:

usage = "[>] python3 %prog [options] arg\n\n\r[>] Example for spawn or attach app with -s(--script) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -s trace_class.js\n\n\r[>] Example for spawn or attach app with -m(--method) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -m app-static\n\n\r[>] Example dump decrypt ipa with -d(--dump) and -o(--output) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -d -o App_dump_name"
usage = "[>] python3 %prog [options] arg\n\n\r[>] Example for spawn or attach app with -s(--script) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -s trace_class.js\n\n\r[>] Example for spawn or attach app with -m(--method) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -m app-static\n\n\r[>] Example dump decrypt ipa with -d(--dump) and -o(--output) options:\npython3 hook.py -p com.apple.AppStore / [-n 'App Store'] -d -o App_dump_name\n\n\r[>] Example dump memory of application with --dump-memory and -s(--string) options:\npython3 hook.py -n 'App Store' --dump-memory '-s(--string)'"
parser = optparse.OptionParser(usage,add_help_option=False)
info = optparse.OptionGroup(parser,"Information")
quick = optparse.OptionGroup(parser,"Quick Method")
dump = optparse.OptionGroup(parser,"Dump decrypt IPA")
dumpmemory = optparse.OptionGroup(parser,"Dump memory of Application")

parser.add_option('-h', "--help", action="help", dest="help", help="Show basic help message and exit")
#Using options -p(--package) for spawn application and load script
Expand Down Expand Up @@ -130,7 +141,11 @@ def main():
dump.add_option("-d", "--dump", action="store_true", help="Dump decrypt application.ipa", dest="dumpapp")
dump.add_option("-o", "--output", action="store" , dest="output_ipa", help="Specify name of the decrypted IPA", metavar="OUTPUT_IPA", type="string")

#Dump memory of application using the code of Nightbringer21's repo fridump - Link: https://github.com/Nightbringer21/fridump
dumpmemory.add_option("--dump-memory", action="store", help="Dump memory of application", dest="dumpmemory")

parser.add_option_group(dump)
parser.add_option_group(dumpmemory)
parser.add_option_group(info)
parser.add_option_group(quick)

Expand Down Expand Up @@ -315,6 +330,10 @@ def main():
subprocess.call(cmd)
sys.exit(0)

#dump memory application
elif options.name and options.dumpmemory:
dump_memory(options.dumpmemory, options.name)

else:
logger.warning("[!] Specify the options. use (-h) for more help!")
# sys.exit(0)
Expand Down
2 changes: 1 addition & 1 deletion lib/checkversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from lib.log import *

VERSION = "3.3a"
VERSION = "3.4"

def check_version(speak=True):
"""
Expand Down
39 changes: 39 additions & 0 deletions lib/dump-memory/dumper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import logging

# Reading bytes from session and saving it to a file

def dump_to_file(agent,base,size,error,directory):
try:
filename = str(base)+'_dump.data'
dump = agent.read_memory(base, size)
f = open(os.path.join(directory,filename), 'wb')
f.write(dump)
f.close()
return error
except Exception as e:
logging.debug("[!]"+str(e))
print("Oops, memory access violation!")
return error

#Read bytes that are bigger than the max_size value, split them into chunks and save them to a file

def splitter(agent,base,size,max_size,error,directory):
times = size/max_size
diff = size % max_size
if diff is 0:
logging.debug("Number of chunks:"+str(times+1))
else:
logging.debug("Number of chunks:"+str(times))
global cur_base
cur_base = int(base,0)

for time in range(int(times)):
logging.debug("Save bytes: "+str(cur_base)+" till "+str(cur_base+max_size))
dump_to_file(agent, cur_base, max_size, error, directory)
cur_base = cur_base + max_size

if diff is not 0:
logging.debug("Save bytes: "+str(hex(cur_base))+" till "+str(hex(cur_base+diff)))
dump_to_file(agent, cur_base, diff, error, directory)

160 changes: 160 additions & 0 deletions lib/dump-memory/fridump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import textwrap
import frida
import os
import sys
import frida.core
import dumper
import utils
import argparse
import logging

logo = """
______ _ _
| ___| (_) | |
| |_ _ __ _ __| |_ _ _ __ ___ _ __
| _| '__| |/ _` | | | | '_ ` _ \| '_ \\
| | | | | | (_| | |_| | | | | | | |_) |
\_| |_| |_|\__,_|\__,_|_| |_| |_| .__/
| |
|_|
"""


# Main Menu
def MENU():
parser = argparse.ArgumentParser(
prog='dump-memory',
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent(""))

# parser.add_argument('process',
# help='the process that you will be injecting to')
parser.add_argument('-o', '--out', type=str, metavar="dir",
help='provide full output directory path. (def: \'memory-dump\')')
parser.add_argument('-U', '--usb', action='store_true',
help='device connected over usb')
parser.add_argument('-v', '--verbose', action='store_true',
help='verbose')
parser.add_argument('-r', '--read-only', action='store_true',
help="dump read-only parts of memory. More data, more errors")
parser.add_argument('-s', '--strings', action='store_true',
help='run strings on all dump files. Saved in output dir.')
parser.add_argument('--max-size', type=int, metavar="bytes",
help='maximum size of dump file in bytes (def: 20971520)')
args = parser.parse_args()
return args


#print(logo)

arguments = MENU()

# Define Configurations
APP_NAME = arguments.process
DIRECTORY = ""
USB = arguments.usb
DEBUG_LEVEL = logging.INFO
STRINGS = arguments.strings
MAX_SIZE = 20971520
PERMS = 'rw-'

if arguments.read_only:
PERMS = 'r--'

if arguments.verbose:
DEBUG_LEVEL = logging.DEBUG
logging.basicConfig(format='%(levelname)s:%(message)s', level=DEBUG_LEVEL)


# Start a new Session
session = None
try:
if USB:
session = frida.get_usb_device().attach(APP_NAME)
else:
session = frida.attach(APP_NAME)
except Exception as e:
print("Can't connect to App. Have you connected the device?")
logging.debug(str(e))
sys.exit()


# Selecting Output directory
if arguments.out is not None:
DIRECTORY = arguments.out
if os.path.isdir(DIRECTORY):
print("Output directory is set to: " + DIRECTORY)
else:
print("The selected output directory does not exist!")
sys.exit(1)

else:
print("Current Directory: " + str(os.getcwd()))
DIRECTORY = os.path.join(os.getcwd(), "memory-dump")
print("Output directory is set to: " + DIRECTORY)
if not os.path.exists(DIRECTORY):
print("Creating directory...")
os.makedirs(DIRECTORY)

mem_access_viol = ""

print("Starting Memory dump...")

script = session.create_script(
"""'use strict';
rpc.exports = {
enumerateRanges: function (prot) {
return Process.enumerateRangesSync(prot);
},
readMemory: function (address, size) {
return Memory.readByteArray(ptr(address), size);
}
};
""")
script.on("message", utils.on_message)
script.load()

agent = script.exports
ranges = agent.enumerate_ranges(PERMS)

if arguments.max_size is not None:
MAX_SIZE = arguments.max_size

i = 0
l = len(ranges)

# Performing the memory dump
for range in ranges:
base = range["base"]
size = range["size"]

logging.debug("Base Address: " + str(base))
logging.debug("")
logging.debug("Size: " + str(size))


if size > MAX_SIZE:
logging.debug("Too big, splitting the dump into chunks")
mem_access_viol = dumper.splitter(
agent, base, size, MAX_SIZE, mem_access_viol, DIRECTORY)
continue
mem_access_viol = dumper.dump_to_file(
agent, base, size, mem_access_viol, DIRECTORY)
i += 1
utils.printProgress(i, l, prefix='Progress:', suffix='Complete', bar=50)
print("")

# Run Strings if selected

if STRINGS:
files = os.listdir(DIRECTORY)
i = 0
l = len(files)
print("Running strings on all files:")
for f1 in files:
utils.strings(f1, DIRECTORY)
i += 1
utils.printProgress(i, l, prefix='Progress:', suffix='Complete', bar=50)
print("Finished!")
33 changes: 33 additions & 0 deletions lib/dump-memory/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import sys
import string
import logging
import os
import re
from io import open

# Progress bar function
def printProgress (times, total, prefix ='', suffix ='', decimals = 2, bar = 100):
filled = int(round(bar * times / float(total)))
percents = round(100.00 * (times / float(total)), decimals)
bar = '#' * filled + '-' * (bar - filled)
sys.stdout.write('%s [%s] %s%s %s\r' % (prefix, bar, percents, '%', suffix)),
sys.stdout.flush()
if times == total:
print("\n")


# A very basic implementations of Strings
def strings(filename, directory, min=4):
strings_file = os.path.join(directory, "strings.txt")
path = os.path.join(directory, filename)
with open(path, encoding='Latin-1') as infile:
str_list = re.findall("[\x20-\x7E]+\x00", infile.read())
with open(strings_file, "a") as st:
for string in str_list:
if len(string) > min:
logging.debug(string)
st.write(string + "\n")

# Method to receive messages from Javascript API calls
def on_message(message, data):
print("[on_message] message:", message, "data:", data)

0 comments on commit 26df0ba

Please sign in to comment.