예제 #1
0
    def sub_operations(jar, cf, instruction,
                       operand, arg_names=[""]):
        """Gets the instrcutions of a different class"""
        invoked_class = operand.c
        name = operand.name
        descriptor = operand.descriptor
        args = method_descriptor(descriptor)[0]
        cache_key = "%s/%s/%s/%s" % (invoked_class, name,
                                     descriptor, _PIT.join(arg_names, ","))

        if cache_key in _PIT.CACHE:
            cache = _PIT.CACHE[cache_key]
            operations = [op.clone() for op in cache]
        else:
            operations = _PIT.operations(jar, invoked_class,
                                         args, name, arg_names)

        position = 0
        for operation in _PIT.ordered_operations(operations):
            position += 0.01
            operation.position = instruction.pos + (position)

        _PIT.CACHE[cache_key] = operations

        return operations
예제 #2
0
    def sub_operations(jar, cf, instruction, operand, arg_names=[""]):
        """Gets the instructions of a different class"""
        invoked_class = operand.c
        name = operand.name
        descriptor = operand.descriptor
        args = method_descriptor(descriptor)[0]
        cache_key = "%s/%s/%s/%s" % (invoked_class, name, descriptor,
                                     _PIT.join(arg_names, ","))

        if cache_key in _PIT.CACHE:
            cache = _PIT.CACHE[cache_key]
            operations = [op.clone() for op in cache]
        else:
            operations = _PIT.operations(jar, invoked_class, args, name,
                                         arg_names)

        position = 0
        for operation in _PIT.ordered_operations(operations):
            position += 0.01
            operation.position = instruction.pos + (position)

        _PIT.CACHE[cache_key] = operations

        return operations
예제 #3
0
    def operations(jar, classname, args=('java.io.DataOutputStream',),
                   methodname=None, arg_names=("this", "stream")):
        """Gets the instructions of the specified method"""

        # Find the writing method
        cf = jar.open_class(classname)

        if methodname == None:
            method = cf.methods.find_one(args=args)
        else:
            method = cf.methods.find_one(name=methodname, args=args)

        if method == None:
            if cf.superclass:
                return _PIT.operations(jar, cf.superclass, args,
                                       methodname, arg_names)
            else:
                raise Exception("Call to unknown method")

        # Decode the instructions
        operations = []
        stack = []
        shortif_pos = -1
        shortif_cond = ''

        for instruction in method.instructions:
            opcode = instruction.opcode
            operands = [InstructionField(operand, instruction, cf.constants)
                        for operand in instruction.operands]

            # Shortcut if
            if instruction.pos == shortif_pos:
                category = stack[-1].category
                stack.append(Operand("((%(cond)s) ? %(sec)s : %(first)s)" % {
                    "cond": shortif_cond,
                    "first": stack.pop(),
                    "sec": stack.pop()
                }, category))

            # Method calls
            if opcode >= 0xb6 and opcode <= 0xb9:
                name = operands[0].name
                desc = operands[0].descriptor
                if name in _PIT.TYPES:
                    operations.append(Operation(instruction.pos, "write",
                                                type=_PIT.TYPES[name],
                                                field=stack.pop()))
                    stack.pop()
                elif name == "write":
                    if desc.find("[BII") >= 0:
                        stack.pop()
                        stack.pop()
                    operations.append(Operation(
                        instruction.pos, "write",
                        type="byte[]" if desc.find("[B") >= 0 else "byte",
                        field=stack.pop()))
                    stack.pop()
                elif desc == "(Ljava/lang/String;Ljava/io/DataOutputStream;)V":
                    stack.pop()
                    operations.append(Operation(instruction.pos, "write",
                                                type="string16",
                                                field=stack.pop()))
                else:
                    descriptor = method_descriptor(desc)
                    num_arguments = len(descriptor[0])
                    if num_arguments > 0:
                        arguments = stack[-len(descriptor[0]):]
                    else:
                        arguments = []
                    for i in range(num_arguments):
                        stack.pop()
                    obj = "static" if opcode == 0xb8 else stack.pop()
                    if descriptor[1] != "void":
                        stack.append(Operand(
                            "%s.%s(%s)" % (
                                obj, name, _PIT.join(arguments)
                            ),
                            2 if descriptor[1] in ("long", "double") else 1)
                        )

                    if "java.io.DataOutputStream" in descriptor[0]:
                        operations += _PIT.sub_operations(
                            jar, cf, instruction, operands[0],
                            [obj] + arguments if obj != "static" else arguments
                        )

            # Conditional statements and loops
            elif opcode in [0xc7, 0xc6] or opcode >= 0x99 and opcode <= 0xa6:
                if opcode == 0xc7:
                    condition = "%s == null" % stack.pop()
                elif opcode == 0xc6:
                    condition = "%s != null" % stack.pop()
                else:
                    if opcode <= 0x9e:                  # if
                        comperation = opcode - 0x99
                        fields = [0, stack.pop()]
                    elif opcode <= 0xa4:                # if_icmp
                        comperation = opcode - 0x9f
                        fields = [stack.pop(), stack.pop()]
                    else:                               # if_acmp
                        comperation = opcode - 0xa5
                        fields = [operands.pop(), operands.pop()]
                    if comperation == 0 and fields[0] == 0:
                        condition = fields[1]
                    else:
                        condition = "%s %s %s" % (
                            fields[1], _PIT.CONDITIONS[comperation], fields[0]
                        )
                operations.append(Operation(instruction.pos, "if",
                                            condition=condition))
                operations.append(Operation(operands[0].target, "endif"))
                shortif_cond = condition

            elif opcode == 0xaa:                        # tableswitch
                operations.append(Operation(instruction.pos, "switch",
                                            field=stack.pop()))
                low = operands[0].value[1]
                for opr in range(1, len(operands)):
                    target = operands[opr].target
                    operations.append(Operation(target, "case",
                                                value=low + opr - 1))
                operations.append(Operation(operands[0].target, "endswitch"))

            elif opcode == 0xab:                        # lookupswitch
                operations.append(Operation(instruction.pos, "switch",
                                            field=stack.pop()))
                for opr in range(1, len(operands)):
                    target = operands[opr].find_target(1)
                    operations.append(Operation(target, "case",
                                                value=operands[opr].value[0]))
                operations.append(Operation(operands[0].target, "endswitch"))

            elif opcode == 0xa7:                        # goto
                target = operands[0].target
                endif = _PIT.find_next(operations, instruction.pos, "endif")
                case = _PIT.find_next(operations, instruction.pos, "case")
                if case != None and target > case.position:
                    operations.append(Operation(instruction.pos, "break"))
                elif target > instruction.pos:
                    endif.operation = "else"
                    operations.append(Operation(target, "endif"))
                    if len(stack) != 0:
                        shortif_pos = target
                else:
                    endif.operation = "endloop"
                    _PIT.find_next(operations, target, "if").operation = "loop"

            # Math
            elif opcode >= 0x74 and opcode <= 0x77:
                category = stack[-1].category
                stack.append(Operand("(- %s)" % (stack.pop), category))
            elif opcode >= 0x60 and opcode <= 0x7f:     # Tneg
                lookup_opcode = opcode
                while not lookup_opcode in _PIT.MATH:
                    lookop_opcode -= 1
                category = stack[-1].category
                stack.append(Operand(
                    "(%s %s %s)" % (
                        _PIT.MATH[lookup_opcode],
                        stack.pop(), stack.pop()
                    ), category
                ))
            elif opcode == 0x84:                        # iinc
                operations.append(Operation(instruction.pos, "increment",
                                            field="var%s" % operands[0],
                                            amount=operands[1]))

            # Other manually handled opcodes
            elif opcode == 0xc5:                        # multianewarray
                operand = ""
                for i in range(operands[1].value):
                    operand = "[%s]%s" % (stack.pop(), operand)
                stack.append(Operand(
                    "new %s%s" % (operands[0].type, operand)))
            elif opcode == 0x58:                        # pop2
                if stack.pop().category != 2:
                    stack.pop()
            elif opcode == 0x5f:                        # swap
                stack += [stack.pop(), stack.pop()]
            elif opcode == 0x59:                        # dup
                stack.append(stack[-1])
            elif opcode == 0x5a:                        # dup_x1
                stack.insert(-2, stack[-1])
            elif opcode == 0x5b:                        # dup_x2
                stack.insert(-2 if stack[-2].category == 2 else -3, stack[-1])
            elif opcode == 0x5c:                        # dup2
                if stack[-1].category == 2:
                    stack.append(stack[-1])
                else:
                    stack += stack[-2:]
            elif opcode == 0x5d:                        # dup2_x1
                if stack[-1].category == 2:
                    stack.insert(-2, stack[-1])
                else:
                    stack.insert(-3, stack[-2])
                    stack.insert(-3, stack[-1])
            elif opcode == 0x5e:                        # dup2_x2
                if stack[-1].category == 2:
                    stack.insert(
                        -2 if stack[-2].category == 2 else -3, stack[-1]
                    )
                else:
                    stack.insert(
                        -3 if stack[-3].category == 2 else -4, stack[-2]
                    )
                    stack.insert(
                        -3 if stack[-3].category == 2 else -4, stack[-1]
                    )

            # Unhandled opcodes
            elif opcode in [0xc8, 0xa8, 0xc9]:
                raise Exception("unhandled opcode 0x%x" % opcode)

            # Default handlers
            else:
                lookup_opcode = opcode
                while not lookup_opcode in _PIT.OPCODES:
                    lookup_opcode -= 1

                handler = _PIT.OPCODES[lookup_opcode]
                index = 0

                if isinstance(handler, int):
                    handler = [handler]

                assert len(stack) >= handler[index]

                for i in range(handler[index]):
                    operands.insert(0, stack.pop())

                index += 1

                if len(handler) > index:
                    format = handler[index]
                    index += 1

                    if (len(handler) > index and
                        isinstance(handler[index], LambdaType)):
                        value = handler[index](opcode)
                        operands.append(value)
                        operands.append(arg_names[value]
                                        if value < len(arg_names)
                                        else "var%s" % value)
                        index += 1
                    elif len(operands) >= 1:
                        value = operands[0].value
                        operands.append(arg_names[value]
                                        if value < len(arg_names)
                                        else "var%s" % value)

                    if (len(handler) > index and
                        isinstance(handler[index], int)):
                        category = handler[index]
                    else:
                        category = 1

                    stack.append(Operand(
                        StringFormatter.format(format, *operands),
                        category)
                    )

        return operations
