‘It’s 2202, and you are still concerned about the security of Android Service?’ Yes, that’s right. However, what we are talking about here is not just the Service, which is one of the four major components of Android, but also the Android system Service. At the beginning of the popularity of Android systems, there were countless researchers studying the security of application layer Service. However, up to now, there is still little systematic research on the security of Android system Service.
We will comprehensively analyze the types and usage methods of Android Service, including application layer Service, framework layer Service, and Native layer Service, and examine the architecture of Android Service from the perspective of source code. Of course, we will also intersperse the explanation of Binder, one of the core components of Android. Since we are not Android application developers or system developers, we will not elaborate on the underlying principles of Binder (In the Android system, the IPC mechanism is called Binder. The security of Android Binder services has always been a research direction for many vulnerability researchers), and we will focus on the principles, framework, and existing security issues of Service.
0x00 Basic Knowledge of Xiao Bai
0x01 Binder

Binder is a new inter-process communication mechanism based on OpenBinder for Android development, providing remote procedure call (RPC) functionality.
Intuitively, Binder is a class in the Android SDK, inheriting the IBinder interface;
From the IPC perspective, Binder is the core inter-process communication method in Android, which does not exist in Linux, and makes up for some of the shortcomings of traditional Linux IPC;
From the perspective of the device, Binder can be understood as a virtual physical device, with the device driver being /dev/binder;
From the Android application layer, Binder is the communication medium between the client and server, for example, when the client binds to a Service, it will return a Binder object containing the server's business calls. Through this Binder object, the client can obtain the services or data provided by the server (Android Service is a typical architecture of this kind).
Binder 架构图如下所示,如果你不理解,没有关系,这张图只是给大家一个感性的认识,说明 Binder 其实自顶向下贯穿 AOSP 整个架构。你会在后文中慢慢理解这张架构图的含义。
The Binder architecture diagram is as follows. If you do not understand, it is okay. This diagram is just to give everyone a sensory understanding, to show that Binder actually runs through the entire AOSP architecture from top to bottom. You will gradually understand the meaning of this architecture diagram in the following text.
0x02 Service
Android Service can refer to the service that Android developers often say is one of the four major components of Android, or it can refer to the many services provided by the Android framework itself. The types of Android Service are as shown below
Application Layer Service
System Service
Android Service (usually refers to the Service written in Java)
Native Service (Service written in C++)
Service communicates between different components through the Binder channel provided by intent. Through intent, we can pass parameters to the target Service to achieve certain functions. Of course, Service can also provide some interface methods, which can be called by the application itself or provided to other applications.
0x10 Application Layer Service
Here, the general Service refers to the application layer Service, which is a Service defined by the application developer. As one of the four major components of Android, the application layer service is mainly a solution for implementing background operations in Android. Activity provides the user interface, while Service is just suitable for operations that do not require interaction with the user.
Here, we will briefly discuss the usage of the application layer Service. The service end definition is as follows
public class MyService extends Service {
public MyService() {
}
@Override
/* Binding service, it is an abstract method, must be overridden */
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
/* Create service, if the service is already running, it will not be executed */
public void onCreate() {
super.onCreate();
}
/* Handle business, receive intent */
public int onStartCommand(Intent intent, int flags, int startId) {
// Log.i(TAG, intent.getStringExtra("MainActivity"));
return super.onStartCommand(intent, flags, startId);
}
/* Destroy */
public void onDestroy() {
super.onDestroy();
}
}
Android Studio will automatically register the service in the Manifest file. The Activity registers through start/stopService
Start or stop the service. You can also use bindService()
Start the service.
final Intent intent = new Intent(this, MyService.class);
Button button = findViewById(R.id.fisrt_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intent.putExtra(TAG, "This is MainActivity");
startService(intent); // Start the service
stopService(intent); // Stop the service
}
});
The following figure is Android DeveloperThe example diagram used on the official website to explain the Service lifecycle. The left figure shows the lifecycle when creating thestartService()
Lifecycle, the right figure shows the lifecycle when creating the servicebindService()
.
The Service lifecycle of both methods
startService: onCreate -> onStartCommand -> onDestory
bindService: onCreate -> onBind -> onDestory
0x11 Component communication with the application layer Service
By binding components to Services, it is very convenient to implement inter-process or inter-process communication. There are many ways to use Service for inter-process communication, each with its specific application scope.
1 Extend the Binder class
Add an inner class binder in the inherited Service class and define some common methods. By binding the Service to the front-end component, the public methods of the binder subclass can be called. If the service is justIntra-process callPrefer to use this method.
In the Service classCreate binder subclass and use onbinder method to return binder instance
private static final String TAG = "MyService";
private LocalBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
public void start() {
Log.d(TAG, "start:");
}
public void end() {
Log.d(TAG, "end:");
}
}
@Override
/* Binding service, it is an abstract method, must be overridden */
public IBinder onBind(Intent intent) {
return mBinder;
}
Components to be associated, create ServiceConnectionAnonymous inner class
private MyService.LocalBinder loadBinder; // Get the LocalBinder defined in the service
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// Binding successful, you can call any public method
loadBinder = (MyService.LocalBinder) service;
loadBinder.start();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Binding or unbindingService
bindService(intent, connection, BIND_AUTO_CREATE);
unbindService(connection);
2 Use Messenger
Messenger is to implement IPC (Inter-process communicationThe simplest method, Messenger creates a message queue containing all requests through a single thread, developers do not need to consider thread safety.
Create Messenger on the server sideAnonymous inner class, and return instance using onbinder
public class MyService extends Service {
private static final String TAG = "MyService";
Messenger serverMessenger = new Messenger(new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
/* Receive message */
Bundle bundle = msg.getData();
String recv = bundle.getString("message_from_client");
Log.d(TAG, recv);
/* Send message */
Messenger clientMessenger = msg.replyTo; // Client messenger
Message message = new Message();
Bundle bundleSend = new Bundle();
bundleSend.putString("message_from_server", "Hi, this is MyService");
message.setData(bundleSend);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
@Override
public IBinder onBind(Intent intent) {
return serverMessenger.getBinder();
}
}
the components to be associated (i.e., the client), create ServiceConnectionAnonymous inner class
private Messenger serverMessenger;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serverMessenger = new Messenger(service); // Obtain the server messenger through the service
Message message = new Message();
message.replyTo = serverMessenger;
Bundle bundle = new Bundle();
bundle.putString("message_from_client", "Hello, this is client_MainActivity");
message.setData(bundle);
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Binding or unbinding Service with the one mentioned earlierExtend the Binder classbe the same.
3 Use AIDL
AIDL (Android Interface Definition Language) can bind to another process's service through AIDL to achieve inter-process communication. Compared to Messenger, AIDL inter-process communication canAllow a Service to handle multiple requests at the same time.
Android Interface Definition Language (AIDL) is a tool available to users for abstracting IPC. For example, in
.aidl
For the interface specified in the file, various build systems will useaidl
Construct a binary file binding in C++ or Java to use this interface across processes, regardless of its runtime environment or bitness.
in the Service application,}Create a new AIDL file. The AIDL file is actually an interface file, similar to the header file in C language, and other applications can call the methods declared by AIDL.
interface IMyAidlInterface {
/* Automatically generated methods, describing the data types supported by the current AIDL, which can be ignored */
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
/* Methods intended to be provided for inter-process calls by other applications */
String getTime();
}
Synchronize AIDL information (sync project-> rebuild), create an inner class in the Service class that inherits the Stub class of the interface, and implements the relevant methods declared by the AIDL interface.
public IBinder onBind(Intent intent) {
return myBinder;
}
MyBinder myBinder = new MyBinder();
class MyBinder extends IMyAidlInterface.Stub {
public String getTime() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(new Date());
}
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString){};
}
About Android Stub class
Stub, which is the stub class, implements an interface, but each method implemented is empty. If an interface has many methods, to implement this interface, all methods must be implemented. However, from a business perspective, a class may only need one or two methods. If you implement the interface directly, in addition to implementing the required methods, you also have to implement all the irrelevant methods. But if you implement the interface by inheriting the stub class, you can avoid this trouble.
Client application copies AIDL files, it is necessary to copy the entire AIDL file on the server side, including the package name. Similarly, after copying the AIDL file, it needs to be synchronized, otherwise other classes cannot refer to the methods declared in the AIDL file.
the client application binds to the Service, and in onServiceConnectedCall the AIDL interface method in the method, which is the same as what was said beforeExtend the Binder classIt is still the same.
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
String result = iMyAidlInterface.getTime();
Log.d(TAG, result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
/* Bind service */
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.service", "com.example.service.MyService");
intent.setComponent(componentName);
bindService(intent, connection, BIND_AUTO_CREATE);
If setComponent is not used to explicitly bind the Service component, setAction can also be used to bind the Service, but in this case, it is necessary to add in the service end Android Manifest.xml file,Declare the Service component as remote and add an action, the action must be named service package name.aidl
<service
android:name=".MyService"
android:process=":remote"
android:exported="true"
<intent-filter>
<action android:name="com.example.service.IMyAidlInterface" />
</intent-filter>
</service>
A brief summary, regardless of whether Service extends the Binder class, uses Messenger, or AIDL, the client binds to the Service using the same method, and overrides the onServiceConnected method in the ServiceConnection instance to complete the corresponding IPC.
0x20 System Service
System Service, that is, the Android system service, is different from the Service at the application layer (one of the four major components of Android, implemented by application developers themselves), the system Service is at the Framework layer of the Android system, **The Android Service we usually talk about refers to the Service written in Java. While Nativce Serice refers to the Service written in C++.**System Service is predefined by AOSP, and manufacturers can also implement their own system Service. Application developers only need to use the Service directly. When talking about system Service, we must mention Service Manager.
All system services are managed by Service Manager and will be registered in Service Manager.When our own APP wants to use system Service, we can obtain the service through Service Manger and use it. System Service and Service Manager are at the Framework layer.
You can use service listList all system services (format:Service name[interface name/description]),The system services built-in in the OnePlus 3T phone are as follows
0x21 Android Service
Service nameIt is determined by the first parameter of addService in the ServiceManager of AOSP source code, for example iphonesubinfoService, registration path: /frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneSubInfoController.javaThrough addService to register the service, and through getService to obtain the service.
Interface descriptionThat is, the interface name, which is mostly corresponding to the AIDL file name
How to find the definitions of these methods?
Search the AOSP source code according to the pattern of implementing AIDL at the application layer IPhoneSubInfo.Stubto get the code path of the Service implementation /frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneSubInfoController.java
The client calls the method provided by iphonesubinfo, taking the unit test Robolectric provided by AOSP as an example (android-all-7.1.0_r7-robolectric-0.jar), the client calls the getDeviceId method of the iphonesubinfo service, which actually implements AIDL.robolectricClass com.android.internal.telephony.IPhoneSubInfoImplementation.
The client creates a Proxy class, unifies the implementation of the methods provided by the server class (com.android.internal.telephony.IPhoneSubInfo), but the actual methods are still defined on the server side. Of course, the client's IPhoneSubInfo also needs to copy the server's asInterface
The asInterface method converts the IBinder object to the IInterface interface
The interface object converted directly calls the relevant methods
The principle diagram is as follows, with a deeper understanding can be gained from the analysis of the context.
0x22 Native Service
For Native Service, the description is provided by IMPLEMENT_META_INTERFACEDefinition, for example media.drmService (Digital Rights Management)
The service is a typical Native Service (Server) code as follows /frameworks/av/drm/libmediadrm/IMediaDrmService.cpp,CHECK_INTERFACE is used to determine if the interface passed in by the client matches the server. An error will be thrown if they do not match "Binder invocation to an incorrect interface"
.
Client CallThe media.drm service is called multiple times, taking nuplayer as an example /frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
Key Methods
Parcel.readStrongBinder(): Reads IBinder type from the parcel object
Parcel.writeStrongBinder(): Writes IBinder to Parcel
interface_cast: Converts the IBinder object to a strong reference type IInterface
When combined with the code, the principle diagram becomes simpler.
Client: BpBinder.transact() sends data
Server: BBinder.onTransact() receives data
BpBinder and BBinder are both representatives of Android communication and Binder, and are derived classes of IBinder.
BpBinder, the proxy class used by the client to interact with the Server
BBinder, BpBinder correspond to the server-side IBinder.
One point to note here is:onTransact() method as the function that truly handles the business logic, its implementation is not in BBinder, but in BnBinder derived from BBinder. I believe that careful students have already seen the implementation of BnMediaDrmService in the above case.
0x23 ADB communication with system Service
We have already explained the Service of the framework and native layers in the above text, and the corresponding client calling methods have also been roughly sorted out, but the explanations are all about the framethe client of the framework layer calls the service of the framework layer, originalclient calls the native serviceSo how can ordinary developers or security testers directly call the system provided Service?
adb provides service callYou can directly call the method provided by the system Service, we still take the iphonesubinfo service as an example. 1 represents the first method of the AIDL file, which is what we mentioned earlier getDeviceId
Of course, you can also pass parameters to the corresponding function
It can also be filtered out by regular expression matching
# IMEI
adb shell "service call iphonesubinfo 1 | grep -o '[0-9a-f]\{8\} ' | tail -n+3 | while read a; do echo -n \u${a:4:4}\u${a:0:4}; done"
# IMSI
adb shell "service call iphonesubinfo 7 | grep -o '[0-9a-f]\{8\} ' | tail -n+3 | while read a; do echo -n \u${a:4:4}\u${a:0:4}; done"
# ICCID
adb shell "service call iphonesubinfo 11 | grep -o '[0-9a-f]\{8\} ' | tail -n+3 | while read a; do echo -n \u${a:4:4}\u${a:0:4}; done"
0x24 APP communication with system Service
For Framework Service,Provide AIDL filesThese services can communicate with the application layer Service directly, and the client can communicate with them using AIDL;Native Service, only some of which provide AIDL (such as the HIDL of the HAL layer)You need to obtain the Binder object directly and then call the transact function of the Binder object.
Using AIDLIt is not very stable and needs to import AIDL files, which is not friendly for black-box testing. The usage is similar to the previous one.
Reflect to get ServiceMangerIt is stable, but there is a possibility of failure.
If you have an Android compilation environment, you can android.os.ServiceManagerEasily use ServiceManager to find the corresponding Service and call the relevant methods.
// NativeService.java
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
public class NativeService {
private static final String SERVICE_NAME = "nativeservice";
public static boolean invoke(int code, Parcel data, Parcel reply) throws RemoteException {
IBinder service = ServiceManager.getService(SERVICE_NAME);
if(service != null){
Parcel data = Parcel.obtain();
data.writeInterfaceToken(TOKEN);
data.appendFrom(data, 0, data.dataSize());
boolean result = service.transact(code, data, reply, 0);
data.recycle();
return true;
}
Log.e("shell", "service not running");
return false;
};
}
But unfortunately, more often than not, we are just ordinary application developers, android.os.ServiceManager
It is a hidden class that is not in android.jar, so it cannot be called directly. However, fortunately, **any hidden class can be called using the reflection API.** As shown below
// ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId();
try {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
/* Obtain the ibinder object of Service through reflection */
IBinder iBinder = (IBinder) Class.forName("android.os.ServiceManager").getMethod("getService", String.class).invoke(null, "iphonesubinfo");
/* Obtain interface description through the ibinder object */ /* If there is no interface, i.e., Nativce Service, it is not necessary to write */
iBinder.transact(IBinder.INTERFACE_TRANSACTION, data, replay, 0);
/* Write interface description */
data.writeInterfaceToken(replay.toString());
/* Pass parameters (if any) */
data.writeInt(0);
Log.d(TAG, replay.readString());
/* Send data */
iBinder.transact(1, data, replay, 0);
/* Receive data */
Log.d(TAG, replay.readString()); // It is actually empty, the reason is unknown
/* Recycle the parcel object */
data.recycle();
replay.recycle();
} catch (Exception e) {
e.printStackTrace();
}
Note that the system Service may require permissions, and therefore, the permission should also be declared in the AndroidManifest file. After Android Marshmallow (6.0),Risk permissions need to be dynamically applied.
transactDescription of method parameters
code, also known as TransactionID, indicates the method number of the server side.
data, the parameters that the client needs to pass.
replay, the parameters returned by the server.
flags, 0 represents a return value, 1 represents no return value.
0x25 APP communication with other application services
In addition to the several conventional methods mentioned at the beginning of the article, application communication with other application services can also be achieved directly by obtaining the iBinder object through binderService, and then invoking through the AIDL method.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.my_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setComponent("com.xxx.text", "com.xxx.test.service");
bindService(intent, connection, BIND_AUTO_CREATE);
}
});
}
IBinder binder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = service;
// Through bindService to obtain the iBinder object
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
_data.writeInterfaceToken("com.xxx.test.service.aidl.xxxExtendService");
}
@Override
public void onServiceDisconnected(ComponentName name) {
binder = null;
}
};
The several methods mentioned in 0x23-0x25 are commonly used Service communication methods by security testers, while Section 0x11 explains more from the perspective of developers on how to communicate with Service.Once you master the basic communication methods, the subsequent testing will naturally follow.
0x26 Service Security
For Service, these interfaces between components are our focus. For security testing, understanding the knowledge in the above section is sufficient for beginners to delve into Android Service vulnerabilities, and you can directly read the chapter "Service attack surface and security issuesHowever, if you want to delve deeper from the source code perspective, then the subsequent chapters "Case Analysis of DRM Source Codeis a good choice for reading.
0x30 Hardware Abstraction Layer HAL
Android HAL (Hardware Abstraction Layer) is located between Framework and kernel. The Treble project is a major architectural change in the Android operating system framework, officially introduced in Android 8.0. It aims to allow manufacturers to update devices to the new Android system more easily, quickly, and at a lower cost.
By introducing HIDL (HAL Description Language), Framework and HAL are separated, and device suppliers only need to build HAL once and place it in the vendor partition to achieve the separation of the system and HAL. The upgrade of AOSP will not affect the HAL customized by manufacturers. Device manufacturers or chip suppliers no longer need to adapt to new versions of Android.
Due to historical reasons, many manufacturers are unwilling to migrate to the Treble architecture all at once, and it was not until Android 8.0 that the coexistence of multiple HALs emerged.
0x31 HAL
The Android kernel is based on Linux, and for traditional Unix-like operating systems, the operation of hardware is completed by the kernel. The Linux kernel uses the GPL protocol, and the kernel code needs to be publicly disclosed. However, Android divides the operation of hardware into HAL (Hardware Abstraction Layer) and kernel drivers, allowing hardware manufacturers to place their core algorithms or logic in the HAL layer, thereby protecting their interests.
The HAL module code is located /hardware/libhardware/modules/is located in the Android file system after compilation /vendor/lib/hw/. For example
# ls /vendor/lib/hw/ | grep drm
android.hardware.drm@1.0-impl.so
0x32 HAL type
AOSP provides the tool lshal, which can directly view the system HAL. HAL services can be implemented using Java or C++.
Binderized HAL (HIDL), that is, the introduction of the Treble architecture, Android versions after 8.0 must use this method. HAL and user calls are in different processes, HAL is written as binder service, and user interfaces such as frameworks as binder clients implement inter-process interface calls through the IPC mechanism. This is Google's ultimate design goal.
Compatibility mode, this mode is designed to be compatible with old versions of HAL, the implementation of old HAL is still provided in the form of a dynamic library, but the binder service links the dynamic library HAL implementation, that is, the binder service links the old HAL implementation through hw_get_module, and the user end indirectly interacts with the old HAL through IPC communication with the binder service.
Binderized mode
Not binderized
The traditional mode, before Android 8.0, the HAL of all previous versions was compiled into so and then dynamically linked to various framework services.
0x33 HIDL and Service
Previously, Framework and HAL were compiled together into system.img. The emergence of HIDL made Framework no longer directly call HAL, but instead call HAL modules indirectly through HIDL. HAL is compiled into a separate partition vendor.img. Each HAL module can correspond to a HIDL service. The Android framework layer creates HIDL services through HwBinder and obtains HAL modules through HIDL services. That is to say,Service is a means to implement HAL, which can expose HAL modules to the application layer for calls. Of course, it can also be used to call HAL directly through C client without going through Serivce..
0x40 Case analysis of DRM source code
The Android Digital Rights Management framework (DRM, Digital Rights Management) is a typical Service and HAL implementation solution. The DRM framework is extensible, and device manufacturers can implement their own license restriction management according to specific devices.
The figure above is the DRM framework before Android 11, for beginners, it is still very difficult to understand without combining the source code. DRM SERVER and MEDIA DRM SERVER are the implementations before and after Android 8.0. At the level of the Android file system, it is represented by two binary programs, which are loaded when the system starts up.
0x41 Traditional implementation
The DRM service list is as follows,drm.drmManagerIs the service name,drm.IDrmManagerServiceIs the service interface description, how do they get it? Below, we analyze how the service is registered from the code level.
DRM SERVER is a binary, the corresponding source code location /frameworks/av/drm/drmserver/, from the main function entry, analyze layer by layer, used for service registration.
DrmManagerServiceClass uses Binder's addService to register the service to the Service Manager
And DrmManagerServiceClass inherits from BnDrmManagerService, this class is in IDrmManagerServiceImplementation. IDrmManagerService implements the Service interface description,Methods available for remote invocationDefinition (implementation of onTransact method), client proxy.
The loadPlugIns function is implemented in libdrmframework.so, used for loading so
The directory below is the dynamic library of the HAL layer implemented by the manufacturer
0x42 Treble implementation
The introduction of HIDL in the Treble project brings innovation to the Android HAL architecture. Similar to the 'traditional implementation', the MEDIA DRM SERVER is also used for service registration, but the binary code of this Service is located at /frameworks/av/services/mediadrm/ ,Binder IPC proxy code is still located at the old location /frameworks/av/drm/libmediadrm/ .
The registration of Service has been analyzed in the previous text, here only a brief statement is made. The binary registered service of mediadrmserver is media.drm, IMediaDrmService.cppdefines interface description android.media.IMediaDrmService,and has implemented the onTransact method (There is a simple analysis in the Native Service chapter),no further elaboration.
# service call media.drm 1
Result: Parcel(
0x00000000: 73682a85 00000113 00000002 00000000 '.*hs............'
0x00000010: 00000000 00000000 '........
# Return a Crypto/CryptoHal object
The client can ultimately call the remote service end makeCryto/Hal through Binder, returning a CryptoHal object, this method is in /frameworks/av/drm/libmediadrm/CryptoHal.cppimplementation
Originally:CryptoEach method can be called, through Binder, the proxy is registered at ICrypto.cppinterface.
Now:CryptoHalEach method can be called.The purpose of this interface is to allow non-privileged applications to decrypt DRM data.
makeCryptoFactories -> ICryptoFactory -> ICryptoFactory.hal(Some methods are publicly exposed in the form of HIDL interfaces, allowing hardware manufacturers to implement the methods declared by HIDL themselves)
makeCryptoPlugin -> ICryptoPlugin -> ICryptoPlugin.hal
0x50 Service Attack Surface and Security Issues
Service, like other components, has many security issues.
0x51 Permission Bypass
Service also has permission control, not all applications have the permission to call any Service. The application layer Service uses the ordinary App's permission management, which can be directly scanned by AndroidManifest.xml. HoweverFor system services, there is currently no general method to directly find the permissions required to call a system service..
The AOSP native system service uses functions like android::checkPermission to judge the permissions of the caller process. Generally, there is no problem. For example, the media.extractor service indicates that the program needs the android.permission.DUMP permission to obtain system dump information from the system service.
However, some phone manufacturers or Android emulators often have their own business. Due to the lack of a unified permission control scheme for system services, they may not have done a good job of permission control when implementing their own services, leading to security issues such as permission bypass. For example
The aforementioned service itself does not have permission control, and the function sendDataToPc can be directly controlled by the caller. The service itself is designed to be used only by applications with System permissions, but appUid is not obtained from the system, but controlled by the service parameters.
0x52 Out-of-bounds Read
Let's take a look at an actual case: CVE-2018-9411, a vulnerability that affects multiple high-permission Android services. This vulnerability is related to the HIDL design. In the case of HIDL, an important shared memory object is hidl_memory. hidl_memory is a structure that can be used to transmit shared memory segments between processes.
Complex data structures will have their own definitions through the Binder transmission structure in HIDL (such as hidl_handle, hidl_string), while simple data structures like integers (uint64_t) are transmitted as is. However, some old dynamic libraries still use 32-bit data. For example, the mapping of hidl_memory objects ashmem
mem.size() is 64-bit, while the type of the length field in the mmap signature is size_t, which means its bit size matches the bit size of the process. There is no problem in 64-bit processes, everything is 64-bit. However, in 32-bit processes, its size is truncated to 32 bits, soOnly the low 32 bits will be usedTherefore, when the HAL module is compiled as 32-bit, applying hidl_memory exceeding UINT32_MAX (0xFFFFFFFF) will be truncated.
Details will not be described in detail. The vulnerable service that finally matched is MediaCasService, and this ordinary service has the permission to read and write TEE, which led to privilege escalation due to out-of-bounds read.
0x53 Denial of Service
Denial of service in application layer Service is a frequently discussed topic. In the era of mobile internet, it was prevalent, but now few people pay attention to it. In fact, denial of service in system Service is also a common problem. Service does not judge the parameters passed in by the remote caller, directly references the client object, and does not catch exceptions, leading to service crashes. For example, the following is a case of Service implementation. This method does not judge the input parameters and prints the result directly.
the system will generate the onTransact method according to the AIDL file, and the method here catches exceptions
but in realitysystem developersusually need to implement the onTransact method by themselves, and it is very likely that exceptions have not been caught. When the client calls getAge with the input "xiaoming", it leads to a null pointer dereference
Many Service vulnerabilities, such as CVE-2015-1526, can lead to denial of service.
0x54 serialization
Java provides Serializable, and Android provides Parcelable for object serialization. Denial of service caused by serialization is also a common problem in early application layer Services, and like the above problem, catching exceptions can solve it.
Serializable
Intent i = getIntent();
if(i.getAction().equals("serializable_action"))
{
i.getSerializableExtra("serializable_key"); // No exception handling done
}
Parcelable
this.b = (RouterConfig);
this.getIntent().getParcelableExtra("filed_router_config"); // This line causes a class cast exception crash
Similar to some common vulnerabilities at the application layer, serialization and deserialization also have other types of vulnerabilities, such as CVE-2015-3525, CVE-2014-7911, which can also be used for privilege escalation. Due to the age of the vulnerabilities, readers who are interested can look them up themselves.
0x55 buffer overflow
Historically, there have also been some Service vulnerabilities, a typical one being the Android Drm service stack overflow vulnerability (CVE-2017-13253). This vulnerability is quite complex and requires the reader to have a deep understanding of the Android Service architecture. If you want to delve deeper into Native Service, this is a good entry point for learning. Since we are in the "Case Analysis of DRM Source CodeThe chapter has already analyzed DRM, now looking back at the cause of this vulnerability is not so difficult. The key to analyzing this vulnerability is to clarify two points
Service and HIDL Data stream
ComplexData structure
Vulnerability point, the function CryptoPlugin::decrypt memcpy has overflow /frameworks/av/drm/mediadrm/plugins/clearkey/CryptoPlugin.cppHere, the focus is on the data stream, clarifying the call relationship between Service and HAL, the specific data structure, and the cause of overflow can be viewed in the reference literature.
Upper-level call /hardware/interfaces/drm/1.0/default/CryptoPlugin.cpp(Generated automatically by the HAL compilation tool)
/hardware/interfaces/drm/1.0/ICryptoPlugin.halBy the / of the Framework layerframeworks/av/drm/libmediadrm/ICrypto.cppCall
And ICrypto is exactly the Binder IPC Proxy implementation of the media.drm service that we analyzed before, which is externally controllable.
1. Are the names and interfaces of the Service one-to-one corresponding?
Most of the time, a Service may not have an interface, or at most one interface, but there are also cases where two Services share the same interface, for example android.hardware.ICamera
2. Does each Service have an interface name?
Some Native services do not have interface descriptions, for example media.extractor
In fact, you can also see it in the corresponding code implementation.
0x60 Fuzzing
For the Service implemented in Java (i.e., what we call Framework Service), we need to be concerned about whether the methods in ADIL or the specific implementation of xxx extends xxx.Stub have security issues. For the Service implemented in C (i.e., what we call Native Service), we need to be concerned about whether the onTransact method implemented by BnBinder has security issues. These are actually the entry functions of the Service.
where the entry function of the Fuzz engine is tansact(code, data, reply, flag)
code is of int type, specifying the service method number;
data is of the parcel type, and it is the data sent, which meets the binder protocol rules;
reply is also of the parcel type, and it is the data returned after communication is completed;
flag is the mark bit, 0 is normal RPC, which needs to wait, and the call is in a blocked state until the return is received, 1 is one-way RPC, indicating a transaction that does not require a reply, usually a one-way call without a return value.
data is the parameter sent by binder, and its composition is RPC header + parameter 1 + parameter 2 + ..., the RPC header is the so-called interface name (the interface description mentioned earlier). Up to this point, we have completed the preliminary knowledge of Fuzzing.
0x61 Service Call or Transact
adb provides tools that can directly call services and supports passing various parameters. This method is simple and rough, but the types of parameters that adb can pass are limited. For slightly complex types or some custom type parameters, the ability of adb to call services is restricted. Therefore, it is possible to write an APP to call system services, and the relevant methods have also been mentioned in the above chapter "Communication between Components and System Services", and here the methods are further formalized.
Get all system services
/**
* Get all service names in the system
* @return Service list
*/
public String[] ListService() {
String[] ServiceList = {};
try {
ServiceList = (String [])Class.forName("android.os.ServiceManager").getMethod("listServices").invoke(null);
// Log.d(TAG, "I find there are " + ServiceList.length + " System Services");
for (int i = 0; i < ServiceList.length; i++) {
// Log.d(TAG, ServiceList[i]);
}
} catch(Exception e) {
e.printStackTrace();
}
return ServiceList;
}
Get the IBinder interface object
/**
* Use reflection to get the IBinder interface of the service
* @param ServiceName Service name
* @return Return the IBinder interface of the service
* @throws Exception
*/
private static IBinder getIBinder(String ServiceName) throws Exception{
return (IBinder) Class.forName("android.os.ServiceManager").getMethod("getService", String.class).invoke(null, ServiceName);
}
Get the interface description or the interface name
/**
* Get the interface name
* @param serHandle The IBinder interface of the service
* @return Return the interface name string
* @throws RemoteException
*/
private static String getInterfaceName(IBinder IBinder) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
IBinder.transact(IBinder.INTERFACE_TRANSACTION, data, reply, 0);
String interfacename = reply.readString();
data.recycle();
reply.recycle();
return interfacename;
}
Call the relevant method of the system service
void test(String sername, int code) {
for (int i = 0; i < testcaseint.length; i++)
for (int j = 0; j < testcaseint.length; j++) {
try {
Log.d(tag, "-" + sername + "-" + code + "-arg1-" + testcaseint[i] + "-arg2-" + testcaseint[j]);
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
IBinder ib = getIBinder(sername);
String in = getInterfaceName(ib);
data.writeInterfaceToken(in);
data.writeInt(testcaseint[i]);
data.writeInt(testcaseint[j]);
ib.transact(code, data, reply, 0);
Log.d(tag, "-" + sername + "-" + code + "-" + "reply is \n" + reply.readString());
reply.readException();
data.recycle();
reply.recycle();
} catch (Exception e) {
}
}
}
The limitation of traditional Service Fuzzing lies in the fact that it is impossible to predict the parameter types of the target service methods in advance, although we can find them in the source code, but this requires refilling the parameter types for each method individually; at the same time, it is also difficult to construct for multi-level interfaces.
0x62 Native Service Fuzz
FANSIt is an open-source tool for Android Service Fuzzing published by Liu Baozheng of Tsinghua University in USENIX Security'20. It solves the problem of how to automatically construct interface model test cases in traditional Fuzzing. Its most outstanding contribution is that it automatically extracts the interface model from the abstract syntax tree (AST) of the target interface, in short, it can automatically infer the parameter types of the target method.
The Interface Collector collects all interfaces in the target service, including top-level interfaces and multi-level interfaces. Then, the Interface Model Extractor extracts the input and output formats, as well as variable semantics, such as variable names and types, of each candidate transaction in each collected interface. The extractor also collects definitions of structures, enumerations, and type aliases related to variables. Next, the Dependency Inference Engine infers interface dependencies, as well as variable dependencies within and between transactions. Finally, based on the above information, the Fuzzing Engine randomly generates transactions and calls the corresponding interfaces to fuzz the local system service. The Fuzzing Engine also has a manager responsible for synchronizing data between the host and the phone being tested.
The disadvantage of FANS is that there must be a source code compilation environment, and it may be necessary to adapt to different manufacturers.
Summary
As one of the core components of Android, Binder provides a unique inter-process communication method for Android. Android Service is the best embodiment of Binder. Through Service, we can better understand Binder. This article systematically introduces the classification, principle, and usage methods of Android Service, then analyzes the common security issues combined with practical security vulnerabilities, and finally gives the corresponding Fuzzing model.
References
The completion of this article is also due to the excellent works of various bigwigs on the Internet. Some text descriptions and some architectural diagrams come from the following articles, and I would like to express my gratitude again! Some architectural diagrams are drawn by the author, and the source code screenshots in the text are taken by the author on Android XRef8.0/9.0 completed, the command line screenshot is the result of executing on a OnePlus 3 (Android 8.0.0) phone. There are numerous articles or books about the Android system architecture, if you want to learn more, read the following blogs, and you will be surprised by the unexpected gains!
Jianshu - Four major components: service
gityuan - Series of Binder - Opening
Personal Blog - Deep understanding of Android: Binder
Personal Poet - Understanding Android Binder mechanism (Part 1/2/3)
Blog Garden - Example of Android native service
zimperium - CVE-2017-13253: Buffer overflow in multiple Android DRM services
zimperium - CVE-2018-9411: New critical vulnerability in multiple high-privileged Android services
Xianzhi Community - CVE-2017-13253: Stack overflow vulnerability in Android Drm service
Android developers - Overview of Services
Android developers - Android Interface Definition Language (AIDL)
Android source - Overview of HIDL
Android source - Overview of AIDL
CSDN - A detailed explanation of Android HIDL HAL interface definition language
Android source - DRM
Zhihu - An analysis of the Binder principle for Android application engineers

评论已关闭