Skip to content

Commit 6b980fd

Browse files
Fix dex2oat fails on System Apps due to injection hardening (ThePedroo#10)
* Fix dex2oat fails on System Apps due to denylist parsing * Update abx parser. - Fix mem leaks. - Enhance parser code size and memory footprint. - Update Parser Attribution. Co-authored-by: frknkrc44 <[email protected]> * Update parser usage as per examples --------- Co-authored-by: frknkrc44 <[email protected]>
1 parent ee13fb1 commit 6b980fd

File tree

11 files changed

+524
-39
lines changed

11 files changed

+524
-39
lines changed

daemon/src/main/java/org/lsposed/lspd/service/DenylistManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525

2626
public class DenylistManager {
2727
native static boolean isInDenylist(String processName);
28+
native static boolean isInDenylistFromClasspathDir(String classpathDir);
2829
}

daemon/src/main/java/org/lsposed/lspd/service/Dex2OatService.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.lsposed.lspd.ILSPManagerService.DEX2OAT_SEPOLICY_INCORRECT;
2727

2828
import static org.lsposed.lspd.service.DenylistManager.isInDenylist;
29+
import static org.lsposed.lspd.service.DenylistManager.isInDenylistFromClasspathDir;
2930

3031
import android.net.LocalServerSocket;
3132
import android.os.Build;
@@ -273,51 +274,50 @@ public void run() {
273274
Log.d(TAG, "Sent fd of " + dex2oatArray[id]);
274275
} else if (op == 2) {
275276
/* INFO: Check if process is in denylist */
276-
int processNameLen = readInt(is);
277-
if (processNameLen < 0) {
278-
Log.w(TAG, "Dex2oat wrapper daemon received invalid process name length: " + processNameLen);
277+
int classpathDirLen = readInt(is);
278+
if (classpathDirLen < 0) {
279+
Log.w(TAG, "Dex2oat wrapper daemon received invalid classpath dir length: " + classpathDirLen);
279280

280281
client.close();
281282

282283
continue;
283284
}
284285

285-
Log.d(TAG, "Received process name length: " + processNameLen);
286-
if (processNameLen < 0 || processNameLen > 1024) {
287-
Log.w(TAG, "Dex2oat wrapper daemon received invalid process name length: " + processNameLen);
286+
Log.d(TAG, "Received classpath dir length: " + classpathDirLen);
287+
if (classpathDirLen < 0 || classpathDirLen > 1024) {
288+
Log.w(TAG, "Dex2oat wrapper daemon received invalid classpath dir length: " + classpathDirLen);
288289

289290
client.close();
290291

291292
continue;
292293
}
293294

294-
byte[] processNameBytes = new byte[processNameLen];
295-
int bytesRead = is.read(processNameBytes);
296-
if (bytesRead != processNameLen) {
295+
byte[] classpathDirBytes = new byte[classpathDirLen];
296+
int bytesRead = is.read(classpathDirBytes);
297+
if (bytesRead != classpathDirLen) {
297298
Log.w(TAG, "Dex2oat wrapper daemon received incomplete process name. Expected: "
298-
+ processNameLen + ", Read: " + bytesRead);
299+
+ classpathDirLen + ", Read: " + bytesRead);
299300

300301
client.close();
301302

302303
continue;
303304
}
304305

305-
String processName = new String(processNameBytes);
306-
if (processName.isEmpty() && processNameLen > 0) {
307-
Log.w(TAG, "Dex2oat wrapper daemon received empty process name despite reported length: " + processNameLen);
306+
String classpathDirArg = new String(classpathDirBytes);
307+
if (classpathDirArg.isEmpty() && classpathDirLen > 0) {
308+
Log.w(TAG, "Dex2oat wrapper daemon received empty process name despite reported length: " + classpathDirLen);
308309

309310
client.close();
310311

311312
continue;
312313
}
313314

314-
Log.d(TAG, "Received process name: " + processName);
315+
Log.d(TAG, "Received classpath dir: " + classpathDirArg);
315316

316-
boolean isDenied = isInDenylist(processName);
317+
boolean isDenied = isInDenylistFromClasspathDir(classpathDirArg);
317318

318319
writeInt(os, isDenied ? 1 : 0);
319-
320-
Log.d(TAG, "Process " + processName + " is "
320+
Log.d(TAG, "Process is "
321321
+ (isDenied ? "denied" : "allowed") + " for injected dex2oat");
322322
} else {
323323
Log.w(TAG, "Dex2oat wrapper daemon received unknown operation: " + op);

daemon/src/main/jni/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(SOURCES
88
denylist.cpp
99
logcat.cpp
1010
obfuscation.cpp
11+
packagename.cpp
1112
)
1213

1314
add_library(${PROJECT_NAME} SHARED ${SOURCES})
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include <cstring>
2+
#include <iostream>
3+
#include <map>
4+
#include <memory>
5+
#include <vector>
6+
7+
#include "const.h"
8+
#include "xml_element.hpp"
9+
10+
/*
11+
* This decoder is made by frknkrc44.
12+
*
13+
* Thanks DanGLES3 and fatalcoder524 to find memory leaks.
14+
*/
15+
16+
class AbxDecoder {
17+
public:
18+
AbxDecoder(std::vector<char> str) {
19+
mInput = str;
20+
}
21+
22+
bool parse() {
23+
if (!isAbx())
24+
return false;
25+
26+
std::cerr << "ABX file found" << std::endl;
27+
28+
docOpen = false;
29+
rootClosed = false;
30+
internedStrings.clear();
31+
elementStack.clear();
32+
33+
while (true) {
34+
char event = readByte();
35+
int tType = event & 0x0f;
36+
int dType = event & 0xf0;
37+
38+
// std::cout << "tType: " << tType << " dType: " << dType << std::endl;
39+
40+
switch (tType) {
41+
case TOKEN_ATTRIBUTE: {
42+
auto attrName = readInternedString();
43+
std::vector<char> value;
44+
45+
switch (dType) {
46+
case DATA_NULL: {
47+
const char* chr = "null";
48+
value.insert(value.begin(), chr, chr + strlen(chr));
49+
goto finishReadAttr;
50+
}
51+
case DATA_BOOLEAN_FALSE: {
52+
const char* chr = "false";
53+
value.insert(value.begin(), chr, chr + strlen(chr));
54+
goto finishReadAttr;
55+
}
56+
case DATA_BOOLEAN_TRUE: {
57+
const char* chr = "true";
58+
value.insert(value.begin(), chr, chr + strlen(chr));
59+
goto finishReadAttr;
60+
}
61+
case DATA_STRING:
62+
case DATA_BYTES_HEX:
63+
case DATA_BYTES_BASE64: {
64+
value = readString();
65+
goto finishReadAttr;
66+
}
67+
case DATA_STRING_INTERNED: {
68+
value = readInternedString();
69+
goto finishReadAttr;
70+
}
71+
case DATA_INT:
72+
case DATA_INT_HEX:
73+
case DATA_FLOAT: {
74+
value = readFromCurPos(4);
75+
goto finishReadAttr;
76+
}
77+
case DATA_LONG:
78+
case DATA_LONG_HEX:
79+
case DATA_DOUBLE: {
80+
value = readFromCurPos(8);
81+
goto finishReadAttr;
82+
}
83+
}
84+
85+
finishReadAttr:
86+
elementStack.back()->pushAttribute(
87+
attrName, std::make_shared<XMLAttribute>(tType, value));
88+
continue;
89+
}
90+
case TOKEN_START_DOCUMENT: {
91+
docOpen = true;
92+
continue;
93+
}
94+
case TOKEN_END_DOCUMENT: {
95+
docOpen = false;
96+
continue;
97+
}
98+
case TOKEN_START_TAG: {
99+
auto tagName = readInternedString();
100+
addElementToStack(std::make_shared<XMLElement>(tagName));
101+
continue;
102+
}
103+
case TOKEN_END_TAG: {
104+
auto tagName = readInternedString();
105+
auto lastTagName = elementStack.back()->mTagName.data();
106+
if (strcmp(tagName.data(), lastTagName) != 0) {
107+
std::cerr << "Mismatching tags " << tagName.data() << " - " << lastTagName << std::endl;
108+
}
109+
110+
if (elementStack.size() == 1) {
111+
root = std::move(elementStack.back());
112+
docOpen = false;
113+
rootClosed = true;
114+
goto breakLoopSuccess;
115+
}
116+
117+
elementStack.pop_back();
118+
continue;
119+
}
120+
case TOKEN_TEXT:
121+
case TOKEN_CDSECT:
122+
case TOKEN_PROCESSING_INSTRUCTION:
123+
case TOKEN_COMMENT:
124+
case TOKEN_DOCDECL:
125+
case TOKEN_IGNORABLE_WHITESPACE: {
126+
auto readVal = readString();
127+
elementStack.back()->textSections.emplace_back(
128+
std::make_shared<XMLAttribute>(tType, readVal));
129+
continue;
130+
}
131+
default:
132+
std::cerr << "Unimplemented type " << (tType >> 4) << " " << dType << std::endl;
133+
return false;
134+
}
135+
136+
breakLoopSuccess:
137+
return true;
138+
}
139+
}
140+
141+
std::shared_ptr<XMLElement> root;
142+
143+
private:
144+
int curPos = 0;
145+
std::vector<char> mInput;
146+
std::vector<std::vector<char>> internedStrings;
147+
std::vector<std::shared_ptr<XMLElement>> elementStack;
148+
bool docOpen = false, rootClosed = false;
149+
const std::vector<char> emptyString;
150+
151+
std::vector<char> readFromCurPos(int len) {
152+
// std::cout << "Reading " << len << " bytes of data from " << curPos << std::endl;
153+
std::vector ret(mInput.begin() + curPos, mInput.begin() + curPos + len);
154+
curPos += len;
155+
return ret;
156+
}
157+
158+
bool isAbx() {
159+
// maybe empty?
160+
if (mInput.size() < 5) return false;
161+
162+
curPos = 0;
163+
std::vector<char> headerV = readFromCurPos(4);
164+
const char* header = reinterpret_cast<const char*>(headerV.data());
165+
return memcmp(header, startMagic, 4) == 0;
166+
}
167+
168+
char readByte() {
169+
return readFromCurPos(1)[0];
170+
}
171+
172+
short readShort() {
173+
std::vector<char> off = readFromCurPos(2);
174+
return ((unsigned short) off[0] << 8) | ((unsigned char) off[1]);
175+
}
176+
177+
std::vector<char> readString() {
178+
short len = readShort();
179+
if (len < 1) {
180+
return emptyString;
181+
}
182+
183+
auto ret = readFromCurPos(len);
184+
ret.emplace_back(0);
185+
return ret;
186+
}
187+
188+
std::vector<char> readInternedString() {
189+
short idx = readShort();
190+
if (idx < 0) {
191+
std::vector<char> str = readString();
192+
internedStrings.emplace_back(str);
193+
return str;
194+
}
195+
196+
auto internedStr = internedStrings.begin();
197+
std::advance(internedStr, idx);
198+
return *internedStr;
199+
}
200+
201+
void addElementToStack(std::shared_ptr<XMLElement> element) {
202+
if (elementStack.size() > 0) {
203+
auto lastElement = elementStack.back().get();
204+
lastElement->subElements.emplace_back(element);
205+
}
206+
207+
elementStack.emplace_back(element);
208+
}
209+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
/*
4+
* This decoder is made by frknkrc44.
5+
*
6+
* Thanks DanGLES3 and fatalcoder524 to find memory leaks.
7+
*/
8+
9+
const char startMagic[4] = { 'A', 'B', 'X', '\0' };
10+
11+
static const short TOKEN_START_DOCUMENT = 0;
12+
static const short TOKEN_END_DOCUMENT = 1;
13+
static const short TOKEN_START_TAG = 2;
14+
static const short TOKEN_END_TAG = 3;
15+
static const short TOKEN_TEXT = 4;
16+
static const short TOKEN_CDSECT = 5;
17+
// static const short TOKEN_ENTITY_REF = 6;
18+
static const short TOKEN_IGNORABLE_WHITESPACE = 7;
19+
static const short TOKEN_PROCESSING_INSTRUCTION = 8;
20+
static const short TOKEN_COMMENT = 9;
21+
static const short TOKEN_DOCDECL = 10;
22+
static const short TOKEN_ATTRIBUTE = 15;
23+
24+
static const short DATA_NULL = 1 << 4;
25+
static const short DATA_STRING = 2 << 4;
26+
static const short DATA_STRING_INTERNED = 3 << 4;
27+
static const short DATA_BYTES_HEX = 4 << 4;
28+
static const short DATA_BYTES_BASE64 = 5 << 4;
29+
static const short DATA_INT = 6 << 4;
30+
static const short DATA_INT_HEX = 7 << 4;
31+
static const short DATA_LONG = 8 << 4;
32+
static const short DATA_LONG_HEX = 9 << 4;
33+
static const short DATA_FLOAT = 10 << 4;
34+
static const short DATA_DOUBLE = 11 << 4;
35+
static const short DATA_BOOLEAN_TRUE = 12 << 4;
36+
static const short DATA_BOOLEAN_FALSE = 13 << 4;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <vector>
2+
3+
/*
4+
* This decoder is made by frknkrc44.
5+
*
6+
* Thanks DanGLES3 and fatalcoder524 to find memory leaks.
7+
*/
8+
9+
class XMLAttribute {
10+
public:
11+
std::vector<char> mValue;
12+
int mDataType;
13+
XMLAttribute(int dataType, std::vector<char> value) {
14+
mDataType = dataType;
15+
mValue.insert(mValue.begin(), value.begin(), value.end());
16+
}
17+
};

0 commit comments

Comments
 (0)