功能-免Root静默安装

免Root静默安装

实现原理:在PC上运行一Java程序,建立socket服务器与app通信,远程执行app下发的代码。「并不是给 app 提权,而是运行了一个有 shell 权限的新程序」

Tips:需要USB连着PC,实现免Root点击任意位置或静默安装。

步骤

  1. 运行app。连着PC,adb shell后才可以输入app_process指令
  2. 找到build/outputs/apk/debug/app-debug.apk,解压得到classes.dex。通过app_process来启动 app_process -Djava.class.path=/data/local/tmp/classes.dex /system/bin shellService.Main

通过「app_process」来启动 Java 。app_process 的参数如下:

1
app_process [vm-options] cmd-dir [options] start-class-name[main-options]

vm-options – VM 选项

cmd-dir –父目录 (/system/bin)

options –运行的参数 :

–zygote

–start-system-server

–application (api>=14)

–nice-name=nice_proc_name (api>=14)

start-class-name–包含main方法的主类 (com.android.commands.am.Am)

main-options–启动时候传递到main方法中的参数

**完整代码**

  • app代码:发送指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//app/src/main/java/top.gtf35.shellapplicatontest/MainActivity.java

package top.gtf35.shellapplicatontest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

private EditText mCmdInputEt;
private Button mRunShellBtn;
private TextView mOutputTv;

private void initView(){
mCmdInputEt = findViewById(R.id.et_cmd);
mRunShellBtn = findViewById(R.id.btn_runshell);
mOutputTv = findViewById(R.id.tv_output);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mRunShellBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String cmd = mCmdInputEt.getText().toString();
if (TextUtils.isEmpty(cmd)) {
Toast.makeText(MainActivity.this, "输入内容为空", Toast.LENGTH_SHORT).show();
return;
}
runShell(cmd);
}
});
}

private void runShell(final String cmd){
if (TextUtils.isEmpty(cmd)) {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
new SocketClient(cmd, new SocketClient.onServiceSend() {
@Override
public void getSend(String result) {
showTextOnTextView(result);
}
});
}
}).start();
}

private void showTextOnTextView(final String text){
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (TextUtils.isEmpty(mOutputTv.getText())) {
mOutputTv.setText(text);
} else {
mOutputTv.setText(mOutputTv.getText() + "\n" + text);
}
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//app/src/main/java/top.gtf35.shellapplicatontest/SocketClient.java
package top.gtf35.shellapplicatontest;

import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;

public class SocketClient {

private String TAG = "SocketClient";

private String HOST = "127.0.0.1";
PrintWriter printWriter;//发送用的
onServiceSend mOnServiceSend;
String cmd;
BufferedReader bufferedReader;
int port = 4521;

public SocketClient(String commod, onServiceSend onServiceSend) {
cmd = commod;
mOnServiceSend = onServiceSend;
try {
Log.d(TAG, "与service进行socket通讯,地址=" + HOST + ":" + port);
/** 创建Socket*/
// 创建一个流套接字并将其连接到指定 IP 地址的指定端口号(本处是本机)
Socket socket = new Socket();
socket.connect(new InetSocketAddress(HOST, port), 3000);//设置连接请求超时时间3 s
// 接收3s超时
socket.setSoTimeout(3000);
Log.d(TAG, "与service进行socket通讯,超时为:" + 3000);
/** 发送客户端准备传输的信息 */
// 由Socket对象得到输出流,并构造PrintWriter对象
printWriter = new PrintWriter(socket.getOutputStream(), true);
/** 用于获取服务端传输来的信息 */
// 由Socket对象得到输入流,并构造相应的BufferedReader对象
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new CreateServerThread(socket);
send(cmd);
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "与service进行socket通讯发生错误" + e);
mOnServiceSend.getSend("###ShellRunError:" + e.toString());
}
}

//线程类
class CreateServerThread extends Thread {
Socket socket;
InputStreamReader inputStreamReader;
BufferedReader reader;
public CreateServerThread(Socket s) throws IOException {
Log.d(TAG, "创建了一个新的连接线程");
socket = s;
start();
}

@Override
public void run() {
try {
// 打印读入一字符串并回调
try {
inputStreamReader = new InputStreamReader(socket.getInputStream());
reader = new BufferedReader(inputStreamReader);
String line = null;
while ((line = reader.readLine()) != null) {
if (line != null) {
mOnServiceSend.getSend(line);
}
}
Log.d(TAG, "客户端接收解析服务器的while循环结束");
} catch (Exception e){
e.printStackTrace();
Log.d(TAG, "客户端接收解析服务器的Threadcatch块执行:" + e.toString());
}
bufferedReader.close();
printWriter.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, "socket 接收线程发生错误:" + e.toString());
}
}
}


public void send(String cmd){
printWriter.println(cmd );
// 刷新输出流,使Server马上收到该字符串
printWriter.flush();
}

public interface onServiceSend{
void getSend(String result);
}
}
  • java程序代码:接收指令,执行免root的操作
