现在,我正在测试Android电话加密中的某些功能。
我通过使用Xposed框架在RIL.java中挂钩了一些方法
if ( lpparam.packageName.contains("com.android.phone")){
XposedBridge.log("damowang Loaded app: " + lpparam.packageName);
findAndHookMethod("com.android.internal.telephony.RIL", lpparam.classLoader, "readRilMessage",InputStream.class,byte[].class,new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
int messageLenth = Integer.parseInt(param.getResult().toString());
XposedBridge.log("damowang : RILSender readRilMessage result==="+messageLenth);
byte[] arr = (byte[])param.args[1];
String byteStr = "";
for(int i=0;i<messageLenth;i++){
byteStr += arr[i];
byteStr += " ";
}
XposedBridge.log("damowang : RILSender readRilMessage byte[]==="+byteStr);
}
});
findAndHookMethod("com.android.internal.telephony.RIL", lpparam.classLoader, "invokeOemRilRequestRaw",byte[].class,Message.class,new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("damowang : RIL invokeOemRilRequestRaw result===");
byte[] arr = (byte[])param.args[0];
String byteStr = "";
for(int i=0;i<arr.length;i++){
byteStr += arr[i];
byteStr += " ";
}
XposedBridge.log("damowang : RIL invokeOemRilRequestRaw byte[]==="+arr.length+"==="+"==="+byteStr);
}
});
}
然后我得到一些日志,例如:
damowang : RIL invokeOemRilRequestRaw byte[]===5======11 24 0 5 1
damowang : RIL invokeOemRilRequestRaw byte[]===5======11 24 0 5 0
damowang : RILSender readRilMessage result===12
damowang : RILSender readRilMessage byte[]===0 0 0 0 -95 0 0 0 17 0 0 0
damowang : RILSender readRilMessage result===12
damowang : RILSender readRilMessage byte[]===0 0 0 0 -94 0 0 0 17 0 0 0
damowang : RILSender readRilMessage result===8
damowang : RILSender readRilMessage byte[]===1 0 0 0 -22 3 0 0
damowang : RILSender readRilMessage result===60
damowang : RILSender readRilMessage byte[]===1 0 0 0 -15 3 0 0 16 0 0 0 99 4 0 0 16 0 0 0 -56 0 0 0 16 0 0 0 -56 0 0 0 -1 -1 -1 -1 99 0 0 0 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127
但是这些字节不能强制转换为有意义的String,这些byte []是什么意思? (也许包含一些AT命令?)
如何使用Xposed钩住RIL.java中的发送者和接收者线程?
class RILSender extends Handler implements Runnable {
public RILSender(Looper looper) {
super(looper);
}
// Only allocated once
byte[] dataLength = new byte[4];
//***** Runnable implementation
public void
run() {
//setup if needed
}
//***** Handler implementation
@Override public void
handleMessage(Message msg) {
RILRequest rr = (RILRequest)(msg.obj);
RILRequest req = null;
switch (msg.what) {
case EVENT_SEND:
/**
* mRequestMessagePending++ already happened for every
* EVENT_SEND, thus we must make sure
* mRequestMessagePending-- happens once and only once
*/
boolean alreadySubtracted = false;
try {
LocalSocket s;
s = mSocket;
if (s == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
return;
}
synchronized (mRequestList) {
mRequestList.add(rr);
mRequestMessagesWaiting++;
}
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
byte[] data;
data = rr.mp.marshall();
rr.mp.recycle();
rr.mp = null;
if (data.length > RIL_MAX_COMMAND_BYTES) {
throw new RuntimeException(
"Parcel larger than max bytes allowed! "
+ data.length);
}
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
//Rlog.v(LOG_TAG, "writing packet: " + data.length + " bytes");
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
} catch (IOException ex) {
Rlog.e(LOG_TAG, "IOException", ex);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
}
} catch (RuntimeException exc) {
Rlog.e(LOG_TAG, "Uncaught exception ", exc);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(GENERIC_FAILURE, null);
rr.release();
}
} finally {
// Note: We are "Done" only if there are no outstanding
// requests or replies. Thus this code path will only release
// the wake lock on errors.
releaseWakeLockIfDone();
}
if (!alreadySubtracted && mRequestMessagesPending > 0) {
mRequestMessagesPending--;
}
break;
case EVENT_WAKE_LOCK_TIMEOUT:
// Haven't heard back from the last request. Assume we're
// not getting a response and release the wake lock.
synchronized (mWakeLock) {
if (mWakeLock.isHeld()) {
// The timer of WAKE_LOCK_TIMEOUT is reset with each
// new send request. So when WAKE_LOCK_TIMEOUT occurs
// all requests in mRequestList already waited at
// least DEFAULT_WAKE_LOCK_TIMEOUT but no response.
// Reset mRequestMessagesWaiting to enable
// releaseWakeLockIfDone().
//
// Note: Keep mRequestList so that delayed response
// can still be handled when response finally comes.
if (mRequestMessagesWaiting != 0) {
Rlog.d(LOG_TAG, "NOTE: mReqWaiting is NOT 0 but"
+ mRequestMessagesWaiting + " at TIMEOUT, reset!"
+ " There still msg waitng for response");
mRequestMessagesWaiting = 0;
if (RILJ_LOGD) {
synchronized (mRequestList) {
int count = mRequestList.size();
Rlog.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " +
" mRequestList=" + count);
for (int i = 0; i < count; i++) {
rr = mRequestList.get(i);
Rlog.d(LOG_TAG, i + ": [" + rr.mSerial + "] "
+ requestToString(rr.mRequest));
}
}
}
}
// mRequestMessagesPending shows how many
// requests are waiting to be sent (and before
// to be added in request list) since star the
// WAKE_LOCK_TIMEOUT timer. Since WAKE_LOCK_TIMEOUT
// is the expected time to get response, all requests
// should already sent out (i.e.
// mRequestMessagesPending is 0 )while TIMEOUT occurs.
if (mRequestMessagesPending != 0) {
Rlog.e(LOG_TAG, "ERROR: mReqPending is NOT 0 but"
+ mRequestMessagesPending + " at TIMEOUT, reset!");
mRequestMessagesPending = 0;
}
mWakeLock.release();
}
}
break;
}
}
}
class RILReceiver implements Runnable {
byte[] buffer;
RILReceiver() {
buffer = new byte[RIL_MAX_COMMAND_BYTES];
}
public void
run() {
int retryCount = 0;
try {for (;;) {
LocalSocket s = null;
LocalSocketAddress l;
try {
s = new LocalSocket();
l = new LocalSocketAddress(SOCKET_NAME_RIL,
LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
} catch (IOException ex){
try {
if (s != null) {
s.close();
}
} catch (IOException ex2) {
//ignore failure to close after failure to connect
}
// don't print an error message after the the first time
// or after the 8th time
if (retryCount == 8) {
Rlog.e (LOG_TAG,
"Couldn't find '" + SOCKET_NAME_RIL
+ "' socket after " + retryCount
+ " times, continuing to retry silently");
} else if (retryCount > 0 && retryCount < 8) {
Rlog.i (LOG_TAG,
"Couldn't find '" + SOCKET_NAME_RIL
+ "' socket; retrying after timeout");
}
try {
Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
} catch (InterruptedException er) {
}
retryCount++;
continue;
}
retryCount = 0;
mSocket = s;
Rlog.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket");
int length = 0;
try {
InputStream is = mSocket.getInputStream();
for (;;) {
Parcel p;
length = readRilMessage(is, buffer);
if (length < 0) {
// End-of-stream reached
break;
}
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
//Rlog.v(LOG_TAG, "Read packet: " + length + " bytes");
processResponse(p);
p.recycle();
}
} catch (java.io.IOException ex) {
Rlog.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed",
ex);
} catch (Throwable tr) {
Rlog.e(LOG_TAG, "Uncaught exception read length=" + length +
"Exception:" + tr.toString());
}
Rlog.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL
+ "' socket");
setRadioState (RadioState.RADIO_UNAVAILABLE);
try {
mSocket.close();
} catch (IOException ex) {
}
mSocket = null;
RILRequest.resetSerial();
// Clear request list on close
clearRequestList(RADIO_NOT_AVAILABLE, false);
}} catch (Throwable tr) {
Rlog.e(LOG_TAG,"Uncaught exception", tr);
}
/* We're disconnected so we don't know the ril version */
notifyRegistrantsRilConnectionChanged(-1);
}
}
最佳答案
因此,在使用RIL挂钩一段时间后,我可以回答您帖子的第二部分。
RIL消息可以为两种类型:SOLLICITED和UNSOLLICITED。
可以从android源RIL.java的java RIL类中看到如何处理这些问题
但是我将简要解释它们的结构以及如何解析它们:
UNSOLICITED消息是RIL自己发送的消息,通常是与无线电有关的事件。
一条不亲和的消息的示例是:
1 0 0 0 -15 3 0 0 15 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 99 0 0 0 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127 -1 -1 -1 127
结构很简单:
[type uint32 little endian][response uint32 little endian][data]
所以
1 0 0 0 = 1 is the type of the request, which means unsollicited message( notice the endianess )
-15 3 0 0 = 1009 is the response , which matches RIL_UNSOL_SIGNAL_STRENGTH
[15 0 . .]= data structure regarding signal strenght ( in this case an array of strings )
响应常量可从源中的RILConstants.java类中找到。
SOLLICITED消息是对android发送到RIL层的请求的响应。这些都是异步完成的,Android会在释放唤醒锁之前保留一个唤醒锁,等待DEFAULT_WAKE_LOCK_TIMEOUT的响应(即使艰难,它最终仍会得到响应时仍会处理该响应)。
SOLLICITED响应的处理方式有所不同,当发送请求时,android获取请求的全局序列号(as seen here),并将其保存到内部列表中。收到响应后,它将使用序列号(as seen here)查找响应。
提示信息的示例是:
0 0 0 0 -94 0 0 0 17 0 0 0
结构如下:
[type uint32 little endian][serial uint32 little endian][error uint32 little endian][data]
所以
0 0 0 0 = 0 is the type, UNSOLICITED
-94 0 0 0 = 162 is the serial
17 0 0 0 = 17 is the error ( NO_SUCH_ELEMENT )
有关错误的常量,请查看CommandException.java
如果您想解析SOLLICITED响应,则必须挂钩RILRequest.obtain才能保存mSerial和mRequest的键值副本,以便能够解析它(否则您无法知道请求)
如果您想了解整个RIL层的更多信息,可以使用一张精美的幻灯片,其中涵盖了android-radio-layer-interface的几乎所有内容。
同样,无耻的插件,但是如果您愿意,可以看一个示例模块,我基本上只是记录RIL消息。 xposed-ril-wrapper