赞
踩
小米8 利用IBinder transact获取服务的接口名字,结果出现以下异常:
- W/System.err: java.lang.SecurityException
- W/System.err: at android.os.BinderProxy.transactNative(Native Method)
- W/System.err: at android.os.BinderProxy.transact(BinderProxy.java:482)
- W/System.err: at com.gamesec.essential.Essential.getInterfaceName(Essential.java:182)
- W/System.err: at com.gamesec.essential.Essential.getRunningServiceInfo(Essential.java:204)
- W/System.err: at com.gamesec.essential.Essential.getEssential(Essential.java:134)
- W/System.err: at com.gamesec.DataCollector.collectDeviceInfo(DataCollector.java:373)
- W/System.err: at com.gamesec.DataCollectorThread.run(DataCollectorThread.java:12)
手机系统为android9.0,所以查看源码 /.core/jni/android_util_Binder.cpp文件
android_os_BinderProxy_transact
[ android_util_Binder.cpp]
- static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
- {
- if (dataObj == NULL) {
- jniThrowNullPointerException(env, NULL);
- return JNI_FALSE;
- }
- Parcel* data = parcelForJavaObject(env, dataObj);
- if (data == NULL) {
- return JNI_FALSE;
- }
- Parcel* reply = parcelForJavaObject(env, replyObj);
- if (reply == NULL && replyObj != NULL) {
- return JNI_FALSE;
- }
- IBinder* target = getBPNativeData(env, obj)->mObject.get();
- if (target == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
- return JNI_FALSE;
- }
- ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
- target, obj, code);
- bool time_binder_calls;
- int64_t start_millis;
- if (kEnableBinderSample) {
- // Only log the binder call duration for things on the Java-level main thread.
- // But if we don't
- time_binder_calls = should_time_binder_calls();
- if (time_binder_calls) {
- start_millis = uptimeMillis();
- }
- }
- //printf("Transact from Java code to %p sending: ", target); data->print();
- status_t err = target->transact(code, *data, reply, flags);
- //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
- if (kEnableBinderSample) {
- if (time_binder_calls) {
- conditionally_log_binder_call(start_millis, target, code);
- }
- }
- if (err == NO_ERROR) {
- return JNI_TRUE;
- } else if (err == UNKNOWN_TRANSACTION) {
- return JNI_FALSE;
- }
- signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
- return JNI_FALSE;
- }

