How MobileAudit's Pattern Engine Detects Vulnerabilities and Malicious Code in Decompiled APKs
MobileAudit's pattern engine detects vulnerabilities by decompiling APKs with jadx, traversing the resulting source tree, and applying user-defined regular expressions to identify hardcoded secrets, malicious URLs, IPs, and other security indicators.
MobileAudit is an open-source mobile application security scanner that automates the detection of vulnerabilities and malicious code in Android APKs. At the heart of the mpast/mobileaudit repository lies a regex-based pattern engine that systematically analyzes decompiled source code to surface security findings. This article examines the architectural flow, core implementation details, and practical usage of MobileAudit's detection engine.
Architectural Overview of the Detection Pipeline
The pattern engine operates as a sequential pipeline that transforms raw APK binaries into structured security findings. The process begins when a user uploads an APK through the web interface or API, triggering an asynchronous Celery task. The system then decompiles the application, walks the resulting directory tree, and executes regex patterns against every relevant source file. Each match generates a Finding object enriched with contextual metadata such as line numbers, code snippets, and malware associations.
Step-by-Step Detection Process
APK Decompilation with JADX
The analysis begins in app/analysis.py where the analyze_apk function (lines 44-96) orchestrates the scan. First, it invokes decompile_jadx (lines 18-22) to execute the jadx command-line tool:
jadx -d <DECOMPILE_PATH> <APK_FILE>
This command decompiles the Dalvik bytecode into human-readable Java and Kotlin source files, alongside XML resource files, creating a navigable source tree for pattern matching.
File Tree Traversal
Once decompilation completes, get_tree_dir (lines 22-38 in app/analysis.py) walks the directory structure using os.walk. The function filters for files with extensions .java, .kt, or .xml, reads each file's complete content into memory, and passes the data to the pattern matching engine:
for dirpath, dirs, files in os.walk(dir):
for filename in files:
fname = os.path.join(dirpath, filename)
extension = os.path.splitext(fname)[1]
if extension in ('.java', '.kt', '.xml'):
f = open(fname, mode="r", encoding="utf-8")
content = f.read()
f.close()
find_patterns(1, '', content, fname, dir, scan)
Pattern Loading and Compilation
The find_patterns function queries the database for active detection rules via Pattern.objects.filter(active=True). Each Pattern object (defined in app/models.py, lines 100-113) contains a regular expression string, default severity, CWE mapping, and human-readable metadata:
class Pattern(models.Model):
id = models.AutoField(primary_key=True)
default_cwe = models.ForeignKey(Cwe, on_delete=models.CASCADE)
default_risk = models.ForeignKey(Risk, on_delete=models.CASCADE, null=True)
default_name = models.TextField()
default_description = models.TextField(blank=True)
default_severity = models.CharField(max_length=10, choices=Severity.choices)
default_mitigation = models.TextField(blank=True)
pattern = models.TextField() # ← the regex string
active = models.BooleanField(default=True)
Regex Matching and Context Extraction
For each active pattern, the engine compiles the regex with re.MULTILINE support and iterates through all matches in the file content (lines 73-80 in app/analysis.py):
for p in patterns:
pattern = re.compile(p.pattern, re.MULTILINE)
for match in pattern.finditer(line):
# Process match...
The system calculates precise line numbers and extracts code snippets using get_position (lines 71-77) and get_match_lines (lines 53-71), ensuring findings include actionable context for developers.
Type Classification and Malware Lookup
MobileAudit's engine applies special handling based on pattern IDs to enrich findings with security intelligence (lines 82-88 and 90-103):
- Pattern ID 8: IP addresses
- Pattern ID 9: URLs (checked against the
Malwaredatabase table) - Pattern ID 10: Email addresses
- Pattern ID 21: Hexadecimal strings
- Pattern ID 22: Base64 encoded data
For URL patterns, the engine parses the domain and queries the Malware model to identify known malicious infrastructure:
if p.id == 9: # URL pattern
type = 'URL'
url = urllib.parse.urlsplit(match.group())
try:
m = Malware.objects.get(url__icontains=url.netloc)
except Malware.DoesNotExist:
m = None
Finding Persistence
Each match creates a Finding record (lines 24-41) storing metadata including severity, CWE classification, risk level, and the matched line content. The system also creates associated String objects to store extracted values and Domain objects for URL findings linked to malware entries (lines 44-53):
finding = Finding(
scan=scan,
path=name.replace(dir, ""),
line_number=position,
line=match.group(),
snippet=snippet,
status=Status.TD,
type=p,
name=p.default_name,
description=p.default_description,
severity=p.default_severity,
cwe=p.default_cwe,
risk=p.default_risk,
user=scan.user,
)
finding.save()
String.objects.create(type=type, value=match.group(), scan=scan, finding=finding)
Adding Custom Detection Patterns
Security teams can extend MobileAudit's detection capabilities by creating new Pattern objects via the Django admin interface or programmatically. The following example demonstrates adding a regex to detect hardcoded AWS secret access keys:
from app.models import Pattern, Cwe, Risk, Severity
# Retrieve or create supporting metadata
cwe = Cwe.objects.get_or_create(cwe=326)[0] # CWE-326: Inadequate Encryption Strength
risk = Risk.objects.get_or_create(risk=3)[0] # Risk rating scale
# Create the detection pattern
Pattern.objects.create(
default_cwe=cwe,
default_risk=risk,
default_name="AWS Secret Access Key",
default_description="Hard-coded AWS secret access key found in source code.",
default_severity=Severity.HI,
pattern=r'AKIA[0-9A-Z]{16}',
active=True,
)
Once persisted to the database, the pattern engine automatically includes this regex in subsequent scans without requiring application restarts.
Key Source Files and Their Roles
| File | Role |
|---|---|
app/analysis.py |
Core engine implementing decompilation (decompile_jadx), directory traversal (get_tree_dir), pattern matching (find_patterns), and finding persistence. |
app/models.py |
Database schema defining Pattern (regex rules), Finding (scan results), String (extracted values), Domain (URL metadata), and Malware (threat intelligence). |
app/worker/tasks.py |
Celery task definitions that orchestrate asynchronous scan execution via scan_task. |
app/views.py |
Web interface endpoints for pattern management and finding visualization. |
Summary
- MobileAudit's pattern engine leverages jadx to decompile APKs into readable Java, Kotlin, and XML source code.
- The engine traverses the decompiled tree and applies user-defined regular expressions loaded dynamically from the database via the
Patternmodel. - Special pattern IDs trigger contextual analysis, including malware database lookups for URLs and classification of IPs, emails, base64, and hex strings.
- Each match generates a
Findingrecord with precise line numbers, code snippets, severity ratings, and CWE mappings for actionable remediation. - Security teams can extend detection capabilities by adding new regex patterns without modifying core engine code.
Frequently Asked Questions
How does MobileAudit decompile APK files for analysis?
MobileAudit utilizes the jadx decompiler to convert Android APK bytecode into human-readable source code. When a scan initiates, the decompile_jadx function in app/analysis.py (lines 18-22) executes the command jadx -d <DECOMPILE_PATH> <APK_FILE>, producing Java, Kotlin, and XML files that the pattern engine subsequently analyzes.
What types of security patterns can the engine detect?
The engine detects vulnerabilities through customizable regular expressions stored in the Pattern model. Built-in pattern types include hardcoded IP addresses (ID 8), URLs (ID 9) with malware intelligence integration, email addresses (ID 10), hexadecimal strings (ID 21), and base64 encoded data (ID 22). Users can add patterns for secrets, cryptographic keys, or proprietary business logic exposure.
How does MobileAudit handle URL detection and malware verification?
When the pattern engine identifies a URL using pattern ID 9, it parses the domain using urllib.parse.urlsplit and queries the Malware database table to check for known malicious infrastructure. If a match exists, the engine creates a Domain object linking the finding to the malware record, enriching the security report with threat intelligence context. This implementation appears in app/analysis.py lines 82-88 and 90-103.
Can security teams add custom vulnerability patterns without modifying source code?
Yes, security teams can extend detection capabilities by creating new Pattern objects through the Django admin interface or programmatically via the ORM. Each pattern requires a regular expression string, severity rating, CWE mapping, and metadata description. Once saved to the database with active=True, the engine automatically includes the new pattern in subsequent scans without requiring application restarts or code modifications, as demonstrated in the find_patterns function in app/analysis.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →