NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte 10 | * order of these buffers is little-endian. 11 | */ 12 | public abstract class ZipUtils { 13 | 14 | private static final int ZIP_EOCD_REC_MIN_SIZE = 22; 15 | private static final int ZIP_EOCD_REC_SIG = 0x06054b50; 16 | private static final int ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET = 12; 17 | private static final int ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET = 16; 18 | private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20; 19 | 20 | private static final int ZIP64_EOCD_LOCATOR_SIZE = 20; 21 | private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50; 22 | 23 | private static final int UINT16_MAX_VALUE = 0xffff; 24 | 25 | private ZipUtils() { 26 | } 27 | 28 | /** 29 | * Returns the position at which ZIP End of Central Directory record starts in the provided 30 | * buffer or {@code -1} if the record is not present. 31 | * 32 | *
NOTE: Byte order of {@code zipContents} must be little-endian. 33 | */ 34 | public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) { 35 | assertByteOrderLittleEndian(zipContents); 36 | 37 | // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive. 38 | // The record can be identified by its 4-byte signature/magic which is located at the very 39 | // beginning of the record. A complication is that the record is variable-length because of 40 | // the comment field. 41 | // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from 42 | // end of the buffer for the EOCD record signature. Whenever we find a signature, we check 43 | // the candidate record's comment length is such that the remainder of the record takes up 44 | // exactly the remaining bytes in the buffer. The search is bounded because the maximum 45 | // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number. 46 | 47 | int archiveSize = zipContents.capacity(); 48 | if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) { 49 | return -1; 50 | } 51 | int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE); 52 | int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE; 53 | for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength; expectedCommentLength++) { 54 | int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength; 55 | if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) { 56 | int actualCommentLength = getUnsignedInt16(zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET); 57 | if (actualCommentLength == expectedCommentLength) { 58 | return eocdStartPos; 59 | } 60 | } 61 | } 62 | 63 | return -1; 64 | } 65 | 66 | /** 67 | * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory 68 | * Locator. 69 | * 70 | *
NOTE: Byte order of {@code zipContents} must be little-endian. 71 | */ 72 | public static boolean isZip64EndOfCentralDirectoryLocatorPresent(ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) { 73 | assertByteOrderLittleEndian(zipContents); 74 | 75 | // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central 76 | // Directory Record. 77 | 78 | int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE; 79 | if (locatorPosition < 0) { 80 | return false; 81 | } 82 | 83 | return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG; 84 | } 85 | 86 | /** 87 | * Returns the offset of the start of the ZIP Central Directory in the archive. 88 | * 89 | *
NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. 90 | */ 91 | public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) { 92 | assertByteOrderLittleEndian(zipEndOfCentralDirectory); 93 | return getUnsignedInt32(zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); 94 | } 95 | 96 | /** 97 | * Sets the offset of the start of the ZIP Central Directory in the archive. 98 | * 99 | *
NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian. 100 | */ 101 | public static void setZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory, long offset) { 102 | assertByteOrderLittleEndian(zipEndOfCentralDirectory); 103 | setUnsignedInt32(zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET, offset); 104 | } 105 | 106 | /** 107 | * Returns the size (in bytes) of the ZIP Central Directory. 108 | * 109 | *
NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
110 | */
111 | public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) {
112 | assertByteOrderLittleEndian(zipEndOfCentralDirectory);
113 | return getUnsignedInt32(zipEndOfCentralDirectory, zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET);
114 | }
115 |
116 | private static void assertByteOrderLittleEndian(ByteBuffer buffer) {
117 | if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
118 | throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
119 | }
120 | }
121 |
122 | private static int getUnsignedInt16(ByteBuffer buffer, int offset) {
123 | return buffer.getShort(offset) & 0xffff;
124 | }
125 |
126 | private static long getUnsignedInt32(ByteBuffer buffer, int offset) {
127 | return buffer.getInt(offset) & 0xffffffffL;
128 | }
129 |
130 | private static void setUnsignedInt32(ByteBuffer buffer, int offset, long value) {
131 | if ((value < 0) || (value > 0xffffffffL)) {
132 | throw new IllegalArgumentException("uint32 value of out range: " + value);
133 | }
134 | buffer.putInt(buffer.position() + offset, (int) value);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/randompkg/src/main/java/cn/martinkay/randompkg/utils/AXML.kt:
--------------------------------------------------------------------------------
1 | package cn.martinkay.randompkg.utils
2 |
3 | import java.io.ByteArrayOutputStream
4 | import java.nio.ByteBuffer
5 | import java.nio.ByteOrder.LITTLE_ENDIAN
6 | import java.nio.charset.Charset
7 | import java.util.*
8 |
9 | class AXML(b: ByteArray) {
10 |
11 | var bytes = b
12 | private set
13 |
14 | companion object {
15 | private const val CHUNK_SIZE_OFF = 4
16 | private const val STRING_INDICES_OFF = 7 * 4
17 | private val UTF_16LE = Charset.forName("UTF-16LE")
18 | }
19 |
20 | /**
21 | * String pool header:
22 | * 0: 0x1C0001
23 | * 1: chunk size
24 | * 2: number of strings
25 | * 3: number of styles (assert as 0)
26 | * 4: flags
27 | * 5: offset to string data
28 | * 6: offset to style data (assert as 0)
29 | *
30 | * Followed by an array of uint32_t with size = number of strings
31 | * Each entry points to an offset into the string data
32 | */
33 | fun findAndPatch(vararg patterns: Pair
101 | *
105 | */
106 | public static CommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) {
107 | int result = -1;
108 | if (commands == null || commands.length == 0) {
109 | return new CommandResult(result, null, null);
110 | }
111 |
112 | Process process = null;
113 | BufferedReader successResult = null;
114 | BufferedReader errorResult = null;
115 | StringBuilder successMsg = null;
116 | StringBuilder errorMsg = null;
117 |
118 | DataOutputStream os = null;
119 | try {
120 | process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
121 | os = new DataOutputStream(process.getOutputStream());
122 | for (String command : commands) {
123 | if (command == null) {
124 | continue;
125 | }
126 | Log.d(TAG,"执行命令 : " + command);
127 | // donnot use os.writeBytes(commmand), avoid chinese charset error
128 | os.write(command.getBytes());
129 | os.writeBytes(COMMAND_LINE_END);
130 | os.flush();
131 | }
132 | os.writeBytes(COMMAND_EXIT);
133 | os.flush();
134 |
135 | result = process.waitFor();
136 | // get command result
137 | if (isNeedResultMsg) {
138 | successMsg = new StringBuilder();
139 | errorMsg = new StringBuilder();
140 | successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
141 | errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
142 | String s;
143 | while ((s = successResult.readLine()) != null) {
144 | successMsg.append(s);
145 | }
146 | while ((s = errorResult.readLine()) != null) {
147 | errorMsg.append(s);
148 | }
149 | }
150 | } catch (IOException e) {
151 | e.printStackTrace();
152 | } catch (Exception e) {
153 | e.printStackTrace();
154 | } finally {
155 | try {
156 | if (os != null) {
157 | os.close();
158 | }
159 | if (successResult != null) {
160 | successResult.close();
161 | }
162 | if (errorResult != null) {
163 | errorResult.close();
164 | }
165 | } catch (IOException e) {
166 | e.printStackTrace();
167 | }
168 |
169 | if (process != null) {
170 | process.destroy();
171 | }
172 | }
173 | return new CommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null
174 | : errorMsg.toString());
175 | }
176 |
177 | /**
178 | * result of command
179 | *
180 | *
185 | *
186 | * @author Trinea 2013-5-16
187 | */
188 | public static class CommandResult {
189 |
190 | /** result of command **/
191 | public int result;
192 | /** success message of command result **/
193 | public String successMsg;
194 | /** error message of command result **/
195 | public String errorMsg;
196 |
197 | public CommandResult(int result) {
198 | this.result = result;
199 | }
200 |
201 | public CommandResult(int result, String successMsg, String errorMsg) {
202 | this.result = result;
203 | this.successMsg = successMsg;
204 | this.errorMsg = errorMsg;
205 | }
206 | }
207 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | mavenCentral()
12 | maven("https://maven.aliyun.com/repository/google")
13 | maven("https://maven.aliyun.com/repository/public")
14 | maven("https://jitpack.io")
15 | google()
16 | maven("https://api.xposed.info/")
17 | }
18 | }
19 |
20 | rootProject.name = "AutoCheckinPluginPlus"
21 | include(":app")
22 | include(":smart:detect")
23 | include(":randompkg")
24 |
--------------------------------------------------------------------------------
/smart/detect/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/smart/detect/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.org.jetbrains.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "cn.martinkay.detect"
8 | compileSdk = 34
9 |
10 | defaultConfig {
11 | minSdk = 24
12 |
13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles("consumer-rules.pro")
15 | }
16 |
17 | buildTypes {
18 | release {
19 | isMinifyEnabled = false
20 | proguardFiles(
21 | getDefaultProguardFile("proguard-android-optimize.txt"),
22 | "proguard-rules.pro"
23 | )
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility = JavaVersion.VERSION_1_8
28 | targetCompatibility = JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = "1.8"
32 | }
33 | }
34 |
35 | dependencies {
36 |
37 | implementation(libs.core.ktx)
38 | implementation(libs.appcompat)
39 | implementation(libs.material)
40 | testImplementation(libs.junit)
41 | androidTestImplementation(libs.androidx.test.ext.junit)
42 | androidTestImplementation(libs.espresso.core)
43 | }
--------------------------------------------------------------------------------
/smart/detect/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MartinKayJr/AutoCheckinPlugin/8a819239466a66d5c84aad83e716e949eac621ad/smart/detect/consumer-rules.pro
--------------------------------------------------------------------------------
/smart/detect/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/smart/detect/src/androidTest/java/cn/martinkay/detect/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package cn.martinkay.detect
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("cn.martinkay.detect.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/smart/detect/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |