IBinder獲取手機服務信息異常

 

小米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;
}

這裏會有異常拋出:

  1. 當拋出異常NullPointerException: 代表dataObj爲空,意味着java層傳遞下來的parcel data數據爲空
  2. 當拋出異常IllegalStateException: 代表BpBinder爲空,意味着Native層的BpBinder已經被釋放
  3. 當進入signalExceptionForError(): 根據transact執行具體情況拋出相應的異常,具體看signalExceptionForError內部實現
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的來源:

  1. 返回err=DEAD_OBJECT
  2. 返回writeTransactionData的執行結果err
  3. 返回waitForResponse的執行結果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大類:

  1. RemoteException
  2. RuntimeException
  3. OutOfMemoryError

還有一類見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());
}

此處寫入的異常類型:

  1. NullPointerException
  2. SecurityException
  3. BadParcelableException
  4. IllegalArgumentException
  5. IllegalStateException
  6. NetworkOnMainThreadException
  7. UnsupportedOperationException

 

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服務端發出的,廠商設置了權限無法訪問。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章