这里会有异常抛出:
- void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, bool canThrowRemoteException, int parcelSize) {
- switch (err) {
- case UNKNOWN_ERROR:
- jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
- break;
- case NO_MEMORY:
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- break;
- case INVALID_OPERATION:
- jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
- break;
- case BAD_VALUE:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- break;
- case BAD_INDEX:
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
- break;
- case BAD_TYPE:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- break;
- case NAME_NOT_FOUND:
- jniThrowException(env, "java/util/NoSuchElementException", NULL);
- break;
- case PERMISSION_DENIED:
- jniThrowException(env, "java/lang/SecurityException", NULL);
- break;
- case NOT_ENOUGH_DATA:
- jniThrowException(env, "android/os/ParcelFormatException", "Not enough data");
- break;
- case NO_INIT:
- jniThrowException(env, "java/lang/RuntimeException", "Not initialized");
- break;
- case ALREADY_EXISTS:
- jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
- break;
- case DEAD_OBJECT:
- jniThrowException(env, canThrowRemoteException
- ? "android/os/DeadObjectException"
- : "java/lang/RuntimeException", NULL);
- break;
- case UNKNOWN_TRANSACTION:
- jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
- break;
- case FAILED_TRANSACTION: {
- ALOGE("!!! FAILED BINDER TRANSACTION !!! (parcel size = %d)", parcelSize);
- const char* exceptionToThrow;
- char msg[128];
- //transaction失败的底层原因有可能很多种,这里无法确定是那种,后续binder driver会进一步完善
- if (canThrowRemoteException && parcelSize > 200*1024) {
- exceptionToThrow = "android/os/TransactionTooLargeException";
- snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
- } else {
- exceptionToThrow = (canThrowRemoteException)
- ? "android/os/DeadObjectException"
- : "java/lang/RuntimeException";
- snprintf(msg, sizeof(msg)-1,
- "Transaction failed on small parcel; remote process probably died");
- }
- jniThrowException(env, exceptionToThrow, msg);
- } break;
- case FDS_NOT_ALLOWED:
- jniThrowException(env, "java/lang/RuntimeException",
- "Not allowed to write file descriptors here");
- break;
- case UNEXPECTED_NULL:
- jniThrowNullPointerException(env, NULL);
- break;
- case -EBADF:
- jniThrowException(env, "java/lang/RuntimeException",
- "Bad file descriptor");
- break;
- case -ENFILE:
- jniThrowException(env, "java/lang/RuntimeException",
- "File table overflow");
- break;
- case -EMFILE:
- jniThrowException(env, "java/lang/RuntimeException",
- "Too many open files");
- break;
- case -EFBIG:
- jniThrowException(env, "java/lang/RuntimeException",
- "File too large");
- break;
- case -ENOSPC:
- jniThrowException(env, "java/lang/RuntimeException",
- "No space left on device");
- break;
- case -ESPIPE:
- jniThrowException(env, "java/lang/RuntimeException",
- "Illegal seek");
- break;
- case -EROFS:
- jniThrowException(env, "java/lang/RuntimeException",
- "Read-only file system");
- break;
- case -EMLINK:
- jniThrowException(env, "java/lang/RuntimeException",
- "Too many links");
- break;
- default:
- ALOGE("Unknown binder error code. 0x%" PRIx32, err);
- String8 msg;
- msg.appendFormat("Unknown binder error code. 0x%" PRIx32, err);
- jniThrowException(env, canThrowRemoteException
- ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
- break;
- }
- }

- struct BinderProxyNativeData {
- // Both fields are constant and not null once javaObjectForIBinder returns this as
- // part of a BinderProxy.
- // The native IBinder proxied by this BinderProxy.
- sp<IBinder> mObject;
- // Death recipients for mObject. Reference counted only because DeathRecipients
- // hold a weak reference that can be temporarily promoted.
- sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
- };
-
- ...
-
- BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
- return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
- }
-
- ...
-
- IBinder* target = getBPNativeData(env, obj)->mObject.get();
- status_t err = target->transact(code, *data, reply, flags);

从上述代码可以看到,返回通过IBinder->transact接口返回err结果,IBinder->transact的接口可继续查看:BpBinder.transact和IPC.transact里面的实现,关于BpBinder.transact和IPC.transact的详细信息下面继续介绍:
BpBinder.transact
[ BpBinder.cpp]
- status_t BpBinder::transact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
- {
- if (mAlive) {
- //[见小节2.6]
- status_t status = IPCThreadState::self()->transact(
- mHandle, code, data, reply, flags);
- if (status == DEAD_OBJECT) mAlive = 0;
- return status;
- }
- return DEAD_OBJECT;
- }
当binder死亡,则返回err=DEAD_OBJECT,所对应的抛出的异常为DeadObjectException
IPC.transact
[IPCThreadState.cpp]
- status_t IPCThreadState::transact(int32_t handle,
- uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags)
- {
- status_t err = data.errorCheck(); //错误检查
- flags |= TF_ACCEPT_FDS;
-
- if (err == NO_ERROR) {
- err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
- }
-
- if (err != NO_ERROR) {
- if (reply) reply->setError(err);
- return (mLastError = err); //返回writeTransactionData的执行结果err
- }
-
- if ((flags & TF_ONE_WAY) == 0) {
- if (reply) {
- err = waitForResponse(reply); //
- } else {
- Parcel fakeReply;
- err = waitForResponse(&fakeReply);
- }
- } else {
- err = waitForResponse(NULL, NULL);
- }
- return err; //返回waitForResponse的执行结果err
- }

