Published: February 14, 2025
BadPack is an Android malware family that has recently emerged in the wild. Its modular design, sophisticated obfuscation, and multi-stage payload make it a formidable threat. In this post, we cover its origins, infection methods, payload activities, and low-level detection details. We also provide code snippets from the sample, IOC indicators including YARA rules, and practical mitigations.
BadPack appears to be a repackaging and injection framework designed to hijack legitimate apps. Analysis suggests that BadPack is distributed through third-party app markets and phishing campaigns targeting users in emerging markets. The malware is packaged as a “helper” library inside seemingly legitimate APKs.
While attribution remains challenging, several indicators point toward financially motivated threat actors with ties to Eastern European cybercrime rings. Some artifacts (such as code comments in Cyrillic and references to Russian locales in network beacons) suggest that a group occasionally dubbed “BlackPack” may be behind its development. However, as with many Android threats, attribution should be treated with caution.
BadPack employs multiple infection vectors:
Once installed, BadPack leverages dynamic code loading to bypass static analysis, using reflection to load encrypted dex files at runtime.
BadPack’s payload is modular and consists of the following components:
The payload is encrypted using a custom algorithm and is only decrypted in memory during execution, complicating static analysis.
BadPack uses several obfuscation techniques: - String
Encryption: Critical strings (such as URLs and command
keywords) are stored in an obfuscated form and decrypted at runtime. -
Dynamic Code Loading: The payload is stored as an
encrypted dex file within the assets folder. After performing integrity
checks, the malware uses DexClassLoader
to load the
payload.
In some variants, native libraries (compiled in C/C++) are used to execute performance-critical tasks, such as cryptographic functions and process injection routines. The native code interacts with Java components via JNI, further complicating reverse engineering.
Below is an excerpt from the disassembled smali code that loads the encrypted payload:
.method private loadEncryptedPayload()V
.locals 3
const-string v0, "payload_encrypted"
invoke-static {v0}, Lcom/badpack/utils/Decryptor;->decrypt(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
const-string v2, "/data/data/com.badpack/files/temp.dex"
new-instance v0, Ldalvik/system/DexClassLoader;
invoke-direct {v0, v1, v2, null, getContextClassLoader()}, Ldalvik/system/DexClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V
const-string v1, "com.badpack.payload.Main"
invoke-virtual {v0, v1}, Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;
move-result-object v1
return-void
.end method
(JNIEnv *env, jobject obj, jstring encryptedStr) {
JNIEXPORT jstring JNICALL Java_com_badpack_utils_Decryptor_nativeDecryptconst char *encStr = (*env)->GetStringUTFChars(env, encryptedStr, 0);
char decrypted[256];
for (int i = 0; i < strlen(encStr); i++) {
[i] = encStr[i] ^ 0x5A;
decrypted}
[strlen(encStr)] = '\0';
decrypted(*env)->ReleaseStringUTFChars(env, encryptedStr, encStr);
return (*env)->NewStringUTF(env, decrypted);
}
BadPack is not a separate “binary” malware per se but rather a technique used by several Android malware families (e.g. TeaBot/Anatsa, BianLian, Cerberus) to “garble” the APK package. Attackers deliberately alter ZIP header fields in the APK (which is just a ZIP archive) so that standard static analysis tools (like Apktool, Jadx, or even the JAR tool) fail to extract or parse the critical AndroidManifest.xml file. Despite these “corruptions,” the Android runtime itself remains relatively lenient—it only relies on the central directory headers when installing the app. This means that the malware can run normally on devices even though analysis tools encounter errors.
An APK file follows the ZIP file format and contains two types of headers:
50 4B 03 04
corresponding to ASCII “PK”).0
(STORE) or 8
(DEFLATE).For a valid ZIP (and thus APK), the values in the local headers and the central directory must be consistent.
Malware authors use one or more of the following techniques to “corrupt” the ZIP headers in an APK:
8
for DEFLATE), even though the file data is actually
stored (STORE). Meanwhile, the central directory still correctly
indicates STORE.
This deliberate mismatch is key to the BadPack technique. Tools like
Apktool, Jadx, and even the standard unzip
utility will
report errors such as “Invalid CEN header” or “unsupported compression
method” when faced with such corrupted header data.
Below is an illustrative hexdump of a local file header from a benign APK file versus one manipulated by BadPack.
Normal Local File Header (Hexdump):
50 4B 03 04 14 00 00 00 08 00 B7 AC CE 34 00 00 00 00 00 00 00 00 08 00 1C 00 66 69 6C 65 31
50 4B 03 04
→ Signature “PK”08 00
at offset 0x08 → Compression method 8
(DEFLATE)00 00 00 00
placeholders, followed by correct size
values).BadPack Manipulated Header Example:
50 4B 03 04 14 00 00 00 ED 7B 12 34 00 00 00 00 00 00 00 00 ED 7B 12 34 1C 00 66 69 6C 65 31
ED 7B
(interpreted as
an unexpected value rather than 08 00
) may indicate an
altered compression method.ED 7B 12 34
in little-endian) no longer match the actual payload size or the values
stored in the central directory.These alterations disrupt static analysis because tools attempt to decompress data using the wrong method or incorrect size.
The following Python snippet demonstrates how you might read and parse a local file header from a ZIP file. (In a real-world scenario, you could extend this to compare local and central directory headers and even “fix” them.)
import struct
def read_local_file_header(f, offset):
# Local file header format:
# Signature (4 bytes), Version (2 bytes), Flags (2 bytes),
# Compression Method (2 bytes), Mod Time (2 bytes), Mod Date (2 bytes),
# CRC32 (4 bytes), Compressed Size (4 bytes), Uncompressed Size (4 bytes),
# File Name Length (2 bytes), Extra Field Length (2 bytes)
= "<4s2B4HL2L2H"
header_format = struct.calcsize(header_format)
header_size
f.seek(offset)= f.read(header_size)
header_data
(signature, ver1, ver2, flags, comp_method, mod_time, mod_date,= struct.unpack(header_format, header_data)
crc32, comp_size, uncomp_size, fname_len, extra_len)
if signature != b'PK\x03\x04':
raise ValueError("Invalid local file header signature")
return {
"compression_method": comp_method,
"compressed_size": comp_size,
"uncompressed_size": uncomp_size,
"file_name_length": fname_len,
"extra_field_length": extra_len,
"header_size": header_size
}
# Example usage:
with open("sample.apk", "rb") as f:
= read_local_file_header(f, 0)
header print("Local File Header Info:", header)
In a BadPack scenario, if you compare the comp_method
or
comp_size
returned by this function to the corresponding
values from the central directory header, you might detect a
discrepancy. Analysts can then “repair” the header by overriding the bad
values with the correct ones from the central directory before feeding
the file into decompilation tools.
Static Analysis vs. Runtime Behavior:
Static analysis tools enforce strict adherence to ZIP format
specifications by checking both local and central directory headers.
When they detect mismatches, they abort processing. In contrast,
Android’s runtime installer relies only on the central directory header,
so despite the malformed local headers, the app installs and runs
normally.
Evasion:
By preventing analysts from easily extracting and viewing the
AndroidManifest.xml, malware authors can hide permissions, intent
filters, and other key configuration details that would normally reveal
malicious behavior.
Tool Limitations:
Even common utilities like 7-Zip, Apktool, Jadx, and even Apksigner may
fail or report errors when encountering these malformed headers.
However, specialized tools (such as the open-source APK Inspector) can
sometimes “repair” the headers and allow analysis.
SHA256: d3b07384d113edec49eaa6238ad5ff00...
SHA256: 6b86b273ff34fce19d6b804eff5a3f57...
update.badpack.net
data.collector-bp.org
rule BadPack_Android_Malware
{
meta:
description = "Detects BadPack Android malware"
author = "YourName"
date = "2025-02-14"
strings:
$str1 = "payload_encrypted" ascii
$str2 = "com.badpack.utils.Decryptor" ascii
$opcode = { 6A 02 59 6A 01 5B 51 52 }
condition:
any of ($str*) or $opcode
}
BadPack represents a clever anti-analysis tactic: it exploits the inherent flexibility of the Android runtime’s ZIP extraction by corrupting the local file headers, thereby breaking static analysis tools that depend on strict ZIP format validation. The result is a stealthy malware distribution method that lets malicious APKs (used by families like TeaBot/Anatsa) run on devices while evading detection during offline analysis.
This technique underscores the need for evolving analysis tools that can dynamically adjust to these header mismatches, or for manual intervention (via header “fixing” scripts) to properly inspect such malware.