예제 #4
0
파일: recipes.py 프로젝트: Krenair/Burger
        def find_recipes(jar, cf, method, target_class, setter_names):
            # Temporary state variables
            tmp_recipes = []
            started_item = False
            next_push_is_val = False
            with_metadata = False
            block_substitute = None
            block_subs = {}
            rows = []
            make_count = 0
            metadata = 0
            recipe_target = None
            pushes_are_counts = False
            positions = True

            # Iterate over the methods instructions from the
            # bottom-up so we can catch the invoke/new range.
            for ins in method.instructions.reverse():
                # Find the call that stores the generated recipe.
                if ins.name == "invokevirtual" and not started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    # Parse the string method descriptor
                    desc = const["name_and_type"]["descriptor"]["value"]
                    args, returns = method_descriptor(desc)

                    # We've found the recipe storage method
                    if "java.lang.Object[]" in args and returns == "void":
                        started_item = True
                        pushes_are_counts = False
                        next_push_is_val = False
                        with_metadata = False
                        rows = []
                        make_count = 0
                        metadata = 0
                        recipe_target = None
                        method_name = const["name_and_type"]["name"]["value"]
                        positions = method_name == setter_names[0]
                        if positions:
                            block_subs = {}
                        else:
                            block_subs = []
                    elif superclass in args:
                        _class = const["class"]["name"]["value"]
                        sub_cf = jar.open_class(_class)
                        tmp_recipes += find_recipes(
                            jar, sub_cf,
                            sub_cf.methods.find_one(args=args),
                            target_class, setter_names
                        )
                # We've found the start of the recipe declaration (or the end
                # as it is in our case).
                elif ins.name == "new" and started_item:
                    started_item = False
                    if positions:
                        tmp_recipes.append({
                            "type": "shape",
                            "substitutes": block_subs,
                            "rows": rows,
                            "makes": make_count,
                            "recipe_target": recipe_target
                        })
                    else:
                        tmp_recipes.append({
                            "type": "shapeless",
                            "ingredients": block_subs,
                            "makes": make_count,
                            "recipe_target": recipe_target
                        })
                # The item/block to be substituted
                elif (ins.name == "getstatic" and started_item
                        and not pushes_are_counts):
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    cl_name = const["class"]["name"]["value"]
                    cl_field = const["name_and_type"]["name"]["value"]

                    block_substitute = (cl_name, cl_field)
                    if not positions:
                        block_subs.append(block_substitute)
                elif ins.name == "getstatic" and pushes_are_counts:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    cl_name = const["class"]["name"]["value"]
                    cl_field = const["name_and_type"]["name"]["value"]

                    recipe_target = (cl_name, cl_field, metadata)
                # Block string substitute value
                elif ins.name == "bipush" and next_push_is_val:
                    next_push_is_val = False
                    block_subs[chr(ins.operands[0][1])] = block_substitute
                # Number of items that the recipe makes
                elif ins.name == "bipush" and pushes_are_counts:
                    make_count = ins.operands[0][1]
                    if with_metadata:
                        metadata = make_count
                        with_metadata = False
                elif ins.name.startswith("iconst_") and pushes_are_counts:
                    make_count = int(ins.name[-1])
                    if with_metadata:
                        metadata = make_count
                        with_metadata = False
                # Recipe row
                elif ins.name == "ldc" and started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    if const['tag'] == ConstantType.STRING:
                        rows.append(const['string']['value'])
                # The Character.valueOf() call
                elif ins.name == "invokestatic":
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    # Parse the string method descriptor
                    desc = const["name_and_type"]["descriptor"]["value"]
                    args, returns = method_descriptor(desc)

                    if "char" in args and returns == "java.lang.Character":
                        # The next integer push will be the character value.
                        next_push_is_val = True
                elif ins.name == "invokespecial" and started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    name = const["name_and_type"]["name"]["value"]
                    if name == "<init>":
                        pushes_are_counts = True
                        if "II" in const["name_and_type"]["descriptor"]["value"]:
                            with_metadata = True
            return tmp_recipes
