YinkoShield

Knowledge Center / Mobile runtime attacks / mobile runtime attacks · 2026·01

Malicious input-method editor (IME) compromise

The Input Method Editor — keyboard, voice input, handwriting recogniser — is the chokepoint between physical key taps and the host application's text fields. It runs in its own process, holds an InputConnection to whichever EditText is focused, and has both read access to the input stream and write access to the host's text. A compromised or malicious IME is a structural keylogger and a structural injector simultaneously.

[ malicious IME — input chain compromise ] user tap on key soft-keyboard surface [ IME process ] InputMethodService reads · every key tap · EditText context · field semantics writes · commitText (any string) · deleteSurroundingText · sendKeyEvent host app EditText receives whatever IME committed substrate signal: runtime.environment { active_ime: package + system_signed flag }
The IME is the chokepoint between physical key taps and the host app's text field. A compromised IME has read access to every keystroke and write access to the host's input.

1. Mechanism

An Input Method Editor implements InputMethodService [1] and is selected by the user as the active IME. Once active, the platform binds an InputConnection between the IME process and the focused EditText in whichever app is foregrounded [2]. Every key tap on the soft keyboard is processed by the IME first — the IME decides what gets commitText()-ed back to the host app.

The IME has full bidirectional capability:

  • Reads: it sees every character the user types, plus the surrounding text via getTextBeforeCursor() and getTextAfterCursor(), plus the field’s inputType (password, email, number) and any EditorInfo hints the host app declared.
  • Writes: commitText() writes any string into the host’s field, deleteSurroundingText() removes characters, sendKeyEvent() injects key events including special keys.

The IME process is, by design, in the input chain. The only checkpoint is which IME the user has selected.

2. Where in the runtime it operates

The IME runs in its own process under the IME framework managed by InputMethodManagerService (system-server). The platform’s trust boundary is the IME’s installation provenance and signature lineage: a platform-signed IME (the AOSP keyboard; manufacturer keyboards on OEM builds) and a privileged-app IME preinstalled at build time and signed by a known publisher (Gboard, which is Google-Play-signed and shipped as a privileged app on GMS / Pixel devices) both carry different platform privileges than a user-installed third-party IME — but they arrive at that posture by different routes (platform signing vs priv-app preinstallation), and the substrate distinguishes them by package identifier + signature chain rather than by a single “system-signed” flag.

Per-field flags such as EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING, IME_FLAG_NO_FULLSCREEN_UI, and IME_FLAG_NO_EXTRACT_UI constrain what the IME may retain and how it may render the input surface; they do not constrain what the IME may observe — the data still passes through the IME process on the way to the host app.

3. Which checkpoints it bypasses

  • Play Integrity / App Attest. Both pass cleanly. The IME is a legitimate platform API user, the device profile is unchanged, and the calling APK is correctly signed.
  • FIDO2 / passkeys. Platform-authenticator user-verification (PIN, biometric) is rendered by the system surface — Credential Manager on Android 14+, BiometricPrompt below, the system passcode UI on iOS. These surfaces do not bind an InputConnection to the active IME, so the UV factor is not visible to a malicious IME. A passcode-style fallback typed into a host-app EditText (legacy app-rendered PIN flows) does pass through the IME and is observable; that is an app-design issue, not a FIDO2 property.
  • EMV / 3DS. The PAN entry that feeds the cryptogram is the PAN the user typed; if the IME retained it, the rail call is unaffected.

The IME compromise sits outside everything the standard checkpoints observe.

4. Which signals make it observable

runtime.environment. The Trusted Runtime Primitive observes the active IME at the moment the user is typing in a sensitive field. Two properties matter for the operator’s policy:

  • The IME’s package identifier, signature lineage, and priv-app / status.
  • Whether the IME’s package is on the operator’s allowlist for sensitive fields (some operators allowlist only platform-signed or known priv-app IMEs and a small set of vetted third-party ones).

5. Evidence Token shape when observed

The following example is illustrative; field names, type values, and schema are defined in YEI-001 §4 (available through the spec-access process).

{
  "ev": [{
    "ts":   "2026-06-15T10:23:14Z",
    "class": "runtime.environment",
    "type":  "ime.active",
    "data": {
      "package":       "com.example.ime",
      "system_signed": false,
      "during":        "credential_entry",
      "field_type":    "TYPE_TEXT_VARIATION_PASSWORD"
    }
  }]
}

A non-system, third-party IME active during credential entry is not by itself evidence of compromise — many users use well-vetted third-party IMEs by choice. The signal informs the operator’s policy; combined with installation source, signing chain, and prevalence data on the IME’s package, it becomes actionable. Execution Evidence Infrastructure (EEI) — the device-identity infrastructure layer for banking and payments — signs the IME identity present at the sensitive moment so the operator’s policy reads it together with the rest of the runtime context.

6. Cross-references

7. External references

[1] Android Developers. InputMethodService. developer.android.com/reference/android/inputmethodservice/InputMethodService. Cited 2026-01-09.

[2] Android Developers. InputConnection. developer.android.com/reference/android/view/inputmethod/InputConnection. Cited 2026-01-09.

[3] Android Developers. EditorInfo flags (IME_FLAG_NO_PERSONALIZED_LEARNING, IME_FLAG_NO_FULLSCREEN_UI, IME_FLAG_NO_EXTRACT_UI). developer.android.com/reference/android/view/inputmethod/EditorInfo. Cited 2026-01-09.