새소식

모의해킹/Android

JEB Frida Code Generator

  • -
반응형

Jadx 1.4.1 이후 버전 부터 기본으로 지원하는 'Copy as Frida snippet' 기능을
JEB에서도 플러그인의 힘을 빌려 비슷하게나마 사용 가능하다.

Jadx의 Copy as frida snippet

JEB 플러그인은 iddoeldor의 frida-snippets 레포를 통해 다운로드 가능한데,
플러그인 동작 시 익셉션이 발생한다.

 

GitHub - iddoeldor/frida-snippets: Hand-crafted Frida examples

Hand-crafted Frida examples. Contribute to iddoeldor/frida-snippets development by creating an account on GitHub.

github.com


코드를 확인해보니 Linux/UNIX 환경에 맞추어 개발했는지 xclip 명령어를 사용하고 있어
Windows 환경에서 사용가능하도록 기타 자잘한 이슈와 함께 조금 수정했다.

사용 방법은 아래 코드를 JEB 설치 경로 하위의 scripts 폴더에 .py 확장자로 저장하고,
그 후 Frida 코드를 생성할 메소드 선택 후 Ctrl+Shift+Z 키를 통해 토글시키면
클립보드에 자동으로 복사되어 사용 가능하다.

#?shortcut=Mod1+Shift+Z
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from com.pnfsoftware.jeb.client.api import IScript
from com.pnfsoftware.jeb.core import RuntimeProjectUtil
from com.pnfsoftware.jeb.core.units.code.android import IDexUnit
import subprocess
from subprocess import Popen, PIPE
import os
import sys
#import clipboard


def arg_format(i):
    return 'arg_%d' % i


def generate_body_code(types, retval, method_name, orig_method_name, class_name):
    body_code = "\n\tconsole.log('[{}#{}] ' + JSON.strigify({{\n\t".format(
      FridaCodeGenerator.to_canonical_name(class_name), method_name)
    for i, typ in enumerate(types):
        body_code += '\t{}: {}, // {}\n\t'.format('a%d' % i, arg_format(i), typ)
  
    if retval != 'void':
        #body_code = '\n\tvar retval = this.{}.apply(this, arguments);{}\tretv: retval\n\t}});'.format(orig_method_name, body_code)
        body_code = '\n\tvar retval = this.{}.apply(this, arguments);{}\tretv: retval\n\t}}));'.format(orig_method_name, body_code)
    else:
        #body_code += '}});\n\tthis.{}.apply(this, arguments);'.format(method_name)
        body_code += '}}));\n\tthis.{}.apply(this, arguments);'.format(method_name)

    return body_code + '\n'


class JavaMethod(object):
    def __init__(self):
        self.class_name = None
        self.class_orig_name = None
        self.name = None
        self.orig_name = None
        self.arg = []
        self.retType = None

    def get_parameters(self):
        return self.arg

    def get_return_type(self):
        return self.retType

    def get_name(self):
        return self.name

    def get_orig_name(self):
        return self.orig_name

    def get_class_orig_name(self):
        return self.class_orig_name

    def get_class_name(self):
        return self.class_name

    def __str__(self):
        return 'JavaMethod[name: %s, orig_name: %s, args: %s, return type: %s]' % (
            self.name, self.orig_name, self.arg, self.retType)


class FridaCodeGenerator(IScript):

    @staticmethod
    def to_canonical_name(mname):
        mname = mname.replace('/', '.')
        return {
            'C': 'char',
            'I': 'int',
            'B': 'byte',
            'Z': 'boolean',
            'F': 'float',
            'D': 'double',
            'S': 'short',
            'J': 'long',
            'V': 'void',
            'L': mname[1:-1],
            '[': mname
        }[mname[0]]

    def run(self, ctx):
        project = ctx.getEnginesContext().getProjects()[0]  # Get current project(IRuntimeProject)
        self.dexunit = RuntimeProjectUtil.findUnitsByType(project, IDexUnit, False)[0]  # Get dex context, needs >=V2.2.1
        try:
            self.current_unit = ctx.getFocusedView().getActiveFragment().getUnit()  # Get current Source Tab in Focus
            java_class = self.current_unit.getClassElement().getName()
            current_addr = ctx.getFocusedView().getActiveFragment().getActiveAddress()
            m = FridaCodeGenerator.get_decompiled_method(self.dexunit, current_addr, java_class)
            method_name = m.get_name()
            class_name = FridaCodeGenerator.to_canonical_name(m.get_class_orig_name())
            return_type = FridaCodeGenerator.to_canonical_name(str(m.get_return_type()))
            if method_name == '<clinit>':
                raise Exception('Class initializer')
            args_code = ', '.join([arg_format(i) for i in range(len(m.get_parameters()))])

            if method_name == '<init>': method_name = '$init'

            types = [FridaCodeGenerator.to_canonical_name(param) for param in m.get_parameters()]
            # TODO get original type class names
            type_code = ', '.join(["'{0}'".format(t) for t in types])
            body_code = generate_body_code(types, return_type, method_name, m.get_orig_name(), m.get_class_name())
            hook = "Java.use('{class_name}').{method}.overload({sig}).implementation = function({args}) {{{body}}}".format(
                class_name=class_name, 
                method=m.get_orig_name() if method_name != '$init' else method_name, 
                sig=type_code, 
                args=args_code, 
                body=body_code
            )
            print(hook)
            # copy to system's clipboard
            #Popen(['xclip', '-sel', 'c', '-i'], stdin=PIPE).communicate(input=(hook.encode()))
            subprocess.Popen(['clip'], stdin=subprocess.PIPE).communicate(input=(hook.encode()))
            #clipboard.copy(hook.encode())
        except Exception as e:
            print(e)
            ctx.displayMessageBox(None, 'Place the cursor in the function you want to generate the Frida code', None, None)

    @staticmethod
    def get_decompiled_method(dex, addr, class_orig_name):
        method_info = JavaMethod()
        method_info.orig_name = dex.getMethod(addr).getName(False)
        msig = addr.split('+')[0]
        infos = str(msig).split('->')
        if len(infos) == 2:
            method_info.class_name = infos[0]
            method_info.class_orig_name = class_orig_name
            if len(infos[1].split('(')) == 2:
                method_info.name = infos[1].split('(')[0]
            if len(infos[1].split(')')) == 2:
                method_info.retType = infos[1].split(')')[1]
            if len(infos[1].split('(')) == 2 and len(infos[1].split(')')) == 2:
                args = infos[1].split('(')[-1].split(')')[0]
                while args:
                    if args[0] in ['C', 'I', 'B', 'Z', 'F', 'D', 'S', 'J', 'V']:
                        method_info.arg.append(str(args[0]))
                        args = args[1:]
                    elif args[0] == '[':
                        if args[1] == 'L':
                            offset = args.find(';')
                            method_info.arg.append(str(args[0:offset + 1]))
                            args = args[offset + 1:]
                        else:
                            method_info.arg.append(str(args[0:2]))
                            args = args[2:]
                    elif args[0] == 'L':
                        offset = args.find(";")
                        method_info.arg.append(str(args[0:offset + 1]))
                        args = args[offset + 1:]
                print(method_info)
        return method_info
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.