Adding Features to friTap¶
This guide covers how to add new features to friTap, including SSL/TLS library support, platform support, and analysis capabilities.
Overview¶
friTap's architecture is designed to be extensible:
- Python Host: CLI interface, process management, output formatting
- TypeScript Agent: SSL library hooking, cross-platform compatibility
- Plugin System: Modular SSL library implementations
- Platform Abstraction: OS-specific code organization
Architecture Overview¶
friTap is built on a multi-component architecture that combines Python orchestration with frida's dynamic instrumentation:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Python CLI │ │ Frida Engine │ │ Target Process │
│ (friTap.py) │────│ (Runtime) │────│ (Hooked) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
┌─────────┐ ┌─────────────┐ ┌─────────────┐
│SSL Logic│ │TypeScript │ │SSL Libraries│
│& PCAP │ │Agent │ │(OpenSSL, │
│Gen │ │(_ssl_log.js)│ │BoringSSL, │
└─────────┘ └─────────────┘ │NSS, etc.) │
└─────────────┘
Components¶
- Python CLI (
friTap.py
): Main orchestration layer - SSL Logger (
ssl_logger.py
): Core logging and processing engine - PCAP Generator (
pcap.py
): Network packet creation and manipulation - Frida Agent (
agent/ssl_log.ts
): TypeScript-based instrumentation agent (this is build viafrida-compile
from the TypeScript code in the agent/ folder) - Platform Agents: Platform-specific SSL library hooks
Adding SSL/TLS Library Support¶
Adding support for a new SSL/TLS library involves both TypeScript agent code and Python integration.
Step 1: Research the Library¶
Before implementation, thoroughly analyze the target library:
# Analyze library structure
objdump -T new_library.so | grep -E "(ssl|tls|read|write)"
# Study function signatures
readelf -s new_library.so | grep FUNC
# Check for debug symbols
file new_library.so
objdump -h new_library.so | grep debug
# Analyze with Ghidra/IDA Pro for patterns
# Document function calling conventions
# Identify key structures and data types
Step 2: Create TypeScript Implementation¶
Create the main library implementation in agent/ssl_lib/
:
// agent/ssl_lib/new_library.ts
import { log, devlog, devlog_error } from "../util/log.js";
import { SharedStructures } from "../shared/shared_structures.js";
export class NewLibraryHooks {
private moduleName: string;
private baseAddress: NativePointer;
private addresses: { [key: string]: NativePointer } = {};
constructor(moduleName: string) {
this.moduleName = moduleName;
this.baseAddress = Module.getBaseAddress(moduleName);
this.addresses = this.getAddresses();
}
/**
* Get function addresses for the new library.
* Try symbol-based detection first, then patterns.
*/
private getAddresses(): { [key: string]: NativePointer } {
const addresses: { [key: string]: NativePointer } = {};
try {
// Symbol-based detection (preferred)
addresses["NewSSL_Read"] = Module.getExportByName(this.moduleName, "NewSSL_Read");
addresses["NewSSL_Write"] = Module.getExportByName(this.moduleName, "NewSSL_Write");
addresses["NewSSL_GetKeys"] = Module.getExportByName(this.moduleName, "NewSSL_GetKeys");
devlog(`[${this.moduleName}] Found functions via symbols`);
} catch (error) {
devlog_error(`Symbol-based detection failed for ${this.moduleName}: ${error}`);
// Fall back to pattern-based detection
this.findFunctionsByPatterns(addresses);
}
return addresses;
}
/**
* Find functions using byte patterns when symbols are not available.
*/
private findFunctionsByPatterns(addresses: { [key: string]: NativePointer }): void {
const module = Process.getModuleByName(this.moduleName);
// Define patterns for different architectures
const patterns = {
"arm64": {
"NewSSL_Read": "1F 20 03 D5 ?? ?? ?? ?? F4 4F 01 A9",
"NewSSL_Write": "FF 83 00 D1 ?? ?? ?? ?? F4 4F 02 A9"
},
"x64": {
"NewSSL_Read": "55 48 89 E5 ?? ?? ?? ?? 48 83 EC ??",
"NewSSL_Write": "55 48 89 E5 ?? ?? ?? ?? 48 8B 45 ??"
}
};
const archPatterns = patterns[Process.arch as keyof typeof patterns];
if (!archPatterns) {
devlog_error(`No patterns defined for architecture: ${Process.arch}`);
return;
}
// Search for each function pattern
for (const [funcName, pattern] of Object.entries(archPatterns)) {
try {
const matches = Memory.scanSync(module.base, module.size, pattern);
if (matches.length > 0) {
addresses[funcName] = matches[0].address;
devlog(`[${this.moduleName}] Found ${funcName} at ${matches[0].address} via pattern`);
} else {
devlog_error(`[${this.moduleName}] Pattern not found for ${funcName}`);
}
} catch (error) {
devlog_error(`[${this.moduleName}] Pattern search failed for ${funcName}: ${error}`);
}
}
}
/**
* Install all hooks for this library.
*/
public install(): boolean {
if (Object.keys(this.addresses).length === 0) {
devlog_error(`[${this.moduleName}] No function addresses found`);
return false;
}
let successCount = 0;
// Install read/write hooks
if (this.hookSSLRead()) successCount++;
if (this.hookSSLWrite()) successCount++;
if (this.hookKeyExtraction()) successCount++;
if (successCount > 0) {
log(`Successfully installed ${successCount} hooks for ${this.moduleName}`);
return true;
} else {
devlog_error(`[${this.moduleName}] Failed to install any hooks`);
return false;
}
}
/**
* Hook SSL read function.
*/
private hookSSLRead(): boolean {
const readAddress = this.addresses["NewSSL_Read"];
if (!readAddress) {
devlog_error(`[${this.moduleName}] NewSSL_Read address not found`);
return false;
}
try {
Interceptor.attach(readAddress, {
onEnter(args) {
// Store arguments for onLeave
this.ssl = args[0]; // SSL context
this.buffer = args[1]; // Data buffer
this.bufferSize = args[2]; // Buffer size
devlog(`[${this.moduleName}] SSL_Read called with buffer size: ${this.bufferSize}`);
},
onLeave(retval) {
const bytesRead = retval.toInt32();
if (bytesRead > 0) {
try {
const data = this.buffer.readByteArray(bytesRead);
// Get connection info
const connectionInfo = this.getConnectionInfo(this.ssl);
// Process data using shared functions
SharedStructures.logSSLRead(this.ssl, data, connectionInfo);
devlog(`[${this.moduleName}] Captured ${bytesRead} bytes via SSL_Read`);
} catch (error) {
devlog_error(`[${this.moduleName}] Error processing SSL_Read data: ${error}`);
}
}
}
});
devlog(`[${this.moduleName}] Hooked NewSSL_Read at ${readAddress}`);
return true;
} catch (error) {
devlog_error(`[${this.moduleName}] Failed to hook NewSSL_Read: ${error}`);
return false;
}
}
/**
* Hook SSL write function.
*/
private hookSSLWrite(): boolean {
const writeAddress = this.addresses["NewSSL_Write"];
if (!writeAddress) {
devlog_error(`[${this.moduleName}] NewSSL_Write address not found`);
return false;
}
try {
Interceptor.attach(writeAddress, {
onEnter(args) {
const ssl = args[0];
const dataPtr = args[1];
const dataSize = args[2].toInt32();
if (dataSize > 0) {
try {
const data = dataPtr.readByteArray(dataSize);
// Get connection info
const connectionInfo = this.getConnectionInfo(ssl);
// Process data using shared functions
SharedStructures.logSSLWrite(ssl, data, connectionInfo);
devlog(`[${this.moduleName}] Captured ${dataSize} bytes via SSL_Write`);
} catch (error) {
devlog_error(`[${this.moduleName}] Error processing SSL_Write data: ${error}`);
}
}
}
});
devlog(`[${this.moduleName}] Hooked NewSSL_Write at ${writeAddress}`);
return true;
} catch (error) {
devlog_error(`[${this.moduleName}] Failed to hook NewSSL_Write: ${error}`);
return false;
}
}
/**
* Hook key extraction functions.
*/
private hookKeyExtraction(): boolean {
const keyAddress = this.addresses["NewSSL_GetKeys"];
if (!keyAddress) {
devlog(`[${this.moduleName}] Key extraction function not found (optional)`);
return false;
}
try {
Interceptor.attach(keyAddress, {
onLeave(retval) {
// Extract keys based on library-specific format
const keyData = this.extractKeyData(retval);
if (keyData) {
SharedStructures.logSSLKeys(keyData);
devlog(`[${this.moduleName}] Extracted SSL keys`);
}
}
});
devlog(`[${this.moduleName}] Hooked key extraction at ${keyAddress}`);
return true;
} catch (error) {
devlog_error(`[${this.moduleName}] Failed to hook key extraction: ${error}`);
return false;
}
}
/**
* Extract connection information from SSL context.
*/
private getConnectionInfo(ssl: NativePointer): any {
try {
// Library-specific SSL context parsing
// This will vary significantly between libraries
// Example structure access (adjust for actual library)
const socketFd = ssl.add(0x10).readInt(); // Offset to socket descriptor
// Get socket information using shared utilities
return SharedStructures.getSocketInfo(socketFd);
} catch (error) {
devlog_error(`[${this.moduleName}] Failed to get connection info: ${error}`);
return { src: "0.0.0.0", dst: "0.0.0.0", src_port: 0, dst_port: 0 };
}
}
/**
* Extract key data from library-specific structures.
*/
private extractKeyData(keyPtr: NativePointer): any {
try {
// Parse library-specific key structures
// Return in standard friTap format
return {
client_random: keyPtr.readByteArray(32),
master_secret: keyPtr.add(32).readByteArray(48)
};
} catch (error) {
devlog_error(`[${this.moduleName}] Failed to extract key data: ${error}`);
return null;
}
}
}
/**
* Main entry point for new library detection and hooking.
*/
export function installNewLibraryHooks(): boolean {
const possibleNames = [
"libnewssl.so", // Linux
"libnewssl.so.1", // Linux versioned
"newssl.dll", // Windows
"NewSSL", // macOS framework
"libNewSSL.dylib" // macOS dynamic library
];
for (const name of possibleNames) {
try {
const module = Process.getModuleByName(name);
if (module) {
devlog(`[NewLibrary] Found module: ${name}`);
const hooks = new NewLibraryHooks(name);
const success = hooks.install();
if (success) {
log(`NewLibrary hooks installed for ${name}`);
return true;
}
}
} catch (error) {
// Module not found, continue to next
devlog(`[NewLibrary] Module ${name} not found`);
}
}
devlog(`[NewLibrary] No compatible modules found`);
return false;
}
Step 3: Add Platform-Specific Integration¶
Create platform-specific integration files for each supported platform:
// agent/linux/new_library_linux.ts
import { installNewLibraryHooks } from "../ssl_lib/new_library.js";
import { devlog } from "../util/log.js";
export function installNewLibraryLinux(): boolean {
devlog("[Linux] Attempting NewLibrary detection");
// Linux-specific module names and paths
const linuxModules = [
"libnewssl.so",
"libnewssl.so.1",
"libnewssl.so.1.0",
"/usr/lib/x86_64-linux-gnu/libnewssl.so",
"/usr/local/lib/libnewssl.so"
];
// Check for library presence
for (const moduleName of linuxModules) {
try {
const module = Process.getModuleByName(moduleName);
if (module) {
devlog(`[Linux] Found NewLibrary module: ${moduleName}`);
return installNewLibraryHooks();
}
} catch (error) {
// Continue to next module
}
}
devlog("[Linux] NewLibrary not found");
return false;
}
// agent/android/new_library_android.ts
import { installNewLibraryHooks } from "../ssl_lib/new_library.js";
import { devlog, devlog_error } from "../util/log.js";
export function installNewLibraryAndroid(): boolean {
devlog("[Android] Attempting NewLibrary detection");
// Android-specific considerations
const androidModules = [
"libnewssl.so",
"libapp.so", // May be statically linked in app
"libflutter.so" // If using Flutter with NewSSL
];
// Check if we're in an Android app context
if (!Java.available) {
devlog_error("[Android] Java runtime not available");
return false;
}
// Attempt to find NewLibrary in loaded modules
for (const moduleName of androidModules) {
try {
const module = Process.getModuleByName(moduleName);
if (module) {
devlog(`[Android] Found potential NewLibrary module: ${moduleName}`);
// Additional Android-specific checks
if (this.validateAndroidModule(module)) {
return installNewLibraryHooks();
}
}
} catch (error) {
// Continue to next module
}
}
devlog("[Android] NewLibrary not found");
return false;
}
function validateAndroidModule(module: Module): boolean {
// Android-specific validation
// Check for expected exports or patterns
try {
const exports = module.enumerateExports();
const newSSLExports = exports.filter(exp =>
exp.name.toLowerCase().includes('newssl') ||
exp.name.toLowerCase().includes('ssl_read') ||
exp.name.toLowerCase().includes('ssl_write')
);
return newSSLExports.length > 0;
} catch (error) {
devlog_error(`[Android] Module validation failed: ${error}`);
return false;
}
}
Step 4: Update Main Agent¶
Integrate the new library into the main detection loop:
// agent/ssl_log.ts (add to main detection function)
import { installNewLibraryLinux } from "./linux/new_library_linux.js";
import { installNewLibraryAndroid } from "./android/new_library_android.js";
import { installNewLibraryWindows } from "./windows/new_library_windows.js";
import { installNewLibraryMacOS } from "./macos/new_library_macos.js";
import { installNewLibraryIOS } from "./ios/new_library_ios.js";
// In main SSL library detection function
function detectSSLLibraries(): void {
let librariesFound = 0;
// Existing libraries...
librariesFound += installOpenSSLHooks() ? 1 : 0;
librariesFound += installBoringSSLHooks() ? 1 : 0;
librariesFound += installNSSHooks() ? 1 : 0;
// Add new library detection
if (isLinux()) {
librariesFound += installNewLibraryLinux() ? 1 : 0;
} else if (isAndroid()) {
librariesFound += installNewLibraryAndroid() ? 1 : 0;
} else if (isWindows()) {
librariesFound += installNewLibraryWindows() ? 1 : 0;
} else if (isMacOS()) {
librariesFound += installNewLibraryMacOS() ? 1 : 0;
} else if (isiOS()) {
librariesFound += installNewLibraryIOS() ? 1 : 0;
}
log(`Detected ${librariesFound} SSL/TLS libraries`);
}
Step 5: Compile and Test¶
# Compile the agent
npm run build
# Verify compilation succeeded
ls -la friTap/_ssl_log.js friTap/_ssl_log_legacy.js
# Test with a simple application
python -m friTap.friTap -k test_keys.log ground_truth/new_library_test_app
# Test with debug output
python -m friTap.friTap -do -k test_keys.log ground_truth/new_library_test_app
# Verify key extraction
grep "CLIENT_RANDOM" test_keys.log
Step 6: Add Python Integration¶
Update Python code to handle the new library:
# friTap/ssl_logger.py (add detection logic)
def _detect_ssl_libraries(self) -> Dict[str, Any]:
"""Detect available SSL libraries in target process."""
libraries = {}
try:
# Get loaded modules from Frida
modules = self.session.enumerate_modules()
for module in modules:
# Existing library detection...
# Add new library detection
if self._is_new_library_module(module):
libraries["NewLibrary"] = {
"name": "NewLibrary",
"module": module.name,
"base": module.base_address,
"size": module.size,
"version": self._get_new_library_version(module)
}
except Exception as e:
self.logger.error(f"SSL library detection failed: {e}")
return libraries
def _is_new_library_module(self, module) -> bool:
"""Check if module contains NewLibrary."""
new_library_indicators = [
"libnewssl",
"newssl.dll",
"NewSSL"
]
module_name_lower = module.name.lower()
return any(indicator in module_name_lower for indicator in new_library_indicators)
def _get_new_library_version(self, module) -> str:
"""Get NewLibrary version from module."""
try:
# Extract version from module exports or metadata
# This is library-specific
return "1.0.0" # Default version
except Exception:
return "unknown"
Step 7: Add Tests¶
Create comprehensive tests for the new library:
# tests/unit/test_new_library.py
import pytest
from unittest.mock import MagicMock, patch
from friTap.ssl_logger import SSL_Logger
class TestNewLibraryDetection:
"""Test NewLibrary SSL detection and hooking."""
@patch('friTap.ssl_logger.frida')
def test_new_library_detection(self, mock_frida):
"""Test detection of NewLibrary SSL."""
# Mock module detection
mock_process = MagicMock()
mock_module = MagicMock()
mock_module.name = "libnewssl.so"
mock_module.base_address = 0x7f0000000000
mock_module.size = 1024 * 1024
mock_process.enumerate_modules.return_value = [mock_module]
mock_frida.get_local_device.return_value.attach.return_value = mock_process
logger = SSL_Logger("test_app")
detected_libraries = logger._detect_ssl_libraries()
assert "NewLibrary" in detected_libraries
assert detected_libraries["NewLibrary"]["module"] == "libnewssl.so"
def test_new_library_version_extraction(self):
"""Test NewLibrary version extraction."""
logger = SSL_Logger("test_app")
mock_module = MagicMock()
mock_module.name = "libnewssl.so.1.2.3"
version = logger._get_new_library_version(mock_module)
assert version is not None
@patch('subprocess.run')
def test_new_library_ground_truth(self, mock_subprocess):
"""Test NewLibrary with ground truth application."""
# Mock successful friTap execution
mock_subprocess.return_value.returncode = 0
mock_subprocess.return_value.stdout = "NewLibrary hooks installed"
# This would be a real test with actual ground truth app
assert True # Placeholder
# tests/agent/test_new_library_compilation.py
def test_new_library_agent_compiles():
"""Test that new library agent code compiles successfully."""
result = subprocess.run(['npm', 'run', 'build'],
capture_output=True, text=True)
assert result.returncode == 0
assert "error" not in result.stderr.lower()
# Verify new library code is included
with open('friTap/_ssl_log.js', 'r') as f:
content = f.read()
assert 'NewLibraryHooks' in content
assert 'installNewLibraryHooks' in content
Step 8: Update Documentation¶
Update the documentation to include the new library:
<!-- docs/libraries/new-library.md -->
# NewLibrary Support
friTap supports NewLibrary SSL/TLS implementation with full key extraction and traffic analysis capabilities.
## Overview
NewLibrary is a [description of the library, its features, common usage].
## Supported Features
| Platform | Key Extraction | Traffic Capture | Notes |
|----------|---------------|-----------------|-------|
| Linux | ✅ Full | ✅ Full | All versions |
| Windows | ✅ Full | ✅ Full | Windows 10+ |
| Android | ✅ Full | ✅ Full | API 21+ |
| macOS | ⚠️ Limited | ✅ Full | Key extraction partial |
| iOS | ⚠️ Limited | ✅ Full | Requires jailbreak |
## Usage Examples
### Basic Key Extraction
```bash
# Linux/Windows application
fritap -k newlibrary_keys.log target_app
# Android application
fritap -m -k newlibrary_keys.log com.example.app
Traffic Analysis¶
# Full traffic capture with keys
fritap -k keys.log -p traffic.pcap target_app
# Live analysis with Wireshark
fritap -l target_app
Implementation Details¶
NewLibrary hooks are implemented using: - Symbol-based detection (preferred) - Pattern-based detection (for stripped libraries) - Multiple architecture support (x86, x64, ARM, ARM64)
Troubleshooting¶
Common issues and solutions:
Library Not Detected¶
# Check if library is loaded
fritap --list-libraries target_app
# Enable debug output
fritap -do -v target_app
Pattern-Based Hooking¶
For stripped NewLibrary implementations:
# Generate patterns with BoringSecretHunter
mkdir -p binary results
cp libnewssl.so binary/
docker run --rm -v "$(pwd)/binary":/usr/local/src/binaries -v "$(pwd)/results":/host_output boringsecrethunter
# Use generated patterns
fritap --patterns results/libnewssl.so_patterns.json -k keys.log target_app
Update the main library support matrix:
```markdown
<!-- docs/libraries/index.md -->
| Library | Linux | Windows | macOS | Android | iOS |
|-------------|-------|---------|-------|---------|-----|
| OpenSSL | Full | R/W | TBI | Full | TBI |
| BoringSSL | Full | R/W | KeyEo | Full | KeyEo |
| NSS | Full | R/W | TBI | TBA | TBI |
| GnuTLS | R/W | R/W | TBI | Full | TBI |
| NewLibrary | Full | Full | KeyEo | Full | KeyEo |
Adding Platform Support¶
When adding support for a new platform (operating system or architecture):
Step 1: Platform Analysis¶
Research the new platform's characteristics:
# Analyze target platform
uname -a # System information
file /path/to/executable # Binary format
ldd /path/to/executable # Library dependencies
readelf -h /path/to/executable # ELF header (Linux)
# Study platform-specific SSL libraries
find /usr -name "*ssl*" 2>/dev/null
find /lib -name "*ssl*" 2>/dev/null
Step 2: Platform Detection¶
Add platform detection to TypeScript agent:
// agent/util/process_infos.ts
export function isNewPlatform(): boolean {
// Platform-specific detection logic
return Process.platform === "new_platform_name" &&
// Additional platform-specific checks
checkNewPlatformFeatures();
}
function checkNewPlatformFeatures(): boolean {
try {
// Platform-specific API availability checks
// For example, check for specific system calls, libraries, or features
return true;
} catch (error) {
return false;
}
}
Step 3: Platform-Specific Code¶
Create platform-specific directory and implementations:
// agent/new_platform/ssl_libraries_new_platform.ts
import { log, devlog } from "../util/log.js";
export function installNewPlatformSSLHooks(): boolean {
devlog("[NewPlatform] Starting SSL library detection");
let hookCount = 0;
// Platform-specific SSL library detection
hookCount += installOpenSSLNewPlatform() ? 1 : 0;
hookCount += installBoringSSLNewPlatform() ? 1 : 0;
// Add other libraries...
if (hookCount > 0) {
log(`[NewPlatform] Installed hooks for ${hookCount} SSL libraries`);
return true;
} else {
devlog("[NewPlatform] No SSL libraries found");
return false;
}
}
function installOpenSSLNewPlatform(): boolean {
// Platform-specific OpenSSL module names and paths
const platformModules = [
"platform_libssl.so",
"/platform/path/to/libssl.so"
];
for (const moduleName of platformModules) {
try {
const module = Process.getModuleByName(moduleName);
if (module) {
// Use existing OpenSSL hooks with platform adaptations
return installOpenSSLHooks(moduleName);
}
} catch (error) {
// Continue to next module
}
}
return false;
}
Step 4: Python Platform Handler¶
Create Python platform handler:
# friTap/platforms/new_platform.py
import logging
import subprocess
from typing import List, Dict, Any, Optional
class NewPlatformHandler:
"""Handle NewPlatform-specific operations."""
def __init__(self):
self.platform_name = "NewPlatform"
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
def setup_environment(self) -> bool:
"""Setup NewPlatform environment for analysis."""
try:
# Platform-specific setup
self._check_permissions()
self._setup_dependencies()
return True
except Exception as e:
self.logger.error(f"NewPlatform setup failed: {e}")
return False
def get_process_list(self) -> List[Dict[str, Any]]:
"""Get list of running processes on NewPlatform."""
try:
# Platform-specific process enumeration
result = subprocess.run(['platform_ps_command'],
capture_output=True, text=True)
processes = []
for line in result.stdout.strip().split('\n')[1:]: # Skip header
parts = line.split()
if len(parts) >= 2:
processes.append({
'pid': int(parts[0]),
'name': parts[1],
'full_path': ' '.join(parts[1:])
})
return processes
except Exception as e:
self.logger.error(f"Failed to get process list: {e}")
return []
def attach_to_process(self, target: str) -> Optional[Any]:
"""Attach to process on NewPlatform."""
try:
import frida
# Platform-specific attachment logic
device = frida.get_device("new_platform_device_id")
# Handle different target formats
if target.isdigit():
# PID
session = device.attach(int(target))
else:
# Process name
session = device.attach(target)
return session
except Exception as e:
self.logger.error(f"Failed to attach to process: {e}")
return None
def _check_permissions(self) -> None:
"""Check required permissions for NewPlatform."""
# Platform-specific permission checks
pass
def _setup_dependencies(self) -> None:
"""Setup required dependencies for NewPlatform."""
# Platform-specific dependency setup
pass
def is_new_platform() -> bool:
"""Check if running on NewPlatform."""
import platform
return platform.system().lower() == "newplatform"
Step 5: Integration Points¶
Update main SSL_Logger class:
# friTap/ssl_logger.py
from .platforms.new_platform import NewPlatformHandler, is_new_platform
class SSL_Logger:
def _detect_platform(self):
"""Detect current platform and return appropriate handler."""
if is_android():
return AndroidHandler()
elif is_ios():
return IOSHandler()
elif is_new_platform():
return NewPlatformHandler()
else:
return DesktopHandler() # Default for Linux/Windows/macOS
Step 6: Platform Testing¶
Create platform-specific tests:
# tests/platforms/test_new_platform.py
import pytest
import platform
from friTap.platforms.new_platform import NewPlatformHandler, is_new_platform
@pytest.mark.skipif(not is_new_platform(), reason="NewPlatform only")
class TestNewPlatformSupport:
"""Test NewPlatform-specific functionality."""
def test_platform_detection(self):
"""Test NewPlatform detection."""
assert is_new_platform() == True
def test_environment_setup(self):
"""Test NewPlatform environment setup."""
handler = NewPlatformHandler()
assert handler.setup_environment() == True
def test_process_enumeration(self):
"""Test process enumeration on NewPlatform."""
handler = NewPlatformHandler()
processes = handler.get_process_list()
assert isinstance(processes, list)
if len(processes) > 0:
assert 'pid' in processes[0]
assert 'name' in processes[0]
@pytest.mark.slow
def test_ssl_analysis_workflow(self):
"""Test complete SSL analysis workflow on NewPlatform."""
from friTap.ssl_logger import SSL_Logger
logger = SSL_Logger("test_app")
# Test platform-specific workflow
assert logger.platform_handler.platform_name == "NewPlatform"
Adding Analysis Features¶
When adding new analysis capabilities to friTap:
Step 1: Feature Design¶
Design the new analysis feature:
# friTap/analysis/new_feature.py
import logging
from typing import Dict, List, Any, Optional
class NewAnalysisFeature:
"""Implement new analysis capability."""
def __init__(self, config: Dict[str, Any]):
self.config = config
self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
self.results: Dict[str, Any] = {}
def analyze(self, ssl_data: bytes, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Perform new type of analysis on SSL data."""
try:
# Core analysis logic
results = self._process_data(ssl_data, metadata)
# Format results
formatted_results = self._format_results(results)
# Update internal state
self.results.update(formatted_results)
return formatted_results
except Exception as e:
self.logger.error(f"Analysis failed: {e}")
return {"error": str(e)}
def _process_data(self, data: bytes, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""Core data processing logic."""
# Implement specific analysis algorithm
# Example: pattern detection, anomaly analysis, etc.
processed_results = {
"data_size": len(data),
"analysis_timestamp": time.time(),
"patterns_found": self._find_patterns(data),
"statistics": self._calculate_statistics(data)
}
return processed_results
def _format_results(self, results: Dict[str, Any]) -> Dict[str, Any]:
"""Format analysis results for output."""
return {
"feature_name": "new_analysis_feature",
"version": "1.0.0",
"results": results,
"summary": self._generate_summary(results)
}
def get_summary(self) -> Dict[str, Any]:
"""Get analysis summary."""
return {
"total_analyzed": len(self.results),
"feature_specific_metrics": self._calculate_metrics()
}
Step 2: CLI Integration¶
Add command-line interface for the new feature:
# friTap/friTap.py (add new CLI argument)
@click.option(
"--new-feature",
is_flag=True,
help="Enable new analysis feature"
)
@click.option(
"--new-feature-config",
type=str,
help="Configuration file for new analysis feature"
)
def main(..., new_feature: bool, new_feature_config: Optional[str]):
"""Main friTap entry point."""
# Initialize new feature if enabled
analysis_features = []
if new_feature:
config = {}
if new_feature_config:
with open(new_feature_config, 'r') as f:
config = json.load(f)
from friTap.analysis.new_feature import NewAnalysisFeature
analysis_features.append(NewAnalysisFeature(config))
# Pass features to SSL_Logger
logger = SSL_Logger(target, analysis_features=analysis_features)
Step 3: Output Integration¶
Integrate with existing output formats:
# friTap/ssl_logger.py
def _process_ssl_data(self, data: bytes, metadata: Dict[str, Any]) -> None:
"""Process SSL data with all enabled analysis features."""
# Existing processing...
self._log_to_pcap(data, metadata)
self._log_keys(metadata.get('keys', []))
# Run analysis features
for feature in self.analysis_features:
try:
feature_results = feature.analyze(data, metadata)
# Add to JSON output
if self.json_output:
self._add_to_json_output("analysis", feature_results)
# Log significant findings
if feature_results.get("significant_finding"):
self.logger.info(f"Analysis finding: {feature_results['summary']}")
except Exception as e:
self.logger.error(f"Analysis feature failed: {e}")
def _add_to_json_output(self, category: str, data: Dict[str, Any]) -> None:
"""Add analysis results to JSON output."""
if category not in self.session_data:
self.session_data[category] = []
self.session_data[category].append({
"timestamp": time.time(),
"data": data
})
Step 4: Feature Testing¶
Create comprehensive tests for the new feature:
# tests/analysis/test_new_feature.py
import pytest
from friTap.analysis.new_feature import NewAnalysisFeature
class TestNewAnalysisFeature:
"""Test new analysis feature."""
def test_feature_initialization(self):
"""Test feature initialization with config."""
config = {"param1": "value1", "param2": 42}
feature = NewAnalysisFeature(config)
assert feature.config == config
assert hasattr(feature, 'results')
def test_data_analysis(self):
"""Test data analysis functionality."""
feature = NewAnalysisFeature({})
test_data = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
metadata = {"src": "192.168.1.1", "dst": "8.8.8.8"}
results = feature.analyze(test_data, metadata)
assert "feature_name" in results
assert "results" in results
assert results["results"]["data_size"] == len(test_data)
def test_results_formatting(self):
"""Test results formatting."""
feature = NewAnalysisFeature({})
raw_results = {
"data_size": 100,
"patterns_found": ["pattern1", "pattern2"]
}
formatted = feature._format_results(raw_results)
assert "feature_name" in formatted
assert "version" in formatted
assert "summary" in formatted
def test_error_handling(self):
"""Test error handling in analysis."""
feature = NewAnalysisFeature({})
# Test with invalid data
results = feature.analyze(None, {})
assert "error" in results
Best Practices for Feature Development¶
1. Incremental Development¶
- Start with basic functionality
- Add platform support incrementally
- Test thoroughly at each stage
- Document as you develop
2. Compatibility Considerations¶
- Maintain backward compatibility
- Test with existing features
- Consider performance impact
- Follow established patterns
3. Error Handling¶
- Graceful degradation
- Comprehensive logging
- User-friendly error messages
- Recovery mechanisms
4. Testing Strategy¶
- Unit tests for all components
- Integration tests for workflows
- Platform-specific testing
- Performance benchmarking
5. Documentation Requirements¶
- API documentation
- Usage examples
- Troubleshooting guides
- Platform-specific notes
Next Steps¶
After adding new features:
- Test thoroughly: Use the Testing Guide
- Update documentation: Follow Documentation Guide
- Submit for review: Use Pull Request Process
- Monitor performance: Check for regressions
- Gather feedback: From community and maintainers
For more information: - Development Setup: Environment configuration - Coding Standards: Code quality guidelines - Testing Guide: Comprehensive testing strategies