public void handleMessage(Message msg) { CatLog.d(this, "handleMessage[" + msg.what + "]"); switch (msg.what) { case MSG_ID_SESSION_END: case MSG_ID_PROACTIVE_COMMAND: case MSG_ID_EVENT_NOTIFY: case MSG_ID_REFRESH: CatLog.d(this, "ril message arrived,slotid:" + mSlotId); String data = null; if (msg.obj != null) { AsyncResult ar = (AsyncResult) msg.obj; if (ar != null && ar.result != null) { try { data = (String) ar.result; } catch (ClassCastException e) { break; } } } mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); break; case MSG_ID_CALL_SETUP: mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null)); break; case MSG_ID_ICC_RECORDS_LOADED: break; case MSG_ID_RIL_MSG_DECODED: handleRilMsg((RilMessage) msg.obj); break; case MSG_ID_RESPONSE: handleCmdResponse((CatResponseMessage) msg.obj); break;
MSG_ID_RIL_MSG_DECODED
. private void handleRilMsg(RilMessage rilMsg) { if (rilMsg == null) { return; } // dispatch messages CommandParams cmdParams = null; switch (rilMsg.mId) { case MSG_ID_EVENT_NOTIFY: if (rilMsg.mResCode == ResultCode.OK) { cmdParams = (CommandParams) rilMsg.mData; if (cmdParams != null) { handleCommand(cmdParams, false); } } break; case MSG_ID_PROACTIVE_COMMAND: try { cmdParams = (CommandParams) rilMsg.mData; } catch (ClassCastException e) { // for error handling : cast exception CatLog.d(this, "Fail to parse proactive command"); // Don't send Terminal Resp if command detail is not available if (mCurrntCmd != null) { sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD, false, 0x00, null); } break; } if (cmdParams != null) { if (rilMsg.mResCode == ResultCode.OK) { handleCommand(cmdParams, true); } else { // for proactive commands that couldn't be decoded // successfully respond with the code generated by the // message decoder. sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode, false, 0, null); } } break;
handleCommand()
method, but the second parameter is different in each case:MSG_ID_EVENT_NOTIFY
- a normal notification that does not require a response from the user;MSG_ID_PROACTIVE_COMMAND
- and this, on the contrary, requires.handleCommand
: /** * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command * from RIL. * Sends valid proactive command data to the application using intents. * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is * from RIL_UNSOL_STK_PROACTIVE_COMMAND. */ private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { CatLog.d(this, cmdParams.getCommandType().name()); CharSequence message; CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); switch (cmdParams.getCommandType()) { case SET_UP_MENU: if (removeMenu(cmdMsg.getMenu())) { mMenuCmd = null; } else { mMenuCmd = cmdMsg; } sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); break; case DISPLAY_TEXT: break; case REFRESH: // ME side only handles refresh commands which meant to remove IDLE // MODE TEXT. cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value(); break; case SET_UP_IDLE_MODE_TEXT: sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); break; case SET_UP_EVENT_LIST: if (isSupportedSetupEventCommand(cmdMsg)) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } else { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, false, 0, null); } break; case PROVIDE_LOCAL_INFORMATION: ResponseData resp; switch (cmdParams.mCmdDet.commandQualifier) { case CommandParamsFactory.DTTZ_SETTING: resp = new DTTZResponseData(null); sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); break; case CommandParamsFactory.LANGUAGE_SETTING: resp = new LanguageResponseData(Locale.getDefault().getLanguage()); sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); break; default: sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } // No need to start STK app here. return; case LAUNCH_BROWSER: if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null) && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.launchBrowserDefault); ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString(); } break; case SELECT_ITEM: case GET_INPUT: case GET_INKEY: break; case SEND_DTMF: case SEND_SMS: case SEND_SS: case SEND_USSD: if ((((DisplayTextParams)cmdParams).mTextMsg.text != null) && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.sending); ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString(); } break; case PLAY_TONE: break; case SET_UP_CALL: if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null) && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.SetupCallDefault); ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString(); } break; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: BIPClientParams cmd = (BIPClientParams) cmdParams; /* Per 3GPP specification 102.223, * if the alpha identifier is not provided by the UICC, * the terminal MAY give information to the user * noAlphaUsrCnf defines if you need to show user confirmation or not */ boolean noAlphaUsrCnf = false; try { noAlphaUsrCnf = mContext.getResources().getBoolean( com.android.internal.R.bool.config_stkNoAlphaUsrCnf); } catch (NotFoundException e) { noAlphaUsrCnf = false; } if ((cmd.mTextMsg.text == null) && (cmd.mHasAlphaId || noAlphaUsrCnf)) { CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id"); // If alpha length is zero, we just respond with OK. if (isProactiveCmd) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } else if (cmdParams.getCommandType() == CommandType.OPEN_CHANNEL) { mCmdIf.handleCallSetupRequestFromSim(true, null); } return; } // Respond with permanent failure to avoid retry if STK app is not present. if (!mStkAppInstalled) { CatLog.d(this, "No STK application found."); if (isProactiveCmd) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, false, 0, null); return; } } /* * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by * either PROACTIVE_COMMAND or EVENT_NOTIFY. * If PROACTIVE_COMMAND is used for those commands, send terminal * response here. */ if (isProactiveCmd && ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) || (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) || (cmdParams.getCommandType() == CommandType.SEND_DATA))) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } break; default: CatLog.d(this, "Unsupported command"); return; } mCurrntCmd = cmdMsg; broadcastCatCmdIntent(cmdMsg); }
broadcastCatCmdIntent()
: private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) { Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); intent.putExtra("STK CMD", cmdMsg); intent.putExtra("SLOT_ID", mSlotId); CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId); mContext.sendBroadcast(intent); }
AppInterface.CAT_CMD_ACTION
equals android.intent.action.stk.command
;SLOT_ID
used for devices with multiple SIM cards;STK CMD
- command as a Parcelable
object.Parcelable
object in bytes. When converting Hex to ASCII, you will receive a SIM card message. <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" package="com.android.stk" android:sharedUserId="android.uid.phone"> <original-package android:name="com.android.stk" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.GET_TASKS"/> <application android:icon="@drawable/ic_launcher_sim_toolkit" android:label="@string/app_name" android:clearTaskOnLaunch="true" android:process="com.android.phone" android:taskAffinity="android.task.stk"> ... <receiver android:name="com.android.stk.StkCmdReceiver"> <intent-filter> <action android:name= "android.intent.action.stk.command" /> <action android:name= "android.intent.action.stk.session_end" /> <action android:name= "android.intent.action.stk.icc_status_change" /> <action android:name= "android.intent.action.stk.alpha_notify" /> <action android:name= "android.intent.action.LOCALE_CHANGED" /> </intent-filter> </receiver>
AndroidManifest.xml
file related to the receiver
component. As you can see, the component is fully exported. This allows not only intercepting SIM-card commands, but also creating a Parcelable
object with the help of malicious programs, and then sending it to com.android.stk.StkCmdReceiver
. Receiver
does not check the sender, and the android.intent.action.stk.command action is not announced in the AndroidManifest.xml system file as a secure message, which allows fraudsters to emulate sending SIM commands. public void onClick(View v) { String input = null; switch (v.getId()) { case OK_BUTTON: CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId); cancelTimeOut(); sendResponse(StkAppService.RES_ID_CONFIRM, true); break; case CANCEL_BUTTON: CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId); cancelTimeOut(); sendResponse(StkAppService.RES_ID_CONFIRM, false); break; } finish(); }
sendResponse(StkAppService.RES_ID_CONFIRM, true)
command will be called sendResponse(StkAppService.RES_ID_CONFIRM, true)
; otherwise, sendResponse(StkAppService.RES_ID_CONFIRM, false)
android.intent.action.stk.command
action create the same dialog box with other text (fake) and display it on the screen a few seconds before the SIM card generates the original message (“Confirm transaction No. 1234 on the amount of 100 500 rubles ")? In the text of the message we will write “Click OK to close”, and the buttons will remain the same - “OK” and “Cancel”.sendResponse()
method will be called with the "true" flag and the SIM card will receive the "OK" command as if it were sent from the original dialog. Even if the user selects the “Cancel” option in the second window, this will not affect the previous command in any way. The SIM card will take this as a new response, which it does not expect. In the source I was able to find a description of a similar situation: private void handleCmdResponse(CatResponseMessage resMsg) { // Make sure the response details match the last valid command. An invalid // response is a one that doesn't have a corresponding proactive command // and sending it can "confuse" the baseband/ril. // One reason for out of order responses can be UI glitches. For example, // if the application launch an activity, and that activity is stored // by the framework inside the history stack. That activity will be // available for relaunch using the latest application dialog // (long press on the home button). Relaunching that activity can send // the same command's result again to the CatService and can cause it to // get out of sync with the SIM. This can happen in case of // non-interactive type Setup Event List and SETUP_MENU proactive commands. // Stk framework would have already sent Terminal Response to Setup Event // List and SETUP_MENU proactive commands. After sometime Stk app will send // Envelope Command/Event Download. In which case, the response details doesn't // match with last valid command (which are not related). // However, we should allow Stk framework to send the message to ICC.
For /platform/frameworks/opt/telephony/+/master/: --- a/src/java/com/android/internal/telephony/cat/CatService.java +++ b/src/java/com/android/internal/telephony/cat/CatService.java @@ -501,7 +501,7 @@ intent.putExtra("STK CMD", cmdMsg); intent.putExtra("SLOT_ID", mSlotId); CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent,"android.permission.RECEIVE_STK_COMMANDS"); } /** @@ -514,7 +514,7 @@ mCurrntCmd = mMenuCmd; Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION); intent.putExtra("SLOT_ID", mSlotId); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent,"android.permission.RECEIVE_STK_COMMANDS"); } @@ -868,7 +868,7 @@ intent.putExtra(AppInterface.CARD_STATUS, cardPresent); CatLog.d(this, "Sending Card Status: " + cardState + " " + "cardPresent: " + cardPresent); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent,"android.permission.RECEIVE_STK_COMMANDS"); } private void broadcastAlphaMessage(String alphaString) { @@ -877,7 +877,7 @@ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(AppInterface.ALPHA_STRING, alphaString); intent.putExtra("SLOT_ID", mSlotId); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent,"android.permission.RECEIVE_STK_COMMANDS"); } @Override For /platform/frameworks/base/ : --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -303,6 +303,11 @@ <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE" /> <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED" /> + <protected-broadcast android:name="android.intent.action.stk.command" /> + <protected-broadcast android:name="android.intent.action.stk.session_end" /> + <protected-broadcast android:name="android.intent.action.stk.icc_status_change" /> + <protected-broadcast android:name="android.intent.action.stk.alpha_notify" /> + <!-- ====================================== --> <!-- Permissions for things that cost money --> <!-- ====================================== --> @@ -2923,6 +2928,9 @@ android:description="@string/permdesc_bindCarrierMessagingService" android:protectionLevel="signature|system" /> + <permission android:name="android.permission.RECEIVE_STK_COMMANDS" + android:protectionLevel="signature|system" /> + <!-- The system process is explicitly the only one allowed to launch the confirmation UI for full backup/restore --> <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/> For /platform/packages/apps/Stk/ : --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -24,6 +24,7 @@ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.GET_TASKS"/> + <uses-permission android:name="android.permission.RECEIVE_STK_COMMANDS"/> <application android:icon="@drawable/ic_launcher_sim_toolkit" android:label="@string/app_name"
Source: https://habr.com/ru/post/280095/
All Articles