Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
140 lines (120 sloc) 5.11 KB
# Legilimency - Memory Analysis Framework for iOS
# --------------------------------------
#
# Written and maintained by Gal Beniamini <laginimaineb@google.com>
#
# Copyright 2017 Google Inc. All Rights Reserved.
# 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.
import struct
from bisect import bisect
from MemClient import MemClient
from defs import *
from symbols import *
#The offsets of fields within zone_page_metadata (see zalloc.c)
OFF_OFFSET = 16
META_OFFSET = 20
ZONE_PAGE_METADATA_SIZE = 24
#The offsets of the fields within zone_t (see zalloc.h)
INTERMEDIATE_QUEUE_OFFSET = 40
ALL_USED_QUEUE_OFFSET = 56
#The kalloc zone sizes
KALLOC_ZONE_SIZES = [0x10,
0x20,
0x30,
0x40,
0x50,
0x60,
0x80,
0xA0,
0xC0,
0x100,
0x120,
0x200,
0x240,
0x400,
0x480,
0x500,
0x800,
0x1000,
0x2000,
0x4000,
0x8000]
def create_zone_queue_allocation_generator(client, zone_queue, zone_alloc_size):
"""
Creates a generator over the allocations in the given zone queue.
This can be used to iterate over each allocation in the zone.
"""
#Finding the zone-related bounds
zone_map_min = client.read64(ZONE_MAP_MIN_ADDRESS + client.slide())
zone_meta_min = client.read64(ZONE_METADATA_REGION_MIN + client.slide())
print "zone_map_min: %016X" % zone_map_min
print "zone_meta_min: %016X" % zone_meta_min
#Going over each metadata entry
curr = zone_queue
while True:
#Printing some information about the current node
off = client.read32(curr + OFF_OFFSET)
meta = client.read32(curr + META_OFFSET)
free_count, zindex, page_count = struct.unpack("<HBB", struct.pack("<I", meta))
print "current: %016X" % curr
print "offset: 0x%X" % off
print "free_count: 0x%X" % free_count
print "zindex: 0x%X" % zindex
print "page_count: 0x%X" % page_count
#Making sure the metadata is within the allowed ranges
if not curr >= zone_meta_min:
print "metadata 0x%016X not within zone_meta ranges!" % curr
#Going over each page associated with zone in the zone_map
#and emitting each allocation block in our generator
meta_idx = (curr - zone_meta_min) / ZONE_PAGE_METADATA_SIZE
print "meta_idx: %d" % meta_idx
start_addr = zone_map_min + meta_idx * PAGE_SIZE
end_addr = start_addr + page_count * PAGE_SIZE
for addr in range(start_addr, end_addr, zone_alloc_size):
yield addr
#Going to the next node in the queue
curr = client.read64(curr)
if curr == zone_queue:
break #Looped back to the start - let's stop
def find_object_by_vtable(client, vtable, allocation_size=None, find_all=False):
"""
Find an object with the given vtable in the kalloc zones. If the allocation size
is specified, only the relevant kalloc zone is queried. If find_all is specified,
a list of all instances of such objects are returned.
If no matching instances are found, None is returned.
"""
#Do we have the allocation size?
zones = []
if allocation_size:
zone_idx = bisect(KALLOC_ZONE_SIZES, allocation_size)
zones.append((client.read64(K_ZONE + client.slide() + zone_idx*QWORD_SIZE), KALLOC_ZONE_SIZES[zone_idx]))
else:
for zone_idx in range(0, len(KALLOC_ZONE_SIZES)):
zones.append((client.read64(K_ZONE + client.slide() + zone_idx*QWORD_SIZE), KALLOC_ZONE_SIZES[zone_idx]))
#For each zone, create a generator over the zone
results = []
for (zone, zone_allocation_size) in zones:
#Gathering the relevant queues
all_used_queue = zone + ALL_USED_QUEUE_OFFSET
intermediate_queue = zone + INTERMEDIATE_QUEUE_OFFSET
queues = [all_used_queue, intermediate_queue]
#Going over each of the pages in the zone, either used or intermediate
for queue in queues:
for allocation in create_zone_queue_allocation_generator(client, queue, zone_allocation_size):
if client.read64(allocation) == vtable:
if not find_all:
return allocation
else:
results.append(allocation)
#Return the results
return results if len(results) > 0 else None