Reputation: 21
Now I'm testing something in Android phone calls encryption. I have Hook some method in RIL.java by using Xposed framework
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);
}
});
}
Then I got some Logs like:
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
But these bytes can not by cast to meaningful String , what does these byte[] mean? (maybe contains some AT command?) how can I use Xposed to Hook the sender and receiver threads in 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);
}
}
Upvotes: 2
Views: 1554
Reputation: 1996
So after working with RIL hooking for a while, I can answer the second part of your post.
RIL messages can be of two types, SOLLICITED and UNSOLLICITED.
How these get handled can be seen in the java RIL class from the android sources RIL.java
But I'll explain briefly their structure and how they get parsed:
UNSOLICITED messages are messages that RIL sends on its own and are usually events related to the radio. An example of an unsollicited message is:
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
The structure is simple:
[type uint32 little endian][response uint32 little endian][data]
so
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 )
Response constants can be found in the RILConstants.java class from the sources.
SOLLICITED messages are responses to requests that android sends to the RIL layer. These are done asynchronously and android keeps a wakelock waiting for a response for DEFAULT_WAKE_LOCK_TIMEOUT before releasing the wakelock ( even tough it will still process the response when it will eventually get it ).
SOLLICITED responses get handled a bit differently, when sending a request android gets a global serial for the request ( as seen here ), and saves it into an internal list. When it receives a response, it finds the response using the serial ( as seen here ).
An example of a sollicited message is:
0 0 0 0 -94 0 0 0 17 0 0 0
The structure is as follows:
[type uint32 little endian][serial uint32 little endian][error uint32 little endian][data]
so
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 )
For the constants regarding error you should take a look at CommandException.java
If you want to parse the SOLLICITED responses you must hook RILRequest.obtain to be able to save a key-value copy of mSerial and mRequest to be able to parse it ( otherwise you CAN'T know the request )
If you want to know more about the entire RIL layer there is an exaustive slide that covers pretty much everything android-radio-layer-interface.
Also, shameless plug, but if you want you can take a look at an example module I did that basically just logs RIL messages. xposed-ril-wrapper
Upvotes: 3