返回值err的来源:
IPC.waitForResponse
[IPCThreadState.cpp]
- status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
- {
- uint32_t cmd;
- int32_t err;
-
- while (1) {
- // 向Binder驱动写入交互
- if ((err=talkWithDriver()) < NO_ERROR) break;
- err = mIn.errorCheck();
- ...
- switch (cmd) {
-
- case BR_DEAD_REPLY:
- err = DEAD_OBJECT;
- goto finish;
-
- case BR_FAILED_REPLY:
- err = FAILED_TRANSACTION;
- goto finish;
-
- default:
- err = executeCommand(cmd); //[见小节2.7.1]
- if (err != NO_ERROR) goto finish;
- break;
- }
- }
- ...
- return err;
- }

当收到BR_DEAD_REPLY,则抛出err=DEAD_OBJECT
当收到BR_FAILED_REPLY, 则抛出err=FAILED_TRANSACTION
否则返回的是executeCommand的err
IPC.executeCommand
- status_t IPCThreadState::executeCommand(int32_t cmd)
- {
- BBinder* obj;
- RefBase::weakref_type* refs;
- status_t result = NO_ERROR;
-
- switch ((uint32_t)cmd) {
- case BR_ERROR:
- //从mIn中读取出错误码
- result = mIn.readInt32();
- break;
- ...
-
- default:
- result = UNKNOWN_ERROR;
- break;
- }
- return result;
- }

talkWithDriver过程便会跟Binder驱动交互
当服务端收到bind请求,则此时进入execTransact()过程。
Binder.execTransact
[Binder.java]
- private boolean execTransact(int code, long dataObj, long replyObj, int flags) {
- Parcel data = Parcel.obtain(dataObj);
- Parcel reply = Parcel.obtain(replyObj);
-
- boolean res;
- try {
- //执行onTransact方法[见小节3.1.1]
- res = onTransact(code, data, reply, flags);
- } catch (RemoteException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
- Log.w(TAG, "Binder call failed.", e);
- } else {
- reply.setDataPosition(0);
- reply.writeException(e); //[见后续]
- }
- res = true;
- } catch (RuntimeException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
- Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
- } else {
- reply.setDataPosition(0);
- reply.writeException(e); //[见后续]
- }
- res = true;
- } catch (OutOfMemoryError e) {
- Log.e(TAG, "Caught an OutOfMemoryError from the binder stub implementation.", e);
- RuntimeException re = new RuntimeException("Out of memory", e);
- reply.setDataPosition(0);
- reply.writeException(re); //[见后续]
- res = true;
- }
- //[见小节3.3]
- checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- reply.recycle();
- data.recycle();
- return res;
- }

可以看到服务端发送异常有3大类:
还有一类见writeException
writeException
- public final void writeException(Exception e) {
- int code = 0;
- if (e instanceof SecurityException) {
- code = EX_SECURITY;
- } else if (e instanceof BadParcelableException) {
- code = EX_BAD_PARCELABLE;
- } else if (e instanceof IllegalArgumentException) {
- code = EX_ILLEGAL_ARGUMENT;
- } else if (e instanceof NullPointerException) {
- code = EX_NULL_POINTER;
- } else if (e instanceof IllegalStateException) {
- code = EX_ILLEGAL_STATE;
- } else if (e instanceof NetworkOnMainThreadException) {
- code = EX_NETWORK_MAIN_THREAD;
- } else if (e instanceof UnsupportedOperationException) {
- code = EX_UNSUPPORTED_OPERATION;
- }
- writeInt(code); //写入异常码
- StrictMode.clearGatheredViolations();
- if (code == 0) {
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- throw new RuntimeException(e);
- }
- writeString(e.getMessage());
- }

此处写入的异常类型:
checkParcel
- static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
- // 检查parcel数据是否大于800KB
- if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
- StringBuilder sb = new StringBuilder();
- sb.append(msg);
- sb.append(": on ");
- sb.append(obj);
- sb.append(" calling ");
- sb.append(code);
- sb.append(" size ");
- sb.append(parcel.dataSize());
- sb.append(" (data: ");
- parcel.setDataPosition(0);
- sb.append(parcel.readInt());
- sb.append(", ");
- sb.append(parcel.readInt());
- sb.append(", ");
- sb.append(parcel.readInt());
- sb.append(")");
- Slog.wtfStack(TAG, sb.toString());
- }
- }

总结异常数据传输流程如下图:
结论:小米手机发出安全异常是binder服务端发出的,厂商设置了权限无法访问。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。