ShaulRosenzwieg
3,404 views
39 slides
May 01, 2018
Slide 1 of 39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
About This Presentation
Binder is what differentiates Android from Linux, it is most important internal building block of Android, it is a subject every Android programmer should be familiar with
Size: 807.06 KB
Language: en
Added: May 01, 2018
Slides: 39 pages
Slide Content
Binder Android IPC By: Shaul Rosenzweig
What is IPC? Framework for the exchange of signals and data across multiple processes Used for message passing, synchronization, shared memory, and remote procedure calls (RPC) Enables information sharing, computational speedup, modularity, convenience, privilege separation, data isolation, stability Options: files (fs or memory mapped), Signals, Sockets, Pipes, Semaphores, Shared Memory, Message passing (queues, message bus) Android specific options: Intents, ContentProviders, Messengers, Broadcast receivers, IBinder, AIDL, all are based on Binder framework
What is Binder? An IPC/component system for developing object oriented OS services Not yet another object-oriented kernel Instead an object-oriented operating system environment that works on traditional kernels, like Linux! Essential to Android! EVERYTHING GOES THROUGH BINDER!!!! ...well, nearly everything
Normal OS App calls syscalls Kernel talks to hardware Constant syscalls can hog resources, destabilize kernel, etc Kernel App Hardware
Android App gets Manager Manager talks to Service via binder Service calls kernel Kernel talks to hardware Binder adds security System Service manages kernel resources (hardware) Binder is only kernel resource directly available to apps App Binder System Service Kernel Hardware
Why Binder? Android apps and system services run in separate processes for security, stability, and memory management reasons, but they need to communicate and share data Android implementation of libc does not support System V IPC, low memory killer would create resource leakage, or worse
Binder PRO and CON PRO: Security (identity of sender and receiver), Object reference counter, death notifications, automatic management of thread pools, invoking remote objects as local, synchronous and asynchronous (oneway), ability to send file descriptors & shared memory, AIDL, local execution mode (no marshalling if inside same process) CON: no RPC, client server based (not suited for streaming), not defined by POSIX or any other standard, single attack surface for nearly everything, limited to 1mb
Binder history Started at Be inc, for “next generation BeOS” around 2001 Acquired by PalmSource, first implementation for Palm Cobalt Palm switched to Linux, ported (~ 2005) and open sourced (OpenBinder) Used in Android as is initially, totally rewritten (~2008) Focused on scalability, stability, flexibility, low-latency/overhead, easy programming model Google hired Dianne Hackborn, key engineer from Be inc times and tech owner of OpenBinder project, to join Android team just after they acquired Android inc. https://plus.google.com/+DianneHackborn http://www.angryredplanet.com/~hackbod/
Going wide...
Binder terminology Binder (Framework) - IPC architecture Binder Driver - kernel module Libbinder - native library above kernel Binder Protocol - low level ioctl based IBinder interface - methods that Binder objects must implement AIDL - Android Interface Definition Language, used to describe operations on IBinder interface Binder Object - generic implementation of IBinder interface Binder Token - 32 bit integer that uniquely identifies a Binder object across all processes on a given system Binder Service - Actual implementation of Binder Object Binder Transaction - act of invoking an operation on a remote Binder object Parcel - container for a message (data and object references), one for outbound, one for inbound reply Proxy - implementation of AIDL interface that un/marshalls data and maps method calls to transactions - client side Stub - partial implementation of AIDL interface that maps transactions to methods (service side) Context Manager (servicemanager) - binder object with well known handle, used to discover other Binder Objects
Sample well known Binder implementations Intents Content providers/resolvers Messenger Broadcast / Broadcast receiver System service Managers Activity/Service lifecycle calls (ActivityManager) AIDL / bound service
Intents Framework for asynchronous communication among components They may run in same or across different processes Point to Point or publish - subscribe Intent represents a message containing description of the operation to be performed as well as data to be passed Implicit intents enable loosely coupled APIs Facilitated by system services: ActivityManagerService and PackageManagerService
Content provider / resolver ContentResolvers communicate synchronously with ContentProviders via a fixed CRUD (create/read/update/delete) API Well suited for database queries Content providers and Intents are rather similar CONS: lifecycle callbacks on UI thread, not well suited for low latency, API is loosely defined so prone to runtime errors, facilitated by ActivityManagerService and PackageManagerService
Messenger IPC Messenger represents a reference to a Handler that can be sent to a remote process via an Intent Messages sent by remote process are delivered via the messenger to the local handler Messages designate operation (Message.what) and data (Message.getData()), similar to Intents Still asynchronous, but lower latency than Intents and Content providers Well suited for callbacks from service to the client
Intent intent = new Intent("com.myapp.service.SERVICE"); ArrayList<Uri> uris = intent.putExtra("uris", uris); Messenger messenger = new Messenger(new MyHandler(this)); intent.putExtra("callback-messenger", messenger); super.startService(intent); … private static class MyHandler extends Handler { private final WeakReference<MyActivity> clientRef; public MyHandler(MyActivity client) { this.clientRef = new WeakReference<MyActivity>(client); } @Override public void handleMessage(Message msg) { MyParcelableClass data = msg.getData(); //do stuff with it } }
AIDL: Android Interface Definition Language Sample : https://github.com/shaulr/FooService AIDL allows you to define the programming interface that both the client and service agree upon in a simple and efficient way Similar to simplified Java, with addition of keywords in , out , inout and oneway Similar concept to CORBA or COM, without RPC Need to import each Serializable and Parcelable object, which also need their own AIDL files AIDL is compiled to Java, where Stub and Proxy sides are implemented automatically Only methods can be exposed via AIDL, not fields, and all are public
If params are not primitives (primitives are “ in ” only), they require a directional flag: “ inout ” means it will be copied in both directions, “ in ”; it will be copied into the service, “ out ”; it will be copied back to the client If we add modifier “ oneway ” before method, it will be asynchronous, callback will come on Binder thread, not on original calling thread Service can throw back to the client only one of the following exceptions: SecurityException , BadParcelableException , IllegalArgumentException , NullPointerException , and IllegalStateException
AIDL types null Java Primitives and their arrays CharSequence and String (as UTF-16) FileDescriptor (as dup of original) Serializable (not efficient, made for serialization to storage) Map ( reconstructed as HashMap) Bundle List (reconstructed as ArrayList) Object[] SparseArray and SparseBooleanArray IBinder and IInterface (transferred by globally unique reference, not copied over, used for callbacks, strong binder) Parcelable, for custom types Parcelable can not be serialized to storage, unlike Serializable You need to implement marshalling and unmarshalling yourself Mechanism for flattening objects, proxy calls writeToParcel, stub re-creates the object
package com.tikal.fooservice; public interface IFooService extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.example.app.IFooService { … public static com.example.app.IFooService asInterface( android.os.IBinder obj) { … return new com.example.app.IFooService.Stub.Proxy(obj); } … public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { … case TRANSACTION_save: { … com.example.app.Bar _arg0; … _arg0 = com.example.app.Bar.CREATOR.createFromParcel(data); this.save(_arg0); … } … } … } …
Using generated class Service should extend IFooService.Stub By implementing onBind, onUnbind and onDestroy, service will be able to be registered by Binder framework with Binder driver and wait on requests Stub takes care of translating parcels to and from service Custom app service can not register with service manager, it accepts only trusted sources Therefore we have to create a regular service whose job is to provide Stub implementation to the client Service should return Stub implementation in onBind, and we are done Client will use IFooService via IFooService.Proxy Client should bind to the service using bindService API which goes through the PackageManager, similar to Intent Once bound, in onServiceConnected callback, you ask the IFooService.Stub.asInterface(service) and get the Proxy side of the interface
Binder Object mapping Binder object reference is virtual memory address if in same process, or an abstract 32 bit handle if in different process driver maps addresses to and from local addresses automatically driver maintains mapping between local and global addresses
Sharing memory via Binder Binder is limited to passing 1mb of data per transaction, if larger it will throw TransactionTooLargeException If data comes from a file, just pass the FileDescriptor If it is in memory, it is possible to send it in chunks, but it will complicate the design Alternatively, send it via JNI, use libbinder’s Parcel::writeBlob and Parcel::readBlob https://android.googlesource.com/platform/frameworks/native/+/jb-dev/libs/binder/Parcel.cpp They are implemented using ashmem and writeFileDescriptor and readFileDescriptor but this functionality is not exposed to Java
Diving in deep...
Binder driver Processes can’t invoke operations or access memory on another process, but kernel can Exposed via /dev/binder, offers simple API based on open, release , read , poll , mmap , flush and ioctl write_buffer contains commands for driver to perform read_buffer will contain commands for user-space to perform Clients communicate via transactions which contain token, code, data buffer and sender PID/UID (added by driver) Java/AIDL talks to libbinder via JNI, which talks to the driver struct binder_write_read { signed long write_size; signed long write_consumed; unsigned long write_buffer; signed long read_size; signed long read_consumed; unsigned long read_buffer; };
Binder Driver operations open establishes connection to binder driver and assigns it a linux file pointer release closes the connection mmap is needed to map Binder memory Main operation is ioctl, higher layers submit commands by ioctl: BINDER_WRITE_READ - most important, submits a series of transmission data BINDER_SET_MAX_THREADS - sets maximum number of threads per process BINDER_SET_CONTEXT_MANAGER - sets the context manager, happens once BINDER_THREAD_EXIT - sent by middleware, if Binder thread exists BINDER_VERSION - returns version number BC_REGISTER_LOOPER, BC_ENTER_LOOPER and BC_EXIT_LOOPER - bookkeeping of processes binder thread pool BC_INCREFS, BC_RELEASE and BC_DECREFS - reference counting BC_REQUEST_DEATH_NOTIFICIATION, BC_CLEAR_DEATH_NOTIFICATION, BC_DEAD_ BINDER_DONE - recognize and manage death of nodes
Binder transaction BC_TRANSACITON and BC_REPLY cause a transit of data to another Binder interface BC_REPLY is used by middleware to answer a received BC_TRANSACTION Binder driver takes care of delivering a reply to waiting thread Binder driver copies the transmission data from user memory address space of the sending process to its kernel space, and then to the destination process This is achieved by kernel functions get_user and put_user Source found on AOSP /drivers/staging/android/binder.c and h
Putting it all together Most clients don’t want to know about IPC, never mind Binder or proxies, they count on managers to abstract the complexity for them How do we get the service handle? Ask servicemanager (Binder’s CONTEXT_MGR ) and hope it was already registered with it For security and sanity reasons, binder driver will only accept single one-time CONTEXT_MGR registration, that’s why servicemanager is one of the first services to start on Android To get list of services registered on service manager run adb shell service list
Getting the service handle flow
Making a request to a service flow
Caveat: single attack surface to (nearly) everything Paper and lecture from BlackHat Europe 2015 by team from CheckPoint Gaining root is easy, with root use ptrace to hook ioctl binder calls Keylogger: input method talks with app via binder Banking app intent hijacker: they caught intent on route and changed amount and target account number for bank transfer Defense: don’t trust binder with sensitive data, and EVERYTHING GOES THROUGH THE BINDER!!!! Encrypt the sensitive data passed via binder Implement your in-app keyboard for passwords Fixed by SeLinux: does not allow ptrace hooks https://www.youtube.com/watch?v=O-UHvFjxwZ8
More Binder caveats and gotchas: Binder threads Binder is, as we said, limited to 1mb per transaction Binder thread pool is limited to 15 per process Avoid blocking binder threads
Binder as arbiter of platform security Binder driver automatically adds calling process PID and UID, which is the base for most of Android Platform security, there are exceptions, which use concept of Unix/Linux groups IDs, like WRITE_EXTERNAL_STORAGE Caller PID/UID provides trusted execution environment, by providing callee with identity Special user, System User with UID 1000, is always allowed access (not to be confused with linux root user, which has UID 0) Service can get this info via android.os.Binder.getCallingPid() and getCallingUid(), then get package with PackageManager.getPackageByUid() Service needs to call PackageManager.getPackageInfo() with GET_PERMISSIONS flag Alternative way: Context.checkCallingOrSelfPermission and enforceCallingPermission https://android.googlesource.com/platform/frameworks/base.git/+/android-4.2.2_r1/services/java/com/android/server/VibratorService.java
Binder logs Binder driver reports various stats on active/failed transactions via /proc/binder/ /proc/binder/failed_transaction_log /proc/binder/state /proc/binder/stats /proc/binder/transaction_log /proc/binder/transactions /proc/binder/proc/<pid> If debugfs is enabled, replace /proc/binder with /sys/kernel/debug/binder