예제 #5
0
    def operations(jar,
                   classname,
                   args=None,
                   methodname=None,
                   arg_names=("this", "stream")):
        """Gets the instructions of the specified method"""

        # Find the writing method
        cf = jar.open_class(classname)

        if methodname is None and args is None:
            method = cf.methods.find_one(f=lambda m: m.args in (
                ('java.io.DataOutputStream', ), ('java.io.DataOutput', )))
        elif methodname is None:
            method = cf.methods.find_one(args=args)
        else:
            method = cf.methods.find_one(name=methodname, args=args)

        if method is None:
            if cf.superclass:
                return _PIT.operations(jar, cf.superclass, args, methodname,
                                       arg_names)
            else:
                raise Exception("Call to unknown method")

        # Decode the instructions
        operations = []
        stack = []
        skip_until = -1
        shortif_pos = -1
        shortif_cond = ''

        for instruction in method.instructions:
            if skip_until != -1:
                if instruction.pos == skip_until:
                    skip_until = -1
                else:
                    continue

            opcode = instruction.opcode
            operands = [
                InstructionField(operand, instruction, cf.constants)
                for operand in instruction.operands
            ]

            # Shortcut if
            if instruction.pos == shortif_pos:
                category = stack[-1].category
                stack.append(
                    Operand(
                        "((%(cond)s) ? %(sec)s : %(first)s)" % {
                            "cond": shortif_cond,
                            "first": stack.pop(),
                            "sec": stack.pop()
                        }, category))

            # Method calls
            if opcode >= 0xb6 and opcode <= 0xb9:
                name = operands[0].name
                desc = operands[0].descriptor
                if name in _PIT.TYPES:
                    operations.append(
                        Operation(instruction.pos,
                                  "write",
                                  type=_PIT.TYPES[name],
                                  field=stack.pop()))
                    stack.pop()
                elif name == "write":
                    if desc.find("[BII") >= 0:
                        stack.pop()
                        stack.pop()
                    operations.append(
                        Operation(
                            instruction.pos,
                            "write",
                            type="byte[]" if desc.find("[B") >= 0 else "byte",
                            field=stack.pop()))
                    stack.pop()
                elif desc in (
                        "(Ljava/lang/String;Ljava/io/DataOutputStream;)V",
                        "(Ljava/lang/String;Ljava/io/DataOutput;)V"):
                    stack.pop()
                    operations.append(
                        Operation(instruction.pos,
                                  "write",
                                  type="string16",
                                  field=stack.pop()))
                else:
                    descriptor = method_descriptor(desc)
                    num_arguments = len(descriptor[0])
                    if num_arguments > 0:
                        arguments = stack[-len(descriptor[0]):]
                    else:
                        arguments = []
                    for i in range(num_arguments):
                        stack.pop()
                    obj = "static" if opcode == 0xb8 else stack.pop()
                    if descriptor[1] != "void":
                        stack.append(
                            Operand(
                                "%s.%s(%s)" %
                                (obj, name, _PIT.join(arguments)),
                                2 if descriptor[1] in ("long",
                                                       "double") else 1))

                    if ("java.io.DataOutputStream" in descriptor[0]
                            or "java.io.DataOutput" in descriptor[0]):
                        operations += _PIT.sub_operations(
                            jar, cf, instruction, operands[0], [obj] +
                            arguments if obj != "static" else arguments)

            # Conditional statements and loops
            elif opcode in [0xc7, 0xc6] or opcode >= 0x99 and opcode <= 0xa6:
                if opcode == 0xc7:
                    condition = "%s == null" % stack.pop()
                elif opcode == 0xc6:
                    condition = "%s != null" % stack.pop()
                else:
                    if opcode <= 0x9e:  # if
                        comperation = opcode - 0x99
                        fields = [0, stack.pop()]
                    elif opcode <= 0xa4:  # if_icmp
                        comperation = opcode - 0x9f
                        fields = [stack.pop(), stack.pop()]
                    else:  # if_acmp
                        comperation = opcode - 0xa5
                        fields = [operands.pop(), operands.pop()]
                    if comperation == 0 and fields[0] == 0:
                        condition = fields[1]
                    else:
                        condition = "%s %s %s" % (
                            fields[1], _PIT.CONDITIONS[comperation], fields[0])
                operations.append(
                    Operation(instruction.pos, "if", condition=condition))
                operations.append(Operation(operands[0].target, "endif"))
                shortif_cond = condition

            elif opcode == 0xaa:  # tableswitch
                operations.append(
                    Operation(instruction.pos, "switch", field=stack.pop()))
                low = operands[0].value[1]
                for opr in range(1, len(operands)):
                    target = operands[opr].target
                    operations.append(
                        Operation(target, "case", value=low + opr - 1))
                operations.append(Operation(operands[0].target, "endswitch"))

            elif opcode == 0xab:  # lookupswitch
                operations.append(
                    Operation(instruction.pos, "switch", field=stack.pop()))
                for opr in range(1, len(operands)):
                    target = operands[opr].find_target(1)
                    operations.append(
                        Operation(target, "case",
                                  value=operands[opr].value[0]))
                operations.append(Operation(operands[0].target, "endswitch"))

            elif opcode == 0xa7:  # goto
                target = operands[0].target
                endif = _PIT.find_next(operations, instruction.pos, "endif")
                case = _PIT.find_next(operations, instruction.pos, "case")
                if case is not None and target > case.position:
                    operations.append(Operation(instruction.pos, "break"))
                elif endif is not None:
                    if target > instruction.pos:
                        endif.operation = "else"
                        operations.append(Operation(target, "endif"))
                        if len(stack) != 0:
                            shortif_pos = target
                    else:
                        endif.operation = "endloop"
                        _PIT.find_next(operations, target,
                                       "if").operation = "loop"
                elif target > instruction.pos:
                    skip_until = target

            # Math
            elif opcode >= 0x74 and opcode <= 0x77:
                category = stack[-1].category
                stack.append(Operand("(- %s)" % (stack.pop), category))
