我写了一个小代码部分来测试它:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
private EditText tfData;
private Button btSave, btLoad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tfData = (EditText) findViewById(R.id.tfData);
btSave = (Button) findViewById(R.id.btSave);
btLoad = (Button) findViewById(R.id.btLoad);
btSave.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doSave();
}
});
btLoad.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
doLoad();
}
});
tfData.setText("Some secret data");
boolean btLoadVisible = false; // TODO change this value for the second build!
if (!btLoadVisible) {
btLoad.setVisibility(View.GONE);
}
else{
btSave.setVisibility(View.INVISIBLE);
}
}
private static final String FILENAME = "private.dat";
private void doSave() {
String text = null;
if (tfData.getText() == null) {
Toast.makeText(this, "Please enter a string!", Toast.LENGTH_SHORT).show();
return;
}
text = tfData.getText().toString();
if (text == null || text.length() == 0) {
Toast.makeText(this, "Please enter a string!!!", Toast.LENGTH_SHORT).show();
}
FileOutputStream fos = null;
try {
fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(text.getBytes("UTF-8"));
fos.close();
fos = null;
new AlertDialog.Builder(this).setTitle("Saved").setMessage("Your data is saved:\n" + text+"\nChange the build to recover it!")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
} catch (Exception e) {
Log.e("doSave", "Can't save ...", e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// I don't care:
e.printStackTrace();
}
}
}
}
private void doLoad() {
FileInputStream fis = null;
try {
fis = openFileInput(FILENAME);
} catch (FileNotFoundException e) {
e.printStackTrace();
new AlertDialog.Builder(this)
.setTitle("FileNotFoundException")
.setMessage(
"The file with data can't be found. Or it wasn't saved at all or you have uninstalled the old app or... who knows.\nI can't recover the data, it is lost permanenty!!!")
.setPositiveButton("I am sad", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
return; // I don't like return from catch...
}
if (fis != null) {
try {
int size = fis.available();// not the best, but now I hope is possible to read 10-30 bytes without blocking
byte[] buff = new byte[size];
int readCount = fis.read(buff);
if (readCount != size) {
Toast.makeText(this, "Dammit can't read : " + size + " bytes, only " + readCount + ". Restart app, than phone? ", Toast.LENGTH_SHORT)
.show();
}
String text = new String(buff, "UTF-8");
tfData.setText(text);
new AlertDialog.Builder(this).setTitle("Loaded").setMessage("Your data is recovered:\n" + text)
.setPositiveButton("I am happy", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
} catch (IOException e) {
Log.e("doLoad", "Can't load ...", e);
new AlertDialog.Builder(this).setTitle("IOException").setMessage("There is some error while reading the data:\n" + e.getMessage())
.setPositiveButton("I am sad", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
}
}
}
}
清理、构建、导出为签名的 apk:例如 InternalMemoryReader_save.apk
保存密钥库!!!
- 更改
boolean btLoadVisible = false
为boolean btLoadVisible = true
.
- 使用相同的密钥库导出 apk!但名称不同,例如 InternalMemoryReader_load.apk - 但可以是 _datasaver _factoryservice 什么的。这通常不会提供给用户。
安装第一个 apk 并保存。
结论:
如果您是应用程序开发人员,并且您拥有密钥库并且能够签署修改后的 apk,那么您将可以访问该内部私有文件。
我希望我正在帮助其他人恢复他的应用程序数据,而不是浪费太多时间。
如果您知道更好的解决方案,请告诉我!