Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/smoke_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
shell: bash
# This sample should have 16 behaviors with 100% confidence
run: |
if [ "${{ env.Ahmyth_RESULT }}" == "37" ]; then
if [ "${{ env.Ahmyth_RESULT }}" == "40" ]; then
exit 0
else
exit 1
Expand All @@ -115,7 +115,7 @@ jobs:
shell: bash
# This sample should have 15 behaviors with 100% confidence
run: |
if [ "${{ env.e273e_RESULT }}" == "41" ]; then
if [ "${{ env.e273e_RESULT }}" == "42" ]; then
exit 0
else
exit 1
Expand Down
23 changes: 6 additions & 17 deletions docs/source/quark_inside_objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,28 +254,17 @@ RegisterObject(quark.Objects.registerobject)
============================================

RegisterObject is used to record the state of each register. Each initialized
registerobject will have register_name, value, called_by_func in a single
instance.
registerobject will have value and called_by_func in a single instance.

.. image:: https://i.imgur.com/k5nHprC.png


============== ========== =================================================
register_name value called_by_func
============== ========== =================================================
"v3" "GPS" Lcom/google/progress/APNOperator;->deleteAPN()Z
============== ========== =================================================

**register_name**: register name, such as "v3", "v4".
========== =================================================
value called_by_func
========== =================================================
"GPS" Lcom/google/progress/APNOperator;->deleteAPN()Z
========== =================================================

**value**: the value stored in the register.

**called_by_func**: what functions are called with this register as a parameter.


Explanation of each function
----------------------------

* **hash_index**:

- Get the index number from given VarabileObject, given "v34" will return 34.
44 changes: 10 additions & 34 deletions quark/core/struct/registerobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,24 @@ class RegisterObject:
"""The RegisterObject is used to record the state of each register"""

__slots__ = [
"_register_name",
"_value",
"_called_by_func",
"_current_type",
"_type_history",
]

def __init__(self, register_name, value, called_by_func=None, value_type=None):
def __init__(self, value, called_by_func=None, value_type=None):
"""
A data structure for creating the bytecode variable object, which
used to record the state of each register.

+================+========+==================+
| register_name | value | called_by_func |
+================+========+==================+
+========+==================+
| value | called_by_func |
+========+==================+

:param register_name:
:param value:
:param called_by_func:
"""
self._register_name = register_name
self._value = value
self._current_type = value_type
self._type_history = []
Expand All @@ -36,13 +33,12 @@ def __init__(self, register_name, value, called_by_func=None, value_type=None):
self._called_by_func.append(called_by_func)

def __repr__(self):
return f"<VarabileObject-register:{self._register_name}, value:{self._value}, called_by_func:{','.join(self._called_by_func)}, current_type:{self._value_type}>"
return f"<VarabileObject-value:{self._value}, called_by_func:{','.join(self._called_by_func)}, current_type:{self._current_type}>"

def __eq__(self, obj):
return (
isinstance(obj, RegisterObject)
and obj.called_by_func == self.called_by_func
and obj.register_name == self.register_name
and obj.value == self.value
and obj.current_type == self.current_type
)
Expand All @@ -67,25 +63,6 @@ def called_by_func(self, called_by_func):
self._called_by_func.append(called_by_func)
self._type_history.append(self._current_type)

@property
def register_name(self):
"""
Individual register name, for example 'v3'.

:return: a string of register name
"""
return self._register_name

@register_name.setter
def register_name(self, reg_name):
"""
Setter of register_name.

:param reg_name:
:return: None
"""
self._register_name = reg_name

@property
def value(self):
"""
Expand Down Expand Up @@ -123,15 +100,14 @@ def current_type(self, value):
def type_histroy(self):
return self._type_history

@property
def hash_index(self):
def bears_object(self) -> bool:
"""
Get the index number from given VarabileObject.
Check whether the register bears an object.

