Skip to content

Commit

Permalink
First version of Flutter support for Android and old iOS versions. Al…
Browse files Browse the repository at this point in the history
…so improved error handling while working with pattern based hooking.
  • Loading branch information
monkeywave committed Oct 8, 2024
1 parent 8db416b commit 46832d6
Show file tree
Hide file tree
Showing 18 changed files with 655 additions and 164 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</p>

# friTap
![version](https://img.shields.io/badge/version-1.2.2.0-blue) [![PyPI version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&r=r&ts=1683906897&type=6e&v=1.2.2.0&x2=0)](https://badge.fury.io/py/friTap)
![version](https://img.shields.io/badge/version-1.2.2.8-blue) [![PyPI version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=py&r=r&ts=1683906897&type=6e&v=1.2.2.8&x2=0)](https://badge.fury.io/py/friTap)

The goal of this project is to help researchers to analyze traffic encapsulated in SSL or TLS. For details have a view into the [OSDFCon webinar slides](assets/friTapOSDFConwebinar.pdf) or in [this blog post](https://lolcads.github.io/posts/2022/08/fritap/).

Expand Down
2 changes: 2 additions & 0 deletions agent/android/android_agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { mbedTLS_execute } from "./mbedTLS_android.js";
import { boring_execute } from "./openssl_boringssl_android.js";
import { java_execute} from "./android_java_tls_libs.js";
import { cronet_execute } from "./cronet_android.js";
import { flutter_execute } from "./flutter_android.js";


var plattform_name = "linux";
Expand Down Expand Up @@ -77,6 +78,7 @@ export function load_android_hooking_agent() {
[/.*libssl_sb.so/, invokeHookingFunction(boring_execute)],
[/.*libssl\.so/, invokeHookingFunction(boring_execute)],
[/.*cronet.*\.so/, invokeHookingFunction(cronet_execute)],
[/.*flutter.*\.so/, invokeHookingFunction(flutter_execute)],
[/.*libgnutls\.so/, invokeHookingFunction(gnutls_execute)],
[/.*libwolfssl\.so/, invokeHookingFunction(wolfssl_execute)],
[/.*libnspr[0-9]?\.so/,invokeHookingFunction(nss_execute)],
Expand Down
14 changes: 7 additions & 7 deletions agent/android/conscrypt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { devlog, log } from "../util/log.js";
import { devlog, devlog_error, log } from "../util/log.js";
import { getAndroidVersion } from "../util/process_infos.js";

function findProviderInstallerImplFromClassloaders(currentClassLoader: Java.Wrapper, backupImplementation: any) : Java.Wrapper | null {
Expand All @@ -12,8 +12,8 @@ function findProviderInstallerImplFromClassloaders(currentClassLoader: Java.Wrap
break
} catch (error) {
if(!error.toString().includes("java.lang.ClassNotFoundException")){
devlog("Error in hooking ProviderInstallerImpl (findProviderInstallerImplFromClassloaders):")
devlog("[-] Error message: (findProviderInstallerImplFromClassloaders): "+error);
devlog_error("Error in hooking ProviderInstallerImpl (findProviderInstallerImplFromClassloaders):")
devlog_error("Error message: (findProviderInstallerImplFromClassloaders): "+error);
}
providerInstallerImpl = null;
// On error we return null
Expand Down Expand Up @@ -43,8 +43,8 @@ function findProviderInstallerFromClassloaders(currentClassLoader: Java.Wrapper,
} catch (error) {

if(!error.toString().includes("java.lang.ClassNotFoundException")){
devlog("Error in hooking ProviderInstallerImpl (findProviderInstallerFromClassloaders):")
devlog("[-] Error message (findProviderInstallerFromClassloaders): "+error);
devlog_error("Error in hooking ProviderInstallerImpl (findProviderInstallerFromClassloaders):")
devlog_error("Error message (findProviderInstallerFromClassloaders): "+error);
}
providerInstaller = null;
// On error we return null
Expand Down Expand Up @@ -131,9 +131,9 @@ export function execute() {
}
}
}catch (error) {
devlog("Some error in hooking the Providerinstaller")
devlog_error("Some error in hooking the Providerinstaller")
if(!error.toString().includes("java.lang.ClassNotFoundException")){
devlog("[-] Error message: "+error);
devlog_error("[-] Error message: "+error);
}
// As it is not available, do nothing
}
Expand Down
21 changes: 3 additions & 18 deletions agent/android/cronet_android.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import {Cronet } from "../ssl_lib/cronet.js";
import { socket_library } from "./android_agent.js";
import {PatternBasedHooking } from "../shared/pattern_based_hooking.js";
import {PatternBasedHooking, get_CPU_specific_pattern } from "../shared/pattern_based_hooking.js";
import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog } from "../util/log.js";

Expand All @@ -27,28 +27,13 @@ export class Cronet_Android extends Cronet {
},
"arm": {
primary: "2D E9 F0 43 89 B0 04 46 40 6B D0 F8 2C 01 00 28 49 D0", // Primary pattern
fallback: "2D E9 F0 43 89 B0 04 46 40 6B D0 F8 2C 01 00 28 49 D0" // Fallback pattern (right now we don't have any)
fallback: "2D E9 F0 41 86 B0 04 46 40 6B D0 F8 30 01 00 28 53 D0" // Fallback pattern
}
};
}



// Simulated JSON object (you can replace this with actual file loading)


private get_CPU_specific_pattern(): { primary: string; fallback: string } {
let arch = Process.arch.toString(); // Get architecture, e.g., "x64", "arm64"
if(arch == "ia32"){
arch = "x86"
}

if (this.default_pattern[arch]) {
return this.default_pattern[arch]; // Return the pattern for the architecture
} else {
throw new Error(`No patterns found for CPU architecture: ${arch}`);
}
}

install_key_extraction_hook(){
const cronetModule = Process.findModuleByName(this.module_name);
Expand All @@ -62,7 +47,7 @@ export class Cronet_Android extends Cronet {
}else{
// This are the default patterns for hooking ssl_log_secret in BoringSSL inside Cronet
hooker.hookModuleByPattern(
this.get_CPU_specific_pattern(),
get_CPU_specific_pattern(this.default_pattern),
(args) => {
this.dumpKeys(args[1], args[0], args[2]); // Hook args passed to dumpKeys
}
Expand Down
35 changes: 10 additions & 25 deletions agent/android/flutter_android.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

import { Flutter } from "../ssl_lib/flutter.js";
import { socket_library } from "./android_agent.js";
import {PatternBasedHooking } from "../shared/pattern_based_hooking.js";
import {PatternBasedHooking, get_CPU_specific_pattern } from "../shared/pattern_based_hooking.js";
import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog } from "../util/log.js";
import { devlog, devlog_error } from "../util/log.js";


export class Flutter_Android extends Flutter {
Expand All @@ -15,40 +15,25 @@ export class Flutter_Android extends Flutter {
this.default_pattern = {
"x64": {
primary: "55 41 57 41 56 41 55 41 54 53 48 83 EC 48 48 8B 47 68 48 83 B8 20 02 00 00 00 0F 84 FE 00 00 00", // Primary pattern
fallback: "55 41 57 41 56 41 55 41 54 53 48 83 EC 48 48 8B 47 68 48 83 B8 20 02 00 00 00" // Fallback pattern
fallback: "55 41 57 41 56 41 55 41 54 53 48 83 EC 38 48 8B 47 68 48 83 B8 10 02 00 00 00 0F 84 19 01 00 00" // Fallback pattern
},
"x86": {
primary: "55 53 57 56 83 EC 4C E8 00 00 00 00 5B 81 C3 A9 CB 13 00 8B 44 24 60 8B 40 34", // Primary pattern
fallback: "55 53 57 56 83 EC 4C E8 00 00 00 00 5B 81 C3 A9 CB 13 00 8B 44 24 60" // Fallback pattern
fallback: "55 89 E5 53 57 56 83 E4 F0 83 EC 50 E8 00 00 00 00" // Fallback pattern
},
"arm64": {
primary: "3F 23 03 D5 FF C3 01 D1 FD 7B 04 A9 F6 57 05 A9 F4 4F 06 A9 FD 03 01 91 08 34 40 F9 08 11 41 F9 C8 07 00 B4", // Primary pattern
fallback: "3F 23 03 D5 FF 03 02 D1 FD 7B 04 A9 F7 2B 00 F9 F6 57 06 A9 F4 4F 07 A9 FD 03 01 91 08 34 40 F9 08 11 41 F9 E8 0F 00 B4" // Fallback pattern
primary: "E0 03 13 AA E2 03 16 AA 6D 62 FA 17", // Primary pattern
fallback: "FF 83 01 D1 F6 1B 00 F9 F5 53 04 A9 F3 7B 05 A9 08 34 40 F9 08 09 41 F9 68 07 00 B4" // Fallback pattern
},
"arm": {
primary: "2D E9 F0 43 89 B0 04 46 40 6B D0 F8 2C 01 00 28 49 D0", // Primary pattern
fallback: "2D E9 F0 43 89 B0 04 46 40 6B D0 F8 2C 01 00 28 49 D0" // Fallback pattern (right now we don't have any)
fallback: "2D E9 F0 41 86 B0 04 46 40 6B D0 F8 30 01 00 28 53 D0" // Fallback pattern
}
};
}



// Simulated JSON object (you can replace this with actual file loading)


private get_CPU_specific_pattern(): { primary: string; fallback: string } {
let arch = Process.arch.toString(); // Get architecture, e.g., "x64", "arm64"
if(arch == "ia32"){
arch = "x86"
}

if (this.default_pattern[arch]) {
return this.default_pattern[arch]; // Return the pattern for the architecture
} else {
throw new Error(`No patterns found for CPU architecture: ${arch}`);
}
}

install_key_extraction_hook(){
const flutterModule = Process.findModuleByName(this.module_name);
Expand All @@ -62,7 +47,7 @@ export class Flutter_Android extends Flutter {
}else{
// This are the default patterns for hooking ssl_log_secret in BoringSSL inside Flutter
hooker.hookModuleByPattern(
this.get_CPU_specific_pattern(),
get_CPU_specific_pattern(this.default_pattern),
(args) => {
this.dumpKeys(args[1], args[0], args[2]); // Hook args passed to dumpKeys
}
Expand All @@ -83,7 +68,7 @@ export function flutter_execute(moduleName:string, is_base_hook: boolean){
try {
flutter.execute_hooks();
}catch(error_msg){
devlog(`flutter_execute error: ${error_msg}`)
devlog_error(`flutter_execute error: ${error_msg}`)
}

if (is_base_hook) {
Expand All @@ -94,7 +79,7 @@ export function flutter_execute(moduleName:string, is_base_hook: boolean){
(global as any).init_addresses[moduleName] = init_addresses;
}
}catch(error_msg){
devlog(`flutter_execute base-hook error: ${error_msg}`)
devlog_error(`flutter_execute base-hook error: ${error_msg}`)
}
}

Expand Down
26 changes: 21 additions & 5 deletions agent/ios/cronet_ios.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@

import {Cronet } from "../ssl_lib/cronet.js";
import { socket_library } from "./ios_agent.js";
import {PatternBasedHooking } from "../shared/pattern_based_hooking.js";
import {PatternBasedHooking, get_CPU_specific_pattern } from "../shared/pattern_based_hooking.js";
import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog } from "../util/log.js";

export class Cronet_Android extends Cronet {
export class Cronet_iOS extends Cronet {
private default_pattern: { [arch: string]: { primary: string; fallback: string } };

constructor(public moduleName:string, public socket_library:String, is_base_hook: boolean){
super(moduleName,socket_library,is_base_hook);

this.default_pattern = {
"arm64": {
primary: "FF 83 01 D1 F6 57 03 A9 F4 4F 04 A9 FD 7B 05 A9 FD 43 01 91 08 34 40 F9 08 51 41 F9 48 08 00 B4", // Primary pattern
fallback: "3F 23 03 D5 FF 03 02 D1 FD 7B 04 A9 F7 2B 00 F9 F6 57 06 A9 F4 4F 07 A9 FD 03 01 91 08 34 40 F9 08 11 41 F9 E8 0F 00 B4" // Fallback pattern
}
};
}

install_key_extraction_hook(){
const cronetModule = Process.findModuleByName(this.module_name);
const hooker = new PatternBasedHooking(cronetModule);

if (isPatternReplaced()){
devlog("Hooking libcronet functions by pattern");
hooker.hook_DumpKeys(this.module_name,"libcronet.so",patterns,(args: any[]) => {
devlog("Hooking Cronet functions by pattern\nThis is still untested and might fail");
hooker.hook_DumpKeys(this.module_name,"Cronet",patterns,(args: any[]) => {
this.dumpKeys(args[1], args[0], args[2]); // Unpack args into dumpKeys
});
}else{
// This are the default patterns for hooking ssl_log_secret in BoringSSL inside Cronet
hooker.hookModuleByPattern(
get_CPU_specific_pattern(this.default_pattern),
(args) => {
this.dumpKeys(args[1], args[0], args[2]); // Hook args passed to dumpKeys
}
);
}


Expand All @@ -37,7 +53,7 @@ export class Cronet_Android extends Cronet {


export function cronet_execute(moduleName:string, is_base_hook: boolean){
var cronet = new Cronet_Android(moduleName,socket_library,is_base_hook);
var cronet = new Cronet_iOS(moduleName,socket_library,is_base_hook);
cronet.execute_hooks();

if (is_base_hook) {
Expand Down
74 changes: 74 additions & 0 deletions agent/ios/flutter_ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

import { Flutter } from "../ssl_lib/flutter.js";
import { socket_library } from "./ios_agent.js";
import {PatternBasedHooking, get_CPU_specific_pattern } from "../shared/pattern_based_hooking.js";
import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog, devlog_error } from "../util/log.js";


export class Flutter_iOS extends Flutter {
private default_pattern: { [arch: string]: { primary: string; fallback: string } };

constructor(public moduleName:string, public socket_library:String, is_base_hook: boolean){
super(moduleName,socket_library,is_base_hook);

this.default_pattern = {
"arm64": {
primary: "FF 83 01 D1 F6 57 03 A9 F4 4F 04 A9 FD 7B 05 A9 FD 43 01 91 08 34 40 F9 08 51 41 F9 48 08 00 B4", // Primary pattern
fallback: "3F 23 03 D5 FF 03 02 D1 FD 7B 04 A9 F7 2B 00 F9 F6 57 06 A9 F4 4F 07 A9 FD 03 01 91 08 34 40 F9 08 11 41 F9 E8 0F 00 B4" // Fallback pattern
}
};
}




install_key_extraction_hook(){
const flutterModule = Process.findModuleByName(this.module_name);
const hooker = new PatternBasedHooking(flutterModule);

if (isPatternReplaced()){
devlog("Hooking Flutter functions by patterns from JSON file");
hooker.hook_DumpKeys(this.module_name,"Flutter",patterns,(args: any[]) => {
this.dumpKeys(args[1], args[0], args[2]); // Unpack args into dumpKeys
});
}else{
// This are the default patterns for hooking ssl_log_secret in BoringSSL inside Flutter
hooker.hookModuleByPattern(
get_CPU_specific_pattern(this.default_pattern),
(args) => {
this.dumpKeys(args[1], args[0], args[2]); // Hook args passed to dumpKeys
}
);
}

}

execute_hooks(){
this.install_key_extraction_hook();
}

}


export function flutter_execute(moduleName:string, is_base_hook: boolean){
var flutter = new Flutter_iOS(moduleName,socket_library,is_base_hook);
try {
flutter.execute_hooks();
}catch(error_msg){
devlog_error(`flutter_execute error: ${error_msg}`)
}

if (is_base_hook) {
try {
const init_addresses = flutter.addresses[moduleName];
// ensure that we only add it to global when we are not
if (Object.keys(init_addresses).length > 0) {
(global as any).init_addresses[moduleName] = init_addresses;
}
}catch(error_msg){
devlog_error(`flutter_execute base-hook error: ${error_msg}`)
}
}

}
4 changes: 3 additions & 1 deletion agent/ios/ios_agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { log, devlog } from "../util/log.js";
import { getModuleNames, ssl_library_loader, invokeHookingFunction } from "../shared/shared_functions.js";
import { boring_execute } from "./openssl_boringssl_ios.js";
import { cronet_execute } from "./cronet_ios.js"
import { flutter_execute } from "./flutter_ios.js"


var plattform_name = "darwin";
Expand Down Expand Up @@ -59,7 +60,8 @@ function hook_iOS_SSL_Libs(module_library_mapping: { [key: string]: Array<[any,
export function load_ios_hooking_agent() {
module_library_mapping[plattform_name] = [
[/.*libboringssl\.dylib/, invokeHookingFunction(boring_execute)],
[/.*cronet.*\.dylib/, invokeHookingFunction(cronet_execute)]]
[/.*cronet.*\.dylib/, invokeHookingFunction(cronet_execute)],
[/.*flutter.*\.dylib/, invokeHookingFunction(flutter_execute)]]

hook_iOS_SSL_Libs(module_library_mapping, true);
hook_iOS_Dynamic_Loader(module_library_mapping, false);
Expand Down
4 changes: 2 additions & 2 deletions agent/linux/cronet_linux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {PatternBasedHooking } from "../shared/pattern_based_hooking.js";
import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog } from "../util/log.js";

export class Cronet_Android extends Cronet {
export class Cronet_Linux extends Cronet {

constructor(public moduleName:string, public socket_library:String, is_base_hook: boolean){
super(moduleName,socket_library,is_base_hook);
Expand Down Expand Up @@ -37,7 +37,7 @@ export class Cronet_Android extends Cronet {


export function cronet_execute(moduleName:string, is_base_hook: boolean){
var cronet = new Cronet_Android(moduleName,socket_library,is_base_hook);
var cronet = new Cronet_Linux(moduleName,socket_library,is_base_hook);
cronet.execute_hooks();

if (is_base_hook) {
Expand Down
8 changes: 4 additions & 4 deletions agent/macos/cronet_macos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { patterns, isPatternReplaced } from "../ssl_log.js"
import { devlog } from "../util/log.js";


export class Cronet_Android extends Cronet {
export class Cronet_MacOS extends Cronet {

constructor(public moduleName:string, public socket_library:String, is_base_hook: boolean){
super(moduleName,socket_library,is_base_hook);
Expand All @@ -17,8 +17,8 @@ export class Cronet_Android extends Cronet {
const hooker = new PatternBasedHooking(cronetModule);

if (isPatternReplaced()){
devlog("Hooking libcronet functions by pattern");
hooker.hook_DumpKeys(this.module_name,"libcronet.so",patterns,(args: any[]) => {
devlog("Hooking Cronet functions by pattern");
hooker.hook_DumpKeys(this.module_name,"Cronet",patterns,(args: any[]) => {
this.dumpKeys(args[1], args[0], args[2]); // Unpack args into dumpKeys
});
}
Expand All @@ -38,7 +38,7 @@ export class Cronet_Android extends Cronet {


export function cronet_execute(moduleName:string, is_base_hook: boolean){
var cronet = new Cronet_Android(moduleName,socket_library,is_base_hook);
var cronet = new Cronet_MacOS(moduleName,socket_library,is_base_hook);
cronet.execute_hooks();

if (is_base_hook) {
Expand Down
Loading

0 comments on commit 46832d6

Please sign in to comment.