PrivacySentry Documentation

repository·main·Indexed 25 days ago

https://github.com/allenymt/privacysentry

An Android privacy compliance detection tool that uses compile-time bytecode instrumentation (ASM + Booster) to intercept 60+ sensitive API calls. It generates automated compliance reports in Excel and JSON formats without runtime performance overhead. The tool features a four-layer architecture (Annotation, Plugin, Hook, and Proxy), supports multi-process scenarios, and includes reflection hooking to detect sensitive calls made via Java reflection.

Tokens
13.3K
Snippets
16
Records
62
Agent score
80%

What's inside PrivacySentry

  1. Overview of PrivacySentry

    main

    PrivacySentry is an Android privacy compliance detection tool designed to help developers avoid compliance issues during app store reviews. It uses compile-time bytecode instrumentation (based on the ASM + Booster framework) to ensure zero runtime performance overhead.

    Key features include:

    • Compile-time Bytecode Instrumentation: Zero runtime performance impact.
    • Comprehensive Interception: Supports interception of 60+ sensitive APIs to cover MIIT (Ministry of Industry and Information Technology) compliance requirements.
    • Automated Detection Reports: Generates detailed call records and statistical analysis in Excel format.
    • Smart Caching: Multi-level memory/disk caching for optimized performance.
    • Multi-process Support: Automatically handles multi-process scenarios with independent log output.
    • Extensibility: Supports custom interception rules and blacklist configurations.
  2. Get started with PrivacySentry

    main

    PrivacySentry is a privacy protection tool designed to intercept and monitor sensitive API calls. To begin using the project, you should consult the architecture documentation to understand its four-layer model (Annotation, Plugin, Hook, and Proxy layers) and the complete data flow from annotations to bytecode replacement.

    Key components include:

    • Annotation Layer: Core annotation system.
    • Plugin Layer: Gradle plugin and bytecode transformation mechanism.
    • Hook Layer: Runtime SDK and caching mechanisms.
    • Proxy Layer: Pre-configured API interception implementations.
    • Generated Artifacts: privacy_hook.json and Excel files containing detection results.
  3. Interpret PrivacySentry detection logs

    main

    PrivacySentry logs follow a specific format to help you identify when sensitive APIs are accessed and where they are called from:

    [PrivacyOfficer] {methodName}-{threadName}: {thread} | {apiType} | {callStack}

    Example Log: [PrivacyOfficer] getDeviceId-线程名: main | 读取IMEI | com.example.MainActivity.onCreate(MainActivity.kt:42)

    Handling check!!! warnings

    If you see check!!! 还未展示隐私协议,Illegal print in your logs, it means a sensitive API was called before the user consented to the privacy policy.

    Resolution:

    1. Ensure updatePrivacyShow() is called immediately after the user agrees to the privacy policy.
    2. Refactor code to ensure no sensitive APIs are invoked during the pre-consent phase.
  4. Integration strategies for PrivacySentry

    main

    Depending on your project architecture, use one of the following strategies to integrate PrivacySentry:

    • Component-based projects: It is recommended to copy the relevant classes out of the library and maintain them locally within your own codebase.
    • Plugin-based projects: Since integration is more complex in plugin architectures, it is recommended to wrap the library into an .aar file for easier distribution and usage.
  5. Understand the Static Scan Result File

    main

    When replaceFileName is configured in the Gradle plugin, PrivacySentry generates a JSON file (e.g., privacy_hook.json) in your project root. This file provides a static map of all intercepted sensitive API calls.

    File Structure

    • hookServiceList: A list of services that were intercepted.
    • replaceMethodMap: A mapping where keys are the intercepted sensitive methods (e.g., android.telephony.TelephonyManager.getDeviceId).
      • count: Number of times this method was intercepted.
      • originMethodList: A list of the actual code locations that called the sensitive method, containing originClassName and originMethodName.

    Use Cases

    1. Static Analysis: Perform offline audits of sensitive API usage.
    2. Compliance: Provide evidence to security teams for privacy reviews.
    3. Debugging: Verify that the plugin is correctly intercepting target methods.
    {
        "hookServiceList": [
            "com.example.TestService"
        ],
        "replaceMethodMap": {
            "android.telephony.TelephonyManager.getDeviceId": {
                "count": 1,
                "originMethodList": [
                    {
                        "originClassName": "com.example.DeviceManager",
                        "originMethodName": "getIMEI"
                    }
                ]
            }
        }
    }
  6. Customize interception with @PrivacyClassProxy

    main
    To implement custom interception, create a new class annotated with @PrivacyClassProxy. This class must follow the format defined in the privacy-proxy module to allow the bytecode transformer to correctly replace calls with your custom proxy logic.
  7. How PrivacySentry's four-layer architecture works

    main

    PrivacySentry uses a four-layer architecture to perform Android privacy compliance detection through compile-time annotation, bytecode instrumentation, and runtime hooking:

    1. Annotation Layer (privacy-annotation): Defines compile-time annotations used to mark methods, classes, or fields for interception. These annotations have @Retention(RetentionPolicy.CLASS), meaning they are available during compilation but not at runtime.
    2. Plugin Layer (plugin-sentry): A Gradle plugin that performs bytecode transformation during the build process using ASM and the Booster framework.
    3. Hook Layer (hook-sentry): The runtime SDK responsible for initialization, managing memory/disk caches, and collecting/outputting logs.
    4. Proxy Layer (privacy-proxy): Contains the actual implementations for intercepting pre-defined APIs and uses annotations to declare interception rules.
  8. Understand the PrivacySentry data flow

    main

    PrivacySentry operates in three distinct phases:

    1. Development/Compile Time:

      • Developers define proxy methods using @PrivacyClassProxy and @PrivacyMethodProxy.
      • The Gradle plugin (PrivacySentryPlugin) runs MethodProxyCollectTransform to scan for these annotations and MethodHookTransform to replace original API calls in the application bytecode with calls to your proxy methods.
      • A privacy_hook.json file is generated containing the mapping of hooked methods.
    2. Runtime Initialization:

      • PrivacySentry.Privacy.init(...) must be called as early as possible (ideally in Application.attachBaseContext()).
      • When a user agrees to the privacy policy, call PrivacySentry.Privacy.updatePrivacyShow() to update the internal state.
    3. Runtime Execution:

      • When the app calls a sensitive API (e.g., Build.getSerial()), the modified bytecode redirects the call to your PrivacyProxyCall method.
      • The proxy method checks inDangerousState() and either returns dummy data or the real value.
      • Intercepted calls are logged and can be exported to an Excel file.
  9. How reflection hooking works

    main

    Reflection hooking allows PrivacySentry to intercept sensitive API calls that are made using Java reflection rather than direct method calls. This is common in third-party SDKs (like JPush, GeTui, or advertising SDKs) that attempt to bypass standard API monitoring by looking up device identifiers (OAID, AAID, etc.) via reflection.

    Implementation Detail

    When hookReflex = true and a class/method is defined in reflexMap, the plugin detects the LDC (Load Constant) instruction in the bytecode that loads the class name and the method name, and replaces the subsequent Method.invoke() call with a proxy method.

    Configuration Requirements

    • Precision: Class names and method names must match exactly.
    • Activation: hookReflex must be set to true for reflexMap to have any effect.
    • Limitation: It cannot intercept dynamically generated class or method names.
  10. Understand the PrivacySentry logging data flow

    main

    PrivacySentry captures sensitive API calls at runtime and processes them through a structured pipeline:

    1. Capture: doFilePrinter() (via PrivacyProxyUtil) intercepts the call.
    2. Modeling: A PrivacyFunBean is built containing the function alias, name, call stack, and call count.
    3. State Check: If the SDK is initialized, data flows to PrivacyDataManager. If not initialized, data is stored in a sticky queue (粘性数据保存) to be processed later.
    4. Output: The BasePrinter list processes the data, sending it to various outputs like DefaultLogPrint (Logcat) and DefaultFilePrint (Excel files).
  11. How the PrivacySentry Transform process works

    main

    PrivacySentry operates using a two-stage Gradle Transform architecture during the compilation process to intercept and redirect sensitive method calls to privacy proxies.

    Stage 1: Pre-Transform (Collection)

    In this stage, the plugin scans all .class and .jar files to collect interception rules.

    • MethodProxyCollectTransform: Scans classes annotated with @PrivacyClassProxy. It looks for methods within those classes annotated with @PrivacyMethodProxy.
    • It extracts metadata such as the originalClass, originalMethod, and originalOpcode from the annotations.
    • The collected rules are stored in the HookMethodManager.

    Stage 2: Transform (Execution/Replacement)

    In this stage, the plugin performs the actual bytecode manipulation.

    • MethodHookTransform: Iterates through all class files and searches for method calls (MethodInsnNode) that match the rules collected in Stage 1.
    • When a match is found, the instruction is modified: the opcode is changed to INVOKESTATIC, and the owner, name, and desc are updated to point to the privacy proxy class and method.
    • Reflection Hooking: If hookReflex is enabled, the plugin also scans for LdcInsnNode (loading constants) to intercept reflection-based calls.

    Output

    The process results in:

    1. Modified .class files where sensitive calls are redirected.
    2. A privacy_hook.json file containing the generated hook configurations.
    编译期流程:
    ┌──────────────────────────────────┐
    │  输入:所有 .class 文件和 Jar    │
    └──────┬───────────────────────────┘
           │
    ┌──────▼─────────────┐
    │  第一阶段(预处理) │
    │  Pre-Transform     │
    └──────┬─────────────┘
           │
        ┌──┴──────────────┐
        │                 │
    ┌───▼────────────────┐   ┌───▼──────────────────┐
    │MethodProxy         │   │ ClassProxy           │
    │ CollectTransform   │   │ CollectTransform     │
    │ (收集方法拦截规则) │   │ (收集类替换规则)     │
    └───┬────────────────┘   └───┬──────────────────┘
        │                         │
        │ 结果写入:            │
        │ HookMethodManager     │ ReplaceClassManager
        │                      │
            ┌──────▼────────────────┐
            │  第二阶段(执行替换) │
            │  Transform            │
            └──────┬─────────────┘
                   │
        ┌──────────┼──────────────┬──────────────┐
        │          │              │              │
    ┌───▼───┐ ┌───▼───┐ ┌───────▼──┐ ┌────────▼───┐
    │Method │ │Field  │ │ Class    │ │ Service    │
    │Hook   │ │Proxy  │ │ Proxy    │ │ Hook       │
    │       │ │       │ │          │ │            │
    └───┬───┘ └───┬───┘ └───────┬──┘ └────────┬───┘
        │         │              │              │
        └─────────┴──────────────┴──────────────┘
                   │
            ┌──────▼──────────┐
            │ FlushHookData   │
            │ Transform       │
            │ (生成hook配置)   │
            └────────┬────────┘
                     │
            ┌────────▼──────────────┐
            │ 输出:修改后的 .class 和  │
            │ privacy_hook.json     │
            └───────────────────────┘