:return: an integer corresponding to the register index
:return: True if the register bears an object, False otherwise
:rtype: bool
"""
return int(self.register_name[1:])

return self.current_type is not None and self.current_type.startswith("L")

if __name__ == "__main__":
pass
107 changes: 74 additions & 33 deletions quark/evaluator/pyeval.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,19 @@ def __init__(self, apkinfo):
self.eval = {
# invoke-kind
"invoke-virtual": self.INVOKE_VIRTUAL,
"invoke-virtual/range": self.INVOKE_VIRTUAL,
"invoke-direct": self.INVOKE_DIRECT,
"invoke-direct/range": self.INVOKE_DIRECT,
"invoke-static": self.INVOKE_STATIC,
"invoke-virtual/range": self.INVOKE_VIRTUAL_RANGE,
"invoke-static/range": self.INVOKE_STATIC,
"invoke-interface": self.INVOKE_INTERFACE,
"invoke-interface/range": self.INVOKE_INTERFACE,
"invoke-super": self.INVOKE_SUPER,
"invoke-super/range": self.INVOKE_SUPER,
"invoke-polymorphic": self.INVOKE_POLYMORPHIC,
"invoke-polymorphic/range": self.INVOKE_POLYMORPHIC,
"invoke-custom": self.INVOKE_CUSTOM,
"invoke-custom/range": self.INVOKE_CUSTOM,
# move-result-kind
"move-result": self.MOVE_RESULT,
"move-result-wide": self.MOVE_RESULT_WIDE,
Expand Down Expand Up @@ -177,24 +183,31 @@ def _invoke(self, instruction, look_up=False, skip_self=False):
var_obj = self.table_obj.pop(index)
value_of_reg_list.append(var_obj.value)

# Remove duplicate parameter values to save memory
Comment thread
sidra-asa marked this conversation as resolved.
seen = {}
for idx, val in enumerate(value_of_reg_list):
if val in seen:
value_of_reg_list[idx] = f"<ref_{seen[val]}>"
else:
seen[val] = idx

invoked_state = f"{executed_fuc}({','.join(value_of_reg_list)})"

# insert the function and the parameter into called_by_func
for reg in reg_list:
index = int(reg[1:])
obj_stack = self.table_obj.get_obj_list(index)
if obj_stack:
# add the function name into each parameter table
var_obj = self.table_obj.pop(index)
var_obj.called_by_func = invoked_state

if instruction[0].startswith('invoke') and not instruction[0].endswith("static"):
# push the return value into the instance
reg_idx_to_object = int(reg_list[0][1:])
if not self.table_obj.get_obj_list(index):
continue

obj_stack = self.table_obj.get_obj_list(reg_idx_to_object)
if obj_stack:
var_obj = self.table_obj.pop(reg_idx_to_object)
# add the function name into each parameter table
var_obj = self.table_obj.pop(index)
var_obj.called_by_func = invoked_state

if var_obj.bears_object():
# If the register bears an object, update its value to reflect
# the method invocation since the method may modify the
# internal state of the object.
var_obj.value = invoked_state

if not executed_fuc.endswith(")V"):
Expand All @@ -210,20 +223,33 @@ def _move_result(self, instruction):
index = int(reg[1:])
try:
pre_ret = self.ret_stack.pop()
variable_object = RegisterObject(reg, pre_ret, value_type=self.ret_type)
variable_object = RegisterObject(
value=pre_ret, value_type=self.ret_type
)
self.table_obj.insert(index, variable_object)
self.ret_type = ""
except IndexError as e:

log.exception(f"{e} in _move_result")

def _move_object(self, src_reg_idx: int, dest_reg_idx: int):
"""
Move object from src_reg_idx to dest_reg_idx without creating new
RegisterObject. This allow both registers to point to the same object.
"""
# Get the source object from the table
src_obj = self.table_obj.pop(src_reg_idx)

# Insert the source object to the destination register.
self.table_obj.insert(dest_reg_idx, src_obj)

def _assign_value(self, instruction, value_type=""):

reg = instruction[1]
value = instruction[2]
index = int(reg[1:])

variable_object = RegisterObject(reg, value, value_type=value_type)
variable_object = RegisterObject(value=value, value_type=value_type)
self.table_obj.insert(index, variable_object)

def _assign_value_wide(self, instruction, value_type=""):
Expand All @@ -233,17 +259,17 @@ def _assign_value_wide(self, instruction, value_type=""):
reg = instruction[1]
value = instruction[2]
index = int(reg[1:])
reg_plus_one = f"v{index + 1}"

variable_object = RegisterObject(reg, value, value_type=value_type)
variable_object2 = RegisterObject(reg_plus_one, value, value_type=value_type)
variable_object = RegisterObject(value=value, value_type=value_type)
variable_object2 = RegisterObject(value=value, value_type=value_type)
self.table_obj.insert(index, variable_object)
self.table_obj.insert(index + 1, variable_object2)

@logger
def INVOKE_VIRTUAL(self, instruction):
"""
invoke-virtual { parameters }, methodtocall
invoke-virtual/range { parameters }, methodtocall

