Skip to content

Commit

Permalink
New method of communication
Browse files Browse the repository at this point in the history
Introduce a new communication method between Magisk and Magisk Manager.

Magisk used to hardcode classnames and send broadcast/start activities to
specific components. This new method makes no assumption of any class names,
so Magisk Manager can easily be fully obfuscated.

In addition, the new method connects Magisk and Magisk Manager with random
abstract Linux sockets instead of socket files in filesystems, bypassing
file system complexities (selinux, permissions and such)
  • Loading branch information
topjohnwu committed Sep 16, 2018
1 parent 4cf8d41 commit 906b4aa
Show file tree
Hide file tree
Showing 21 changed files with 655 additions and 705 deletions.
9 changes: 8 additions & 1 deletion app/src/full/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,19 @@
<!-- Superuser -->

<activity
android:name=".superuser.RequestActivity"
android:name="a.p"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@style/SuRequest" />

<activity
android:name=".superuser.RequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@style/AppTheme.Translucent" />

<receiver android:name=".superuser.SuReceiver" />

<!-- Receiver -->
Expand Down
7 changes: 7 additions & 0 deletions app/src/full/java/a/p.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package a;

import com.topjohnwu.magisk.SuRequestActivity;

public class p extends SuRequestActivity {
/* stub */
}
3 changes: 0 additions & 3 deletions app/src/full/java/com/topjohnwu/magisk/Const.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ public static class Value {
public static final int NAMESPACE_MODE_ISOLATE = 2;
public static final int NO_NOTIFICATION = 0;
public static final int NOTIFICATION_TOAST = 1;
public static final int NOTIFY_NORMAL_LOG = 0;
public static final int NOTIFY_USER_TOASTS = 1;
public static final int NOTIFY_USER_TO_OWNER = 2;
public static final int SU_PROMPT = 0;
public static final int SU_AUTO_DENY = 1;
public static final int SU_AUTO_ALLOW = 2;
Expand Down
1 change: 1 addition & 0 deletions app/src/full/java/com/topjohnwu/magisk/Data.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class Data {
classMap.put(OnBootService.class, a.m.class);
classMap.put(UpdateCheckService.class, a.n.class);
classMap.put(AboutCardRow.class, a.o.class);
classMap.put(SuRequestActivity.class, a.p.class);

}

Expand Down
258 changes: 258 additions & 0 deletions app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
package com.topjohnwu.magisk;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.FileObserver;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.SuConnector;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import androidx.annotation.Nullable;

public class SuRequestActivity extends BaseActivity {
LinearLayout suPopup;
Spinner timeout;
ImageView appIcon;
TextView appNameView;
TextView packageNameView;
Button grant_btn;
Button deny_btn;
ImageView fingerprintImg;
TextView warning;

private SuConnector connector;
private Policy policy;
private CountDownTimer timer;
private FingerprintHelper fingerprintHelper;

class SuConnectorV1 extends SuConnector {

SuConnectorV1(String name) throws IOException {
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.FILESYSTEM));
new FileObserver(name) {
@Override
public void onEvent(int fileEvent, String path) {
if (fileEvent == FileObserver.DELETE_SELF) {
finish();
}
}
}.startWatching();
}

@Override
public void response() {
try (OutputStream out = getOutputStream()) {
out.write((policy.policy == Policy.ALLOW ? "socket:ALLOW" : "socket:DENY").getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

class SuConnectorV2 extends SuConnector {

SuConnectorV2(String name) throws IOException {
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT));
}

@Override
public void response() {
try (DataOutputStream out = getOutputStream()) {
out.writeInt(policy.policy);
} catch (IOException e) {
e.printStackTrace();
}
}
}

@Override
public int getDarkTheme() {
return R.style.SuRequest_Dark;
}

@Override
public void finish() {
if (timer != null)
timer.cancel();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
super.finish();
}

@Override
public void onBackPressed() {
if (policy != null) {
handleAction(Policy.DENY);
} else {
finish();
}
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);

PackageManager pm = getPackageManager();
mm.mDB.clearOutdated();

// Get policy
Intent intent = getIntent();
try {
connector = intent.getIntExtra("version", 1) == 1 ?
new SuConnectorV1(intent.getStringExtra("socket")) :
new SuConnectorV2(intent.getStringExtra("socket"));
Bundle bundle = connector.readSocketInput();
int uid = Integer.parseInt(bundle.getString("uid"));
policy = mm.mDB.getPolicy(uid);
if (policy == null) {
policy = new Policy(uid, pm);
}
} catch (IOException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
finish();
return;
}

// Never allow com.topjohnwu.magisk (could be malware)
if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME)) {
finish();
return;
}

