ARTist Development Guide

As described in this post, recent updates to ARTist’s internal workings require an update of this documentation. This note will vanish as soon as the new documentation is published.
Until that, you can still use the description below to some extend, but expect some things to not work anymore.

Basic Information for programming with the ARTist Code Injection ToolKit.

Build Setup

Before you can start developing with ARTist, prepare yourself with the Build Setup Guide.

Development Steps (Overview)

  • CodeLib Creation: Additional Functionality is implemented in Java
  • Injection Definition: Define where the additional code should get placed.
    • See this document, further down.
    • Also check the examples in the ARTist git (submodule): (<artist>/doc/examples/injection/*.examples)
  • ArtistGUI: Deployment application for ARTist (optional: also CodeLib)
    • Build ArtistGui and (mandatory) bundle dex2oat + libraries and (optional) CodeLib.apk .
      • see e.g.: ArtistGui/scripts/build_ssh_nougat_7.1.sh
    • Settings: Choose/Import CodeLib and configure settings.
    • Select application for injection

Development Hints

  • artist is implemented as an compiler optimization pass for dex2oat (art::HOptimization).
    • It runs as the last optimization pass, after all have run.
  • An optimization pass operates on method Level
    • One method(-graph) is accessible as an art::HGraph object.
  • You can traverse the methods graph via your own implementation of the visitor pattern, by extending art:HGraphVisitor.
    • We already implemented a generic visitor to make your life as a developer easier (see art::HInjectionVisitor).
  • You can inject calls to your java code-library very easy when you use artist.
    1. Implemented your functionality in the java CodeLib example or create your personal CodeLib
    2. Generate your codelib header/source files and place them in <art/>compiler/optimizing/artist/env/
    3. Define your injections in HInjections::buildInjections() with four simple attributes:
      • What: Java method signature that gets injected during artists compilation process.
      • Parameters: Java method parameters, that get passed to the injected java method.
      • Where: Target method signature, in relationship to which the injection will get placed. It’s possible to target all methods and there is partial matching in place, that you are able to inject e.g. all onCreate() method.
      • Where-Parameter: The relationship to the method signature, defined in Where. Your injections can get placed inside the method, you defined in Where (Method Start & End) and it can get placed before or after a call to the method, defined in Where.

AOSP Version specifics

You can limit your code changes to a specific Android version with the help of pre-processor #defines:

  • BUILD_MARSHMALLOW
  • BUILD_NOUGAT
  • BUILD_OREO

Example:

#ifdef BUILD_OREO
<CODE>
#endif

Injection Definition

You can define your injections in the file: <art>/compiler/optimizing/artist/modules/generic/injections.h.

There’s a special factory method, that gets called ba ARTist during the runtime, to initialize the Injections. it is called HInjections::buildInjections()

If there are no examples present, check the example injection definition in <art>/compiler/optimizing/artist/doc/examples/injection/*

What [Method Signature]

The method that should get injected. This must be written down as a fully-qualified java method signature, in the internal JVM method notation format, defined in the official document: The class File Format.

Example: Lsaarland/cispa/artist/codelib/CodeLib;traceLog()V for:

package saarland.cispa.artist.codelib;
public class CodeLib {
    public void traceLog() {}
}

Currently only calls to methods with primitive parameters are allowed.

No arrays are allowed as parameters, also no classes.

The return values of injected method calls DON’T get used by the Generic Instruction Injection implementation at the moment, but they could.

Parameter(s) [MethodCall Parameter]

List of primitive parameters, that get passed to the injected method call, specified in the What section.

No arrays are allowed as parameters, also no classes.

Where [Target Signature: Full, Partial or Generic: *]

A target can be a specific method signature or you can also target all methods at once. The target method signature is the simpler Java source code notation you are used to. The target signature is also matched partially, so you can skip return types, package names and parameters as it suits you, e.g.:

  • Specific: void saarland.cispa.artist.codelib.CodeLib.traceLog() => Targets only this specific method.
  • Partial: .onCreate( => Targets all methods with the name onCreate, to inject code in relationship to all of android’s Activity, Service, etc onCreate methods.
  • Generic: *\ => Targets all methods.

  • <Method Signature> e.g.: org.example.JavaClass.helloMethod()
  • Target::GENERIC_TARGET:*

Where-Parameter [Relative Position to the Target]

Relative injection position to the Target. This can be either one of the four:

  • METHOD_START
  • METHOD_END
  • METHOD_CALL_BEFORE
  • METHOD_CALL_AFTER

Examples

Example code snippets, that demonstrate proper injection definition and the code placement in injected applications

CodeLib

This is an excerpt of java code from the CodeLib. Calls to this code get injected by artist.

package saarland.cispa.artist.codelib;

public class CodeLib {
    public static CodeLib INSTANCE = new CodeLib();
    // <SNIP>
    public void traceLog() {
        final String callingMethodName = getCallingMethodName();
        Log.d(TAG, "Caller -> " + callingMethodName);
    }
    // </SNIP>
}

Application (Original Code)

This is the original application that gets injected by ARTist.

package org.example;

class JavaClass {
    public JavaClass() {
    }
    public void helloMethod() {
        doStuff();
        doMoreStuff();
        doStuffAgain();
        return;
    }
    public void main(String[] args){
        helloMethod();
    }
}

Injection Definition

  • What: Lsaarland/cispa/artist/codelib/CodeLib;traceLog()V
  • Parameters: NONE
  • Where: org.example.JavaClass.helloMethod()
  • Where-Parameter: METHOD_START | METHOD_END | METHOD_CALL_BEFORE | METHOD_CALL_AFTER

Place injection definitions in injections.h, Function: HInjections::buildInjections()

static std::vector<Injection> HInjections::buildInjections() {
  std::vector<Injection> injections;
   
  // Matches the method, Params get ignored
  const std::string HELLO_METHOD_TARGET = "org.example.JavaClass.helloMethod("
  
  // Example: Trace Logging Injection on 4 Positions
  Injection trace_logging_mstart(
      CodeLib::_M_SAARLAND_CISPA_ARTIST_CODELIB_CODELIB__TRACELOG____V,
      std::vector<std::shared_ptr<Parameter>>(),  // No params
      Target(HELLO_METHOD_TARGET, InjectionTarget::METHOD_START));
  injections.push_back(trace_logging_mstart);
  
  Injection trace_logging_mend(
      CodeLib::_M_SAARLAND_CISPA_ARTIST_CODELIB_CODELIB__TRACELOG____V,
      std::vector<std::shared_ptr<Parameter>>(),  // No params
      Target(HELLO_METHOD_TARGET, InjectionTarget::METHOD_END));
  injections.push_back(trace_logging_mend);
  
  Injection trace_logging_cbef(
      CodeLib::_M_SAARLAND_CISPA_ARTIST_CODELIB_CODELIB__TRACELOG____V,
      std::vector<std::shared_ptr<Parameter>>(),  // No params
      Target(HELLO_METHOD_TARGET, InjectionTarget::METHOD_CALL_AFTER));
  injections.push_back(trace_logging_cbef);
  
  Injection trace_logging_caft(
      CodeLib::_M_SAARLAND_CISPA_ARTIST_CODELIB_CODELIB__TRACELOG____V,
      std::vector<std::shared_ptr<Parameter>>(),  // No params
      Target(HELLO_METHOD_TARGET, InjectionTarget::METHOD_CALL_BEFORE));
  injections.push_back(trace_logging_caft);
  // => Generate your own codelib.h/.cc and check available methods (signatures) for injection.
  return injections;
}

Application (Injected Code)

The Original application now has four injected methodcalls after the (re)compilation on the four above definied postions

package org.example;

class JavaClass {
    public JavaClass() {
    }
    public void helloMethod() {
        /*Target: METHOD_START*/saarland.cispa.artist.codelib.CodeLib.traceLog()
        doStuff();
        doMoreStuff();
        doStuffAgain();
        /*Target: METHOD_END*/saarland.cispa.artist.codelib.CodeLib.traceLog()
        return;
    }
    public void main(String[] args){
        /*Target: METHOD_CALL_BEFORE*/saarland.cispa.artist.codelib.CodeLib.traceLog()
        helloMethod();
        /*Target: METHOD_CALL_AFTER*/saarland.cispa.artist.codelib.CodeLib.traceLog()
        otherMethod();
        return;
    }
}

Generic Injection Definition

Example Injection Definitions arguments, based on the four described parameters:

What (Method Signature) Parameter(s) Where (Target Signature: Full, Partial or Generic: *) Where-Parameter (Relative position to target)
Lde/infsec/tainttracking/taintlib/TaintLib;traceLog()V <Nothing> * METHOD_START
Lde/infsec/tainttracking/taintlib/TaintLib;traceLog(II)V 1024,42 void java.lang.String.equals(java.lang.Object) METHOD_CALL_BEFORE
Lde/infsec/tainttracking/taintlib/TaintLib;traceLog(L)V 5000000000 .equals( METHOD_END
Lde/infsec/tainttracking/taintlib/TaintLib;traceLog(C)V a * METHOD_END
Lde/infsec/tainttracking/taintlib/TaintLib;traceLog(II)V 1024,42 java.lang.String.equals(java.lang.Object) METHOD_CALL_AFTER