Android RIL и шифрование телефонных звонков?
Сейчас я тестирую что-то в телефонной Android-телефонии, шифрование У меня есть Hook метод в RIL.java с помощью фреймворка Xposed
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
Но эти байты не могут быть преобразованы в значимую строку, что означают эти байты []? (возможно, содержит некоторые 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);
}
}
1 ответ
Поэтому, поработав некоторое время с RIL, я могу ответить на вторую часть вашего поста.
Сообщения RIL могут быть двух типов: SOLLICITED и UNSOLLICITED.
Как они обрабатываются, можно увидеть в классе Java RIL из источников Android RIL.java.
Но я кратко объясню их структуру и как они будут проанализированы:
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 заставляет wakelock ожидать ответа для DEFAULT_WAKE_LOCK_TIMEOUT перед тем, как разблокировать wakelock (даже если он все равно обработает ответ, когда он в итоге получит его).
SOLLICITED ответы обрабатываются немного по-другому, при отправке запроса android получает глобальный серийный номер для запроса ( как показано здесь) и сохраняет его во внутренний список. Когда он получает ответ, он находит ответ, используя серийный номер ( как показано здесь).
Пример запрашиваемого сообщения:
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. Экспоузд-RIL-обертка