switch (Data.suResponseType) {
case Const.Value.SU_AUTO_DENY:
handleAction(Policy.DENY, 0);
return;
case Const.Value.SU_AUTO_ALLOW:
handleAction(Policy.ALLOW, 0);
return;
case Const.Value.SU_PROMPT:
default:
}

// If not interactive, response directly
if (policy.policy != Policy.INTERACTIVE) {
handleAction();
return;
}

setContentView(R.layout.activity_request);
ViewBinder.bind(this);

appIcon.setImageDrawable(policy.info.loadIcon(pm));
appNameView.setText(policy.appName);
packageNameView.setText(policy.packageName);

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.allow_timeout, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
timeout.setAdapter(adapter);

timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
}
@Override
public void onFinish() {
deny_btn.setText(getString(R.string.deny_with_str, "(0)"));
handleAction(Policy.DENY);
}
};

boolean useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint();

if (useFingerprint) {
try {
fingerprintHelper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
warning.setText(errString);
}

@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
warning.setText(helpString);
}

@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
handleAction(Policy.ALLOW);
}

@Override
public void onAuthenticationFailed() {
warning.setText(R.string.auth_fail);
}
};
fingerprintHelper.authenticate();
} catch (Exception e) {
e.printStackTrace();
useFingerprint = false;
}
}

if (!useFingerprint) {
grant_btn.setOnClickListener(v -> {
handleAction(Policy.ALLOW);
timer.cancel();
});
grant_btn.requestFocus();
}

grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);

deny_btn.setOnClickListener(v -> {
handleAction(Policy.DENY);
timer.cancel();
});
suPopup.setOnClickListener(v -> cancelTimeout());
timeout.setOnTouchListener((v, event) -> cancelTimeout());
timer.start();
}

private boolean cancelTimeout() {
timer.cancel();
deny_btn.setText(getString(R.string.deny));
return false;
}

private void handleAction() {
connector.response();
finish();
}

private void handleAction(int action) {
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
}

private void handleAction(int action, int time) {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.addPolicy(policy);
}
handleAction();
}
}
5 changes: 2 additions & 3 deletions app/src/full/java/com/topjohnwu/magisk/ViewBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.topjohnwu.magisk.fragments.ReposFragment;
import com.topjohnwu.magisk.fragments.SuLogFragment;
import com.topjohnwu.magisk.fragments.SuperuserFragment;
import com.topjohnwu.magisk.superuser.RequestActivity;

import androidx.core.content.ContextCompat;

Expand Down Expand Up @@ -56,8 +55,8 @@ public static void bind(FlashActivity target) {
target.findViewById(R.id.no_thanks).setOnClickListener(v -> target.finish());
target.findViewById(R.id.save_logs).setOnClickListener(v -> target.saveLogs());
}
public static void bind(RequestActivity target) {

public static void bind(SuRequestActivity target) {
target.suPopup = target.findViewById(R.id.su_popup);
target.timeout = target.findViewById(R.id.timeout);
target.appIcon = target.findViewById(R.id.app_icon);
Expand Down
25 changes: 21 additions & 4 deletions app/src/full/java/com/topjohnwu/magisk/receivers/BootReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,31 @@
import android.content.Intent;
import android.text.TextUtils;

import a.m;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.utils.SuConnector;

public class BootReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED))
m.enqueueWork(context);
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
switch (intent.getExtras().getString("action", "boot")) {
case "request":
Intent i = new Intent(context, Data.classMap.get(SuRequestActivity.class))
.putExtra("socket", intent.getStringExtra("socket"))
.putExtra("version", 2)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
break;
case "log":
SuConnector.handleLogs(intent, 2);
break;
case "boot":
OnBootService.enqueueWork(context);
break;
}
}
}

}
Loading

0 comments on commit 906b4aa

Please sign in to comment.