1. The activities used within this app must be set to private Activities that are not intended to be made public must be set to private to prevent illegal calls - <activity
- android:name= ".PrivateActivity"
- android:label= "@string/app_name"
- android:exported= "false" />
-
- <activity
- android:name= ".PrivateActivity"
- android:label= "@string/app_name"
- android:exported= "false" />
At the same time, it is important to note that non-public Activities cannot set intent-filters Because, if there is another app with the same intent-filter on the same machine, calling the Activity's intent will wake up the Android selection screen, allowing you to choose which app to use to accept the intent. This will actually bypass the non-public settings. 2. Don’t specify taskAffinity All activities in Android are managed by tasks. Simply put, tasks are a stack data structure with a first-in, last-out order. Generally speaking, if you don't specify which task it belongs to, all Activities in the same app will exist in one task, and the name of the task is the packageName of the app. Because there will not be two apps with the same packageName in the same Android device, the Activity can be protected from attack. But if you specify taskAffinity, such as the following - [html]
- <application android:icon= "@drawable/icon" android:label= "@string/app_name" >
- <activity android:name= ".Activity1"
- android:taskAffinity= "com.winuxxan.task"
- android:label= "@string/app_name" >
- </activity>
- <activity android:name= ".Activity2" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <application android:icon= "@drawable/icon" android:label= "@string/app_name" >
- <activity android:name= ".Activity1"
- android:taskAffinity= "com.winuxxan.task"
- android:label= "@string/app_name" >
- </activity>
- <activity android:name= ".Activity2" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
At this point, if the malware's Activity is also declared as the same taskAffinity, then its Activity will be started in your task and will have a chance to get your intent. 3. Do not specify LaunchMode (default standard mode) The LaunchMode of Activity in Android is divided into the following four types Standard: The Activity opened in this way will not be treated as the rootActivity. A new Activity instance will be generated and will be in the same task as the opener. singleTop: Same as standard, except that if the first Activity in the current task is this Activity, no new instance will be generated. singleTask: The system creates a new task (if the application is not started) and a new instance of the activity at the root of the new task. Then, if the activity instance already exists in a separate task, the system calls the onNewIntent() method of the existing activity instead of creating a new instance. Only one activity instance exists at the same time. singleInstance: Similar to singleTask, except that the system will not let other activities run in all held task instances. This activity is independent and is the only member of the task. Any other activities running this activity will open an independent task. All intents sent to the root Activity will leave a record in Android. So generally speaking, it is forbidden to use singleTask or singleInstance to start the screen. However, even if you use the standard mode to open the screen, there may be problems. For example, if the caller's Activity is opened in singleInstance mode, that is, the called Activity is opened in standard mode, because the caller's Activitytask cannot have other tasks, Android will be forced to generate a new task and put the callee into it. In the end, the callee becomes the rootActivity. The procedure is as follows: - AndroidManifest.xml
-
- [html]
- <?xml version= "1.0" encoding= "utf-8" ?>
- <manifest xmlns:android= "http://schemas.android.com/apk/res/android"
- package = "org.jssec.android.activity.privateactivity"
- android:versionCode= "1"
- android:versionName= "1.0" >
- <uses-sdk android:minSdkVersion= "8" />
- <application
- android:icon= "@drawable/ic_launcher"
- android:label= "@string/app_name" >
- <!—The root Activity is started in "singleInstance" mode -->
- <!—Do not set taskAffinity-->
- <activity
- android:name= ".PrivateUserActivity"
- android:label= "@string/app_name"
- android:launchMode= "singleInstance" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- Non-public Activity -->
- <!—Startup mode is "standard" -->
- <!—Do not set taskAffinity-->
- <activity
- android:name= ".PrivateActivity"
- android:label= "@string/app_name"
- android:exported= "false" />
- </application>
- </manifest>
-
- <?xml version= "1.0" encoding= "utf-8" ?>
- <manifest xmlns:android= "http://schemas.android.com/apk/res/android"
- package = "org.jssec.android.activity.privateactivity"
- android:versionCode= "1"
- android:versionName= "1.0" >
- <uses-sdk android:minSdkVersion= "8" />
- <application
- android:icon= "@drawable/ic_launcher"
- android:label= "@string/app_name" >
- <!—The root Activity is started in "singleInstance" mode -->
- <!—Do not set taskAffinity-->
- <activity
- android:name= ".PrivateUserActivity"
- android:label= "@string/app_name"
- android:launchMode= "singleInstance" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- Non-public Activity -->
- <!—Startup mode is "standard" -->
- <!—Do not set taskAffinity-->
- <activity
- android:name= ".PrivateActivity"
- android:label= "@string/app_name"
- android:exported= "false" />
- </application>
- </manifest>
The code of the non-public Activity is as follows: - [java]
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.private_activity);
- String param = getIntent().getStringExtra( "PARAM" );
- Toast.makeText( this , String.format( ""%s" is obtained." , param),
- Toast.LENGTH_LONG).show();
- }
-
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra( "RESULT" , confidential data");
- setResult(RESULT_OK, intent);
- finish();
- }
- }
-
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.private_activity);
- String param = getIntent().getStringExtra( "PARAM" );
- Toast.makeText( this , String.format( ""%s" is obtained." , param),
- Toast.LENGTH_LONG).show();
- }
-
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra( "RESULT" , confidential data");
- setResult(RESULT_OK, intent);
- finish();
- }
- }
The caller of a non-public Activity opens it in standard mode - [java]
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateUserActivity extends Activity {
- private static final int REQUEST_CODE = 1 ;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.user_activity);
- }
-
- public void onUseActivityClick(View view) {
-
- Intent intent = new Intent();
- intent.setClass( this , PrivateActivity. class );
- intent.putExtra( "PARAM" , "Confidential data" );
- startActivityForResult(intent, REQUEST_CODE);
- }
-
- @Override
- public void onActivityResult( int requestCode, int resultCode,
- Intent data) {
- super .onActivityResult(requestCode, resultCode, data);
- if (resultCode != RESULT_OK)
- return ;
- switch (requestCode) {
- case REQUEST_CODE:
- String result = data.getStringExtra( "RESULT" );
- break ;
- }
- }
- }
-
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateUserActivity extends Activity {
- private static final int REQUEST_CODE = 1 ;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.user_activity);
- }
-
- public void onUseActivityClick(View view) {
-
- Intent intent = new Intent();
- intent.setClass( this , PrivateActivity. class );
- intent.putExtra( "PARAM" , "Confidential data" );
- startActivityForResult(intent, REQUEST_CODE);
- }
-
- @Override
- public void onActivityResult( int requestCode, int resultCode,
- Intent data) {
- super .onActivityResult(requestCode, resultCode, data);
- if (resultCode != RESULT_OK)
- return ;
- switch (requestCode) {
- case REQUEST_CODE:
- String result = data.getStringExtra( "RESULT" );
- break ;
- }
- }
- }
4. Do not set the intent sent to the Activity to FLAG_ACTIVITY_NEW_TASK Even if the lauchMode of the above Activity is set perfectly, you can still specify the opening mode when opening the intent. For example, if the FLAG_ACTIVITY_NEW_TASK mode is specified in the intent, if the activity does not exist, a new task will be created. If FLAG_ACTIVITY_MULTIPLE_TASK + FLAG_ACTIVITY_NEW_TASK are set at the same time, a new task will be generated no matter what, the activity will become the rootActiviy, and the intent will be saved as a resume 5. Encryption of data in Intent The data transmission in Activity relies on intent, which is very easy to be attacked. Therefore, even if the data is transmitted within the same app, it is best to encrypt it. There are many encryption algorithms. 6. Clear ActivityName to send Intent Clearly sending the Intent by Activity can prevent it from being intercepted by malware. Sending within the same app - [java]
- Intent intent = new Intent( this , PictureActivity. class );
- intent.putExtra( "BARCODE" , barcode);
- startActivity(intent);
-
- Intent intent = new Intent( this , PictureActivity. class );
- intent.putExtra( "BARCODE" , barcode);
- startActivity(intent);
-
-
- Sending within different apps
-
- [java]
- Intent intent = new Intent();
- intent.setClassName(
- "org.jssec.android.activity.publicactivity" ,
- "org.jssec.android.activity.publicactivity.PublicActivity" );
- startActivity(intent);
-
- Intent intent = new Intent();
- intent.setClassName(
- "org.jssec.android.activity.publicactivity" ,
- "org.jssec.android.activity.publicactivity.PublicActivity" );
- startActivity(intent);
But, be careful! Not all problems can be avoided by specifying packageName and ActivityName. If a malware deliberately creates the same packageName and ActivityName as the target you are sending to, the intent will be intercepted. 7. When accepting intents across apps, make sure the other party is identified When receiving an intent from another app, you need to be able to determine the identity of the other party. A good way is to compare the hashcode of the other party's app. Currently, the premise is that the caller must use startActivityForResult(), because only this method can the callee get the caller's packageName The code is as follows: The called Activity - [java]
- package org.jssec.android.activity.exclusiveactivity;
- import org.jssec.android.shared.PkgCertWhitelists;
- import org.jssec.android.shared.Utils;
- import android.app.Activity;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class ExclusiveActivity extends Activity {
-
- private static PkgCertWhitelists sWhitelists = null ;
-
- private static void buildWhitelists(Context context) {
- boolean isdebug = Utils.isDebuggable(context);
- sWhitelists = new PkgCertWhitelists();
- sWhitelists
- .add( "org.jssec.android.activity.exclusiveuser" , isdebug ?
- "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255"
- :
- "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A" );
- }
-
- private static boolean checkPartner(Context context, String pkgname) {
- if (sWhitelists == null )
- buildWhitelists(context);
- return sWhitelists.test(context, pkgname);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- if (!checkPartner( this , getCallingPackage())) {
- Toast.makeText( this , "Not in the whitelist." , Toast.LENGTH_LONG).show();
- finish();
- return ;
- }
- }
-
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra( "RESULT" , "confidential data" );
- setResult(RESULT_OK, intent);
- finish();
- }
- }
-
- package org.jssec.android.activity.exclusiveactivity;
- import org.jssec.android.shared.PkgCertWhitelists;
- import org.jssec.android.shared.Utils;
- import android.app.Activity;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class ExclusiveActivity extends Activity {
-
- private static PkgCertWhitelists sWhitelists = null ;
-
- private static void buildWhitelists(Context context) {
- boolean isdebug = Utils.isDebuggable(context);
- sWhitelists = new PkgCertWhitelists();
- sWhitelists
- .add( "org.jssec.android.activity.exclusiveuser" , isdebug ?
- "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255"
- :
- "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A" );
- }
-
- private static boolean checkPartner(Context context, String pkgname) {
- if (sWhitelists == null )
- buildWhitelists(context);
- return sWhitelists.test(context, pkgname);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- if (!checkPartner( this , getCallingPackage())) {
- Toast.makeText( this , "Not in the whitelist." , Toast.LENGTH_LONG).show();
- finish();
- return ;
- }
- }
-
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra( "RESULT" , "confidential data" );
- setResult(RESULT_OK, intent);
- finish();
- }
- } [java]
-
-
- PkgCertWhitelists.java
- [java]
- package org.jssec.android.shared;
- import java.util.HashMap;
- import java.util.Map;
- import android.content.Context;
- public class PkgCertWhitelists {
- private Map<String, String> mWhitelists = new HashMap<String, String>();
-
- public boolean add(String pkgname, String sha256) {
- if (pkgname == null )
- return false ;
- if (sha256 == null )
- return false ;
- sha256 = sha256.replaceAll( " " , "" );
- if (sha256.length() != 64 )
- return false ;
- sha256 = sha256.toUpperCase();
- if (sha256.replaceAll( "[0-9A-F]+" , "" ).length() != 0 )
- return false ;
- mWhitelists.put(pkgname, sha256);
- return true ;
- }
-
- public boolean test(Context ctx, String pkgname) {
- String correctHash = mWhitelists.get(pkgname);
- return PkgCert.test(ctx, pkgname, correctHash);
- }
- }
-
- package org.jssec.android.shared;
- import java.util.HashMap;
- import java.util.Map;
- import android.content.Context;
- public class PkgCertWhitelists {
- private Map<String, String> mWhitelists = new HashMap<String, String>();
-
- public boolean add(String pkgname, String sha256) {
- if (pkgname == null )
- return false ;
- if (sha256 == null )
- return false ;
- sha256 = sha256.replaceAll( " " , "" );
- if (sha256.length() != 64 )
- return false ;
- sha256 = sha256.toUpperCase();
- if (sha256.replaceAll( "[0-9A-F]+" , "" ).length() != 0 )
- return false ;
- mWhitelists.put(pkgname, sha256);
- return true ;
- }
-
- public boolean test(Context ctx, String pkgname) {
- String correctHash = mWhitelists.get(pkgname);
- return PkgCert.test(ctx, pkgname, correctHash);
- }
- }
- PkgCert.java
- [java]
-
- package org.jssec.android.shared;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import android.content.Context;
- import android.content.pm.PackageInfo;
- import android.content.pm.PackageManager;
- import android.content.pm.PackageManager.NameNotFoundException;
- import android.content.pm.Signature;
- public class PkgCert {
- public static boolean test(Context ctx, String pkgname,
- String correctHash) {
- if (correctHash == null )
- return false ;
- correctHash = correctHash.replaceAll( " " , "" );
- return correctHash.equals(hash(ctx, pkgname));
- }
-
- public static String hash(Context ctx, String pkgname) {
- if (pkgname == null )
- return null ;
- try {
- PackageManager pm = ctx.getPackageManager();
- PackageInfo pkginfo = pm.getPackageInfo(pkgname,
- PackageManager.GET_SIGNATURES);
- if (pkginfo.signatures.length != 1 )
- return null ;
- Signature sig = pkginfo.signatures[ 0 ];
- byte [] cert = sig.toByteArray();
- byte [] sha256 = computeSha256(cert);
- return byte2hex(sha256);
- } catch (NameNotFoundException e) {
- return null ;
- }
- }
-
- private static byte [] computeSha256( byte [] data) {
- try {
- return MessageDigest.getInstance( "SHA-256" ).digest(data);
- } catch (NoSuchAlgorithmException e) {
- return null ;
- }
- }
-
- private static String byte2hex( byte [] data) {
- if (data == null )
- return null ;
- final StringBuilder hexadecimal = new StringBuilder();
- for ( final byte b : data) {
- hexadecimal.append(String.format( "%02X" , b));
- }
- return hexadecimal.toString();
- }
- }
-
-
- package org.jssec.android.shared;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import android.content.Context;
- import android.content.pm.PackageInfo;
- import android.content.pm.PackageManager;
- import android.content.pm.PackageManager.NameNotFoundException;
- import android.content.pm.Signature;
- public class PkgCert {
- public static boolean test(Context ctx, String pkgname,
- String correctHash) {
- if (correctHash == null )
- return false ;
- correctHash = correctHash.replaceAll( " " , "" );
- return correctHash.equals(hash(ctx, pkgname));
- }
-
- public static String hash(Context ctx, String pkgname) {
- if (pkgname == null )
- return null ;
- try {
- PackageManager pm = ctx.getPackageManager();
- PackageInfo pkginfo = pm.getPackageInfo(pkgname,
- PackageManager.GET_SIGNATURES);
- if (pkginfo.signatures.length != 1 )
- return null ;
- Signature sig = pkginfo.signatures[ 0 ];
- byte [] cert = sig.toByteArray();
- byte [] sha256 = computeSha256(cert);
- return byte2hex(sha256);
- } catch (NameNotFoundException e) {
- return null ;
- }
- }
-
- private static byte [] computeSha256( byte [] data) {
- try {
- return MessageDigest.getInstance( "SHA-256" ).digest(data);
- } catch (NoSuchAlgorithmException e) {
- return null ;
- }
- }
-
- private static String byte2hex( byte [] data) {
- if (data == null )
- return null ;
- final StringBuilder hexadecimal = new StringBuilder();
- for ( final byte b : data) {
- hexadecimal.append(String.format( "%02X" , b));
- }
- return hexadecimal.toString();
- }
- }
8. All intents in the root Activity can be shared by all apps All apps can retrieve the intents received by all root activities in all tasks on this phone as follows: - AndroidManifest.xml
-
- [html]
- <manifest xmlns:android= "http://schemas.android.com/apk/res/android"
- package = "org.jssec.android.intent.maliciousactivity"
- android:versionCode= "1"
- android:versionName= "1.0" >
-
- <uses-sdk
- android:minSdkVersion= "8"
- android:targetSdkVersion= "15" />
-
- <application
- android:icon= "@drawable/ic_launcher"
- android:label= "@string/app_name"
- android:theme= "@style/AppTheme" >
- <activity
- android:name= ".MaliciousActivity"
- android:label= "@string/title_activity_main" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
-
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <uses-permission android:name= "android.permission.GET_TASKS" />
-
- </manifest>
-
- <manifest xmlns:android= "http://schemas.android.com/apk/res/android"
- package = "org.jssec.android.intent.maliciousactivity"
- android:versionCode= "1"
- android:versionName= "1.0" >
-
- <uses-sdk
- android:minSdkVersion= "8"
- android:targetSdkVersion= "15" />
-
- <application
- android:icon= "@drawable/ic_launcher"
- android:label= "@string/app_name"
- android:theme= "@style/AppTheme" >
- <activity
- android:name= ".MaliciousActivity"
- android:label= "@string/title_activity_main" >
- <intent-filter>
- <action android:name= "android.intent.action.MAIN" />
-
- <category android:name= "android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
- <uses-permission android:name= "android.permission.GET_TASKS" />
-
- </manifest>
- MaliciousActivity.java
-
- [java]
- package org.jssec.android.intent.maliciousactivity;
- import java.util.List;
- import android.app.Activity;
- import android.app.ActivityManager;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class MaliciousActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.malicious_activity);
- ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- List<ActivityManager.RecentTaskInfo> list = activityManager
- .getRecentTasks( 100 , ActivityManager.RECENT_WITH_EXCLUDED);
- for (ActivityManager.RecentTaskInfo r : list) {
- Intent intent = r.baseIntent;
- Log.v( "baseIntent" , intent.toString());
- }
- }
- }
-
- package org.jssec.android.intent.maliciousactivity;
- import java.util.List;
- import android.app.Activity;
- import android.app.ActivityManager;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class MaliciousActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState);
- setContentView(R.layout.malicious_activity);
- ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- List<ActivityManager.RecentTaskInfo> list = activityManager
- .getRecentTasks( 100 , ActivityManager.RECENT_WITH_EXCLUDED);
- for (ActivityManager.RecentTaskInfo r : list) {
- Intent intent = r.baseIntent;
- Log.v( "baseIntent" , intent.toString());
- }
- }
- }
9. Possibility of Intent data being lost in LogCat If the code is like the following, the data sent in the Intent will be automatically written to LogCat - [java]
- Uri uri = Uri.parse( "mailto:[email protected]" );
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- startActivity(intent);
-
- Uri uri = Uri.parse( "mailto:[email protected]" );
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- startActivity(intent);
-
-
- If you do it like this, you can avoid
-
- [java]
- Uri uri = Uri.parse( "mailto:" );
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "[email protected]" });
- startActivity(intent);
-
- Uri uri = Uri.parse( "mailto:" );
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- intent.putExtra(Intent.EXTRA_EMAIL, new String[] { "[email protected]" });
- startActivity(intent);
|