Invokes a virtual method with parameters.
"""
Expand All @@ -253,6 +279,7 @@ def INVOKE_VIRTUAL(self, instruction):
def INVOKE_DIRECT(self, instruction):
"""
invoke-direct { parameters }, methodtocall
invoke-direct/range { parameters }, methodtocall

Invokes a method with parameters without the virtual method resolution. (first parameter is "this")
"""
Expand All @@ -262,39 +289,48 @@ def INVOKE_DIRECT(self, instruction):
def INVOKE_STATIC(self, instruction):
"""
invoke-static {parameters}, methodtocall
invoke-static/range {parameters}, methodtocall

Invokes a static method with parameters.
"""
self._invoke(instruction)

@logger
def INVOKE_VIRTUAL_RANGE(self, instruction):
"""
invoke-virtual/range { parameters }, methodtocall
Invokes a virtual-range method with parameters.
"""
self._invoke(instruction, look_up=True)

@logger
def INVOKE_INTERFACE(self, instruction):
"""
invoke-interface { parameters }, methodtocall
invoke-interface/range { parameters }, methodtocall

Invokes a interface method with parameters.
"""
self._invoke(instruction, look_up=True)

@logger
def INVOKE_SUPER(self, instruction):
"""
invoke-interface { parameters }, methodtocall
Invokes a interface method with parameters.
invoke-super { parameters }, methodtocall
invoke-super/range { parameters }, methodtocall

Invokes a super method with parameters.
"""
self._invoke(instruction, look_up=True, skip_self=True)

def INVOKE_POLYMORPHIC(self, instruction):
"""
invoke-polymorphic { parameters }, methodtocall
invoke-polymorphic/range { parameters }, methodtocall

Invokes a polymorphic method with parameters.
"""
self._invoke(instruction)

def INVOKE_CUSTOM(self, instruction):
"""
invoke-custom { parameters }, callsite
invoke-custom/range { parameters }, callsite

Invokes a call site with parameters.
"""
self._invoke(instruction)

@logger
Expand All @@ -320,9 +356,9 @@ def MOVE_RESULT_WIDE(self, instruction):
index = int(reg[1:])
try:
pre_ret = self.ret_stack.pop()
variable_object = RegisterObject(reg, pre_ret, value_type=self.ret_type)
variable_object = RegisterObject(value=pre_ret, value_type=self.ret_type)
variable_object2 = RegisterObject(
f"v{index + 1}", pre_ret, value_type=self.ret_type
value=pre_ret, value_type=self.ret_type
)
self.table_obj.insert(index, variable_object)
self.table_obj.insert(index + 1, variable_object2)
Expand Down Expand Up @@ -488,6 +524,13 @@ def AGET_KIND(self, instruction):
@logger
def MOVE_KIND(self, instruction):
try:
if instruction[0].startswith("move-object"):
self._move_object(
src_reg_idx=int(instruction[2][1:]),
dest_reg_idx=int(instruction[1][1:]),
)
return

wide = "wide" in instruction[0]
self._move_value_to_register(instruction, "{src0}", wide=wide)
except IndexError as e:
Expand Down Expand Up @@ -714,8 +757,7 @@ def _move_value_and_data_to_register(
value_dict["data"] = data

new_register = RegisterObject(
f"v{source}",
str_format.format(**value_dict),
value=str_format.format(**value_dict),
value_type=value_type,
)
self.table_obj.insert(source, new_register)
Expand Down Expand Up @@ -765,8 +807,7 @@ def _transfer_register(
value_dict["data"] = data

new_register = RegisterObject(
f"v{destination}",
str_format.format(**value_dict),
value=str_format.format(**value_dict),
value_type=value_type,
)

Expand Down
Loading
Loading