1
2
3
4
5
6
7
8
9
//app/src/main/java/shellService/shellService.Main.java  Java程序启动新线程
package shellService;
public class Main {
public static void main(String[] args){
new ServiceThread().start();
while (true) {
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//app/src/main/java/shellService/ServiceThread.java 子线程调用服务,服务中执行指令
package shellService;

public class ServiceThread extends Thread {
private static int ShellPORT = 4521;

@Override
public void run() {
System.out.println(">>>>>>Shell服务端程序被调用<<<<<<");
new Service(new Service.ServiceGetText() {
@Override
public String getText(String text) {
if (text.startsWith("###AreYouOK")){
return "###IamOK#";
}
try{
ServiceShellUtils.ServiceShellCommandResult sr = ServiceShellUtils.execCommand(text, false);
if (sr.result == 0){
return "###ShellOK#" + sr.successMsg;
} else {
return "###ShellError#" + sr.errorMsg;
}
}catch (Exception e){
return "###CodeError#" + e.toString();
}
}
}, ShellPORT);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//app/src/main/java/shellService/Service.java 执行具体指令的服务
package shellService;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Service {
private ServiceGetText mServiceGetText;


public Service(ServiceGetText serviceGetText, int PORT) {
mServiceGetText = serviceGetText;
try {
/** 创建ServerSocket*/
// 创建一个ServerSocket在端口4521监听客户请求
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("服务端运行在" + PORT + "端口");
while (true) {
// 侦听并接受到此Socket的连接,请求到来则产生一个Socket对象,并继续执行
Socket socket = serverSocket.accept();
System.out.println("监听请求到来则产生一个Socket对象,并继续执行");
new CreateServerThread(socket);//当有请求时,启一个线程处理
}
} catch (Exception e) {
System.out.println("连接监听发生错误 Exception:" + e);
}finally{
// serverSocket.close();
}
}

//线程类
class CreateServerThread extends Thread {
Socket socket;
public CreateServerThread(Socket s) throws IOException {
System.out.println("创建了一个新的连接线程");
socket = s;
start();
}

@Override
public void run() {
try {
/** 获取客户端传来的信息 */
// 由Socket对象得到输入流,并构造相应的BufferedReader对象
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("由Socket对象得到输入流,并构造相应的BufferedReader对象");
String line = bufferedReader.readLine();
/** 发送服务端准备传输的 */
// 由Socket对象得到输出流,并构造PrintWriter对象
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
System.out.println("由Socket对象得到输出流,并构造PrintWriter对象");
// 获取从客户端读入的字符串
System.out.println("while循环:获取从客户端读入的字符串");
System.out.println("while循环:客户端返回 : " + line);
String repeat = mServiceGetText.getText(line);
System.out.println("while循环:服务器将返回:" + repeat);
//printWriter.print("hello Client, I am Server!");
printWriter.print(repeat);
System.out.println("while循环:准备刷新返回");
printWriter.flush();
System.out.println("while循环:已刷新返回");
System.out.println("关闭Socket");
/** 关闭Socket*/
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
System.out.println("socket 连接线程发生错误:" + e.toString());
}
}
}

public interface ServiceGetText{
String getText(String text);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//app/src/main/java/shellService/ServiceSellUtils.java 执行命令的工具类
package shellService;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;


public class ServiceShellUtils {

public static final String COMMAND_SU = "su"; // 获取root权限的命令
public static final String COMMAND_SH = "sh"; // 执行sh文件的命令
public static final String COMMAND_EXIT = "exit\n"; // 退出的命令
public static final String COMMAND_LINE_END = "\n"; // 执行命令必须加在末尾

private ServiceShellUtils() {
throw new AssertionError();
}
//检测root状态
public static boolean checkRootPermission() {
return execCommand("echo root", true, false).result == 0;
}
//执行单行命令,实际还是调用的执行多行 ,传入命令和是否需要root
public static ServiceShellUtils.ServiceShellCommandResult execCommand(String command, boolean isRoot) {
return execCommand(new String[]{command}, isRoot, true);
}
//执行List<String>中的命令 , 传入List和是否需要root
public static ServiceShellUtils.ServiceShellCommandResult execCommand(List<String> commands, boolean isRoot) {
return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, true);
}
//执行多行命令
public static ServiceShellUtils.ServiceShellCommandResult execCommand(String[] commands, boolean isRoot) {
return execCommand(commands, isRoot, true);
}

public static ServiceShellUtils.ServiceShellCommandResult execCommand(String command, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(new String[]{command}, isRoot, isNeedResultMsg);
}

public static ServiceShellUtils.ServiceShellCommandResult execCommand(List<String> commands, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg);
}
//执行命令,获得返回的信息
public static ServiceShellUtils.ServiceShellCommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) {
int result = -1;
if (commands == null || commands.length == 0) {
return new ServiceShellUtils.ServiceShellCommandResult(result, null, null);
}

Process process = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = null;
StringBuilder errorMsg = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
}
os.write(command.getBytes());
os.writeBytes(COMMAND_LINE_END);
os.flush();
}
os.writeBytes(COMMAND_EXIT);
os.flush();

result = process.waitFor();
// get command result
if (isNeedResultMsg) {
successMsg = new StringBuilder();
errorMsg = new StringBuilder();
successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(s);
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (successResult != null) {
successResult.close();
}
if (errorResult != null) {
errorResult.close();
}
} catch (IOException e) {
e.printStackTrace();
}

if (process != null) {
process.destroy();
}
}
return new ServiceShellUtils.ServiceShellCommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null
: errorMsg.toString());
}
//封装了返回信息
public static class ServiceShellCommandResult {

public int result;
public String successMsg; //成功信息
public String errorMsg; // 错误信息

public ServiceShellCommandResult(int result) {
this.result = result;
}

public ServiceShellCommandResult(int result, String successMsg, String errorMsg) {
this.result = result;
this.successMsg = successMsg;
this.errorMsg = errorMsg;
}
}
}