예제 #6
0
        def find_recipes(jar, cf, method, target_class, setter_names):
            # Temporary state variables
            tmp_recipes = []
            started_item = False
            next_push_is_val = False
            with_metadata = False
            block_substitute = None
            block_subs = {}
            rows = []
            make_count = 0
            metadata = 0
            recipe_target = None
            pushes_are_counts = False
            positions = True

            # Iterate over the methods instructions from the
            # bottom-up so we can catch the invoke/new range.
            for ins in method.instructions.reverse():
                # Find the call that stores the generated recipe.
                if ins.name == "invokevirtual" and not started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    # Parse the string method descriptor
                    desc = const["name_and_type"]["descriptor"]["value"]
                    args, returns = method_descriptor(desc)

                    # We've found the recipe storage method
                    if "java.lang.Object[]" in args:
                        started_item = True
                        pushes_are_counts = False
                        next_push_is_val = False
                        with_metadata = False
                        rows = []
                        make_count = 0
                        metadata = 0
                        recipe_target = None
                        method_name = const["name_and_type"]["name"]["value"]
                        positions = method_name == setter_names[0]
                        if positions:
                            block_subs = {}
                        else:
                            block_subs = []
                    elif superclass in args:
                        _class = const["class"]["name"]["value"]
                        sub_cf = jar.open_class(_class)
                        tmp_recipes += find_recipes(
                            jar, sub_cf, sub_cf.methods.find_one(args=args),
                            target_class, setter_names)
                # We've found the start of the recipe declaration (or the end
                # as it is in our case).
                elif ins.name == "new" and started_item:
                    started_item = False
                    if positions:
                        tmp_recipes.append({
                            "type": "shape",
                            "substitutes": block_subs,
                            "rows": rows,
                            "makes": make_count,
                            "recipe_target": recipe_target
                        })
                    else:
                        tmp_recipes.append({
                            "type": "shapeless",
                            "ingredients": block_subs,
                            "makes": make_count,
                            "recipe_target": recipe_target
                        })
                # The item/block to be substituted
                elif (ins.name == "getstatic" and started_item
                      and not pushes_are_counts):
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    cl_name = const["class"]["name"]["value"]
                    cl_field = const["name_and_type"]["name"]["value"]

                    block_substitute = (cl_name, cl_field)
                    if not positions:
                        block_subs.append(block_substitute)
                elif ins.name == "getstatic" and pushes_are_counts:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    cl_name = const["class"]["name"]["value"]
                    cl_field = const["name_and_type"]["name"]["value"]

                    recipe_target = (cl_name, cl_field, metadata)
                # Block string substitute value
                elif ins.name == "bipush" and next_push_is_val:
                    next_push_is_val = False
                    block_subs[chr(ins.operands[0][1])] = block_substitute
                # Number of items that the recipe makes
                elif ins.name == "bipush" and pushes_are_counts:
                    make_count = ins.operands[0][1]
                    if with_metadata:
                        metadata = make_count
                        with_metadata = False
                elif ins.name.startswith("iconst_") and pushes_are_counts:
                    make_count = int(ins.name[-1])
                    if with_metadata:
                        metadata = make_count
                        with_metadata = False
                # Recipe row
                elif ins.name == "ldc" and started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    if const['tag'] == ConstantType.STRING:
                        rows.append(const['string']['value'])
                # The Character.valueOf() call
                elif ins.name == "invokestatic":
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    # Parse the string method descriptor
                    desc = const["name_and_type"]["descriptor"]["value"]
                    args, returns = method_descriptor(desc)

                    if "char" in args and returns == "java.lang.Character":
                        # The next integer push will be the character value.
                        next_push_is_val = True
                elif ins.name == "invokespecial" and started_item:
                    const_i = ins.operands[0][1]
                    const = cf.constants[const_i]

                    name = const["name_and_type"]["name"]["value"]
                    if name == "<init>":
                        pushes_are_counts = True
                        if ("II" in const["name_and_type"]["descriptor"]
                            ["value"]):
                            with_metadata = True
            return tmp_recipes