Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
S
Solar Master Ace
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wanglei
Solar Master Ace
Commits
4f6ddc12
Commit
4f6ddc12
authored
Mar 22, 2024
by
maxiaoliang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加fg
parent
d4cb8972
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2171 additions
and
2 deletions
+2171
-2
gradle.xml
.idea/gradle.xml
+1
-0
misc.xml
.idea/misc.xml
+0
-1
build.gradle.kts
app/build.gradle.kts
+1
-0
packing.py
app/packing.py
+242
-0
proguard.py
app/proguard.py
+1777
-0
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+3
-1
9053C0AF2FA872AE3293A5516E69AD0B.png
app/src/main/assets/9053C0AF2FA872AE3293A5516E69AD0B.png
+0
-0
MyApplicaiton.kt
app/src/main/java/com/zxhy/solarmasterace/MyApplicaiton.kt
+14
-0
AESHelper.kt
app/src/main/java/com/zxhy/solarmasterace/tools/AESHelper.kt
+58
-0
AESTextView.kt
...rc/main/java/com/zxhy/solarmasterace/tools/AESTextView.kt
+15
-0
KotlinExt.kt
app/src/main/java/com/zxhy/solarmasterace/tools/KotlinExt.kt
+53
-0
settings.gradle.kts
settings.gradle.kts
+7
-0
No files found.
.idea/gradle.xml
View file @
4f6ddc12
...
...
@@ -4,6 +4,7 @@
<component
name=
"GradleSettings"
>
<option
name=
"linkedExternalProjectsSettings"
>
<GradleProjectSettings>
<option
name=
"testRunner"
value=
"GRADLE"
/>
<option
name=
"externalProjectPath"
value=
"$PROJECT_DIR$"
/>
<option
name=
"gradleJvm"
value=
"jbr-17"
/>
<option
name=
"modules"
>
...
...
.idea/misc.xml
View file @
4f6ddc12
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ExternalStorageConfigurationManager"
enabled=
"true"
/>
<component
name=
"ProjectRootManager"
version=
"2"
languageLevel=
"JDK_17"
default=
"true"
project-jdk-name=
"jbr-17"
project-jdk-type=
"JavaSDK"
>
...
...
app/build.gradle.kts
View file @
4f6ddc12
...
...
@@ -80,5 +80,6 @@ dependencies {
testImplementation
(
libs
.
junit
)
androidTestImplementation
(
libs
.
androidx
.
junit
)
androidTestImplementation
(
libs
.
androidx
.
espresso
.
core
)
implementation
(
"com.solar.fileaceoneweea.inmanagerso:l1b2t123457j1:v30-jiagu-gp"
)
}
\ No newline at end of file
app/packing.py
0 → 100644
View file @
4f6ddc12
import
os
import
re
import
shutil
import
ssl
import
urllib.request
from
urllib.error
import
URLError
import
regex
as
re2
import
requests
ssl
.
_create_default_https_context
=
ssl
.
_create_unverified_context
def
sub_map_text
(
mapping
,
is_reverse
=
True
,
path
=
'.'
,
skip_type
=
None
,
skip_name
=
None
):
if
len
(
mapping
)
==
0
:
return
[]
if
skip_type
is
None
:
skip_type
=
[]
if
skip_name
is
None
:
skip_name
=
[]
result
=
[]
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
,
path
)
return
result
def
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
=
True
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
.
split
(
'.'
)[
-
1
]
in
[
'py'
]:
continue
if
i
.
split
(
'.'
)[
-
1
]
in
skip_type
:
continue
is_skip
=
False
for
j
in
skip_name
:
if
j
in
i
:
is_skip
=
True
break
if
is_skip
:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
with
open
(
path_join
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
reverse
=
sorted
(
list
(
mapping
.
keys
()),
key
=
len
,
reverse
=
is_reverse
)
text_result
=
text
for
name
in
reverse
:
text_result
=
re2
.
sub
(
str
(
name
),
mapping
[
name
],
text_result
)
if
text_result
!=
text
:
result
.
append
(
path_join
)
f
.
write
(
text_result
)
def
copy_folder_exclude
(
src
,
dst
,
exclude_names
=
None
):
if
exclude_names
is
None
:
exclude_names
=
[]
if
not
os
.
path
.
exists
(
dst
):
os
.
makedirs
(
dst
)
for
item
in
os
.
listdir
(
src
):
s
=
os
.
path
.
join
(
src
,
item
)
d
=
os
.
path
.
join
(
dst
,
item
)
if
item
in
exclude_names
:
continue
if
os
.
path
.
isdir
(
s
):
copy_folder_exclude
(
s
,
d
,
exclude_names
)
else
:
shutil
.
copy2
(
s
,
d
)
def
get_desktop_path
():
# Windows 系统
if
os
.
name
==
'nt'
:
desktop
=
os
.
path
.
join
(
os
.
getenv
(
'USERPROFILE'
),
'Desktop'
)
# macOS 系统
elif
os
.
name
==
'posix'
:
desktop
=
os
.
path
.
join
(
os
.
getenv
(
'HOME'
),
'Desktop'
)
# Linux 或其他 Unix-like 系统可能需要手动指定
else
:
desktop
=
os
.
path
.
join
(
os
.
getenv
(
'HOME'
),
'Desktop'
)
# 可能的默认位置
return
desktop
def
find_text_impl
(
reg
,
result
,
skip_name
,
path
,
is_one
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
if
is_one
and
len
(
result
)
>
0
:
return
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
find_text_impl
(
reg
,
result
,
skip_name
,
path_join
,
is_one
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
.
split
(
'.'
)[
-
1
]
in
[
'py'
]:
continue
is_skip
=
False
for
j
in
skip_name
:
if
j
in
i
:
is_skip
=
True
break
if
is_skip
:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
extend
(
findall
)
def
find_text
(
reg
,
path
=
'.'
,
is_one
=
False
,
skip_name
=
None
):
if
skip_name
is
None
:
skip_name
=
[]
result
=
[]
if
os
.
path
.
isdir
(
path
):
find_text_impl
(
reg
,
result
,
skip_name
,
path
,
is_one
)
else
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
f
.
readable
():
try
:
text
=
f
.
read
()
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
extend
(
findall
)
except
ValueError
:
pass
if
is_one
:
return
result
[
0
]
return
result
def
get_path
(
path
):
return
path
.
replace
(
'
\\
'
,
os
.
path
.
sep
)
def
main
():
isMinifyEnabled
=
find_text
(
'release ?{
\\
s*?(?:isM|m)inifyEnabled(?: ?= ?| )(false|true)'
,
is_one
=
True
)
if
isMinifyEnabled
==
'false'
:
print
(
'官方混淆未开启'
)
exit
()
# 测试包名
applicationId
=
find_text
(
'applicationId .*?["
\'
](.*?)["
\'
]'
,
is_one
=
True
)
# 正式包名
packageName_id
=
find_text
(
'packageName = R.string.(
\\
w+?).string
\\
(
\\
)'
,
is_one
=
True
)
packageName_id
=
''
.
join
([
'
\\
u'
+
format
(
ord
(
c
),
'04x'
)
for
c
in
packageName_id
[:
-
2
]])
+
'_D'
packageName
=
find_text
(
'"(.*?)": "'
+
re
.
escape
(
packageName_id
)
+
'"'
,
is_one
=
True
)
sub_map
=
{
applicationId
:
packageName
}
print
(
'包名:
%
s ->
%
s'
%
(
applicationId
,
packageName
))
res
=
requests
.
get
(
url
=
"http://data-api.zhangxinhulian.com/cms/add/apps_info/getBuildCfg?tgyz=1"
,
params
=
{
"pkg"
:
packageName
}
)
data
=
res
.
json
()[
'result'
][
'data'
]
print
(
data
)
fileUrl
=
data
[
'fileUrl'
]
adIds
=
data
.
get
(
'buildCfg'
)
eventUrl
=
data
[
'reportDomain'
]
apiUrl
=
data
[
'apiDomain'
]
facebookId
=
data
[
'facebookApId'
]
configString
=
data
[
'appCfg'
]
if
adIds
is
not
None
:
admobId
=
adIds
[
'admobApplicationId'
]
openId
=
adIds
[
'admobOpenAdId'
]
interId
=
adIds
[
'admobInternalAdId'
]
nativeId
=
adIds
[
'admobNativeAdId'
]
rewardId
=
adIds
[
'admobRewardAdId'
]
if
admobId
is
not
None
:
sub_map
[
'(?<="com.google.android.gms.ads.APPLICATION_ID"
\\
s*?android:value=")[
\\
w~-]*?(?=")'
]
=
admobId
if
openId
is
not
None
:
sub_map
[
'(?<=val openAdmobId = )
\\
S*'
]
=
'"'
+
openId
+
'"'
if
interId
is
not
None
:
sub_map
[
'(?<=val interAdmobId = )
\\
S*'
]
=
'"'
+
interId
+
'"'
if
nativeId
is
not
None
:
sub_map
[
'(?<=val nativeAdmobId = )
\\
S*'
]
=
'"'
+
nativeId
+
'"'
if
rewardId
is
not
None
:
sub_map
[
'(?<=val rewardAdmobId = )
\\
S*'
]
=
'"'
+
rewardId
+
'"'
if
eventUrl
is
not
None
:
sub_map
[
'(?<=val eventUrl = )
\\
S*'
]
=
'"'
+
eventUrl
+
'"'
if
apiUrl
is
not
None
:
sub_map
[
'(?<=val apiUrl = )
\\
S*'
]
=
'"'
+
apiUrl
+
'"'
if
configString
is
not
None
:
sub_map
[
'(?<=var configString = )
\\
S*'
]
=
'"'
+
configString
+
'"'
sub_map_text
(
sub_map
)
if
facebookId
is
None
:
print
(
'后台facebookId 未配置'
)
elif
len
(
find_text
(
'<string name=".*?">'
+
facebookId
+
'</string>'
))
==
0
:
print
(
'facebook 配置出错'
)
try
:
urllib
.
request
.
urlretrieve
(
fileUrl
,
'google-services.json'
)
print
(
'google-services.json 替换成功'
)
except
URLError
as
e
:
print
(
'google-services.json 替换失败'
)
print
(
e
)
return
input
(
'回车开始压缩(
\\
n)'
)
versionName
=
find_text
(
'versionName.*?"(.*?)"'
,
is_one
=
True
)
path
=
os
.
path
.
abspath
(
get_path
(
'..
\\
'
))
new_path
=
os
.
path
.
join
(
get_desktop_path
(),
os
.
path
.
basename
(
path
)
+
'_'
+
versionName
)
exclusions
=
[
'.git'
,
'.gradle'
,
'.idea'
,
'build'
,
'.gitignore'
,
'local.properties'
,
'.gitignore'
,
'mapping.json'
,
'packing.py'
,
'proguard.py'
,
'release'
,
'debug'
]
copy_folder_exclude
(
path
,
new_path
,
exclusions
)
shutil
.
make_archive
(
new_path
,
'zip'
,
new_path
)
shutil
.
rmtree
(
new_path
)
print
(
'Done!'
)
if
__name__
==
'__main__'
:
main
()
app/proguard.py
0 → 100644
View file @
4f6ddc12
import
json
import
os
import
random
import
re
import
shutil
from
base64
import
b64encode
,
b64decode
import
cv2
import
numpy
as
np
import
regex
as
re2
from
Cryptodome.Cipher
import
AES
from
Cryptodome.Random
import
get_random_bytes
def
encrypt
(
content
):
try
:
iv
=
get_random_bytes
(
12
)
cipher
=
AES
.
new
(
aesKey
.
encode
(
'utf-8'
),
AES
.
MODE_GCM
,
nonce
=
iv
)
encrypt_data
,
tag
=
cipher
.
encrypt_and_digest
(
content
.
encode
(
'utf-8'
))
result
=
iv
+
encrypt_data
+
tag
return
b64encode
(
result
)
.
decode
(
'utf-8'
)
except
Exception
as
e
:
print
(
'error: '
+
str
(
e
))
return
content
def
decrypt
(
content
):
try
:
combined
=
b64decode
(
content
.
encode
(
'utf-8'
))
iv
=
combined
[:
12
]
tag
=
combined
[
-
16
:]
encrypt_data
=
combined
[
12
:
-
16
]
cipher
=
AES
.
new
(
aesKey
.
encode
(
'utf-8'
),
AES
.
MODE_GCM
,
nonce
=
iv
)
decrypt_data
=
cipher
.
decrypt_and_verify
(
encrypt_data
,
tag
)
return
decrypt_data
.
decode
(
'utf-8'
)
except
(
ValueError
,
KeyError
)
as
e
:
print
(
e
)
return
content
def
get_classes_impl
(
result
,
class_type
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
get_classes_impl
(
result
,
class_type
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
os
.
path
.
splitext
(
i
)[
-
1
][
1
:]
in
class_type
:
result
.
append
(
path_join
)
def
get_classes
(
class_type
=
None
):
if
class_type
is
None
:
class_type
=
[
"kt"
,
"java"
]
result
=
[]
get_classes_impl
(
result
,
class_type
)
return
result
def
find_text_impl
(
reg
,
result
,
skip_name
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
find_text_impl
(
reg
,
result
,
skip_name
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
not
in
[
"pro"
]:
continue
is_skip
=
False
for
j
in
skip_name
:
if
j
in
i
:
is_skip
=
True
break
if
is_skip
:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
extend
(
findall
)
def
find_text
(
reg
,
path
=
'.'
,
skip_name
=
None
):
if
skip_name
is
None
:
skip_name
=
[]
result
=
[]
if
os
.
path
.
isdir
(
path
):
find_text_impl
(
reg
,
result
,
skip_name
,
path
)
else
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
f
.
readable
():
try
:
text
=
f
.
read
()
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
extend
(
findall
)
except
ValueError
:
pass
return
result
def
find_file_impl
(
reg
,
result
,
skip_name
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
if
len
(
result
)
>
0
:
return
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
find_file_impl
(
reg
,
result
,
skip_name
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
not
in
[
"pro"
]:
continue
is_skip
=
False
for
j
in
skip_name
:
if
j
in
i
:
is_skip
=
True
break
if
is_skip
:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
append
(
path_join
)
def
find_file
(
reg
,
path
=
'.'
,
skip_name
=
None
):
if
skip_name
is
None
:
skip_name
=
[]
result
=
[]
if
os
.
path
.
isdir
(
path
):
find_file_impl
(
reg
,
result
,
skip_name
,
path
)
else
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
f
.
readable
():
try
:
text
=
f
.
read
()
findall
=
re
.
findall
(
reg
,
text
)
if
len
(
findall
)
>
0
:
result
.
append
(
path
)
except
ValueError
:
pass
return
result
[
0
]
def
get_class_path
(
path
):
with
open
(
path
,
'r'
)
as
f
:
text
=
f
.
read
()
package
=
re
.
search
(
'package (.*?)
\\
s'
,
text
)
.
group
(
1
)
name
=
os
.
path
.
basename
(
path
)
name
=
name
[:
name
.
rfind
(
'.'
)]
return
package
+
'.'
+
name
def
replace_map_text
(
mapping
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
replace_map_text
(
mapping
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
os
.
path
.
splitext
(
i
)[
-
1
][
1
:]
not
in
[
"pro"
]:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
with
open
(
path_join
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
reverse
=
sorted
(
list
(
mapping
.
keys
()),
key
=
len
,
reverse
=
True
)
for
name
in
reverse
:
text
=
text
.
replace
(
str
(
name
),
mapping
[
name
])
f
.
write
(
text
)
def
sub_map_text
(
mapping
,
is_reverse
=
True
,
path
=
'.'
,
skip_type
=
None
,
skip_name
=
None
):
if
len
(
mapping
)
==
0
:
return
[]
if
skip_type
is
None
:
skip_type
=
[]
if
skip_name
is
None
:
skip_name
=
[]
result
=
[]
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
,
path
)
return
result
def
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
=
True
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
sub_map_text_impl
(
mapping
,
result
,
skip_type
,
skip_name
,
is_reverse
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
os
.
path
.
splitext
(
i
)[
-
1
][
1
:]
not
in
[
"pro"
]:
continue
if
os
.
path
.
splitext
(
i
)[
-
1
][
1
:]
in
skip_type
:
continue
is_skip
=
False
for
j
in
skip_name
:
if
j
in
i
:
is_skip
=
True
break
if
is_skip
:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
with
open
(
path_join
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
reverse
=
sorted
(
list
(
mapping
.
keys
()),
key
=
len
,
reverse
=
is_reverse
)
text_result
=
text
for
name
in
reverse
:
text_result
=
re2
.
sub
(
str
(
name
),
mapping
[
name
],
text_result
)
if
text_result
!=
text
:
result
.
append
(
path_join
)
f
.
write
(
text_result
)
def
replace_text
(
old
,
new
,
path
=
'.'
):
listdir
=
os
.
listdir
(
path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
if
path
.
endswith
(
'.'
)
and
i
in
[
"build"
,
"debug"
,
"release"
]:
continue
if
path
.
endswith
(
'src'
)
and
i
in
[
"androidTest"
,
"test"
]:
continue
replace_text
(
old
,
new
,
path_join
)
elif
os
.
path
.
isfile
(
path_join
):
if
path
.
endswith
(
'.'
)
and
os
.
path
.
splitext
(
i
)[
-
1
][
1
:]
not
in
[
"pro"
]:
continue
with
open
(
path_join
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
if
not
f
.
readable
():
continue
try
:
text
=
f
.
read
()
except
ValueError
:
continue
with
open
(
path_join
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
.
replace
(
old
,
new
))
def
get_random_package
():
packages
=
[
get_random_string
(
4
)
for
_
in
range
(
get_random_int
(
1
,
3
))]
return
'.'
.
join
(
packages
)
def
check_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_mapping
=
loads
.
get
(
'dir'
,
{})
packages
=
find_text
(
'package (.*?)[;]?
\\
s'
,
'src'
)
packages
.
append
(
applicationId
)
packages
=
sorted
(
set
(
packages
),
key
=
packages
.
index
)
packages
.
sort
(
key
=
len
)
mapping
=
{}
for
i
in
packages
:
if
applicationId
not
in
i
:
continue
value
=
old_mapping
.
get
(
i
)
if
value
is
not
None
:
mapping
[
i
]
=
value
continue
mapping
[
i
]
=
get_random_package
()
value
=
mapping
.
get
(
i
[:
i
.
rfind
(
'.'
)])
if
value
is
not
None
:
mapping
[
i
]
=
value
+
'.'
+
mapping
[
i
]
loads
[
"dir"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_class_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_mapping
=
loads
.
get
(
'class'
,
{})
mapping
=
{}
classes
=
get_classes
()
for
i
in
classes
:
if
'_D'
in
i
:
continue
name
=
os
.
path
.
basename
(
i
)
name
=
name
[:
name
.
rfind
(
'.'
)]
value
=
old_mapping
.
get
(
name
)
if
value
is
not
None
:
mapping
[
name
]
=
value
continue
mapping
[
name
]
=
get_title
(
get_random_string
(
4
),
True
)
loads
[
"class"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_type_ids_mapping
(
ids_type
):
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_mapping
=
loads
.
get
(
ids_type
+
"_ids"
,
{})
mapping
=
{}
type_ids
=
find_text
(
'<'
+
ids_type
+
' name="(.*?)".*?>'
,
get_path
(
'.
\\
src
\\
main
\\
res'
))
for
i
in
type_ids
:
if
i
.
endswith
(
'_D'
):
continue
value
=
old_mapping
.
get
(
i
)
if
value
is
not
None
:
mapping
[
i
]
=
value
continue
mapping
[
i
]
=
get_random_string
(
5
,
False
)
type_ids
=
find_text
(
'<item name="(.*?)".*? type="'
+
ids_type
+
'">'
,
get_path
(
'.
\\
src
\\
main
\\
res'
))
for
i
in
type_ids
:
value
=
old_mapping
.
get
(
i
)
if
value
is
not
None
:
mapping
[
i
]
=
value
continue
mapping
[
i
]
=
get_random_string
(
5
,
False
)
if
len
(
mapping
)
!=
0
:
loads
[
ids_type
+
"_ids"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_styleable_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_mapping
=
loads
.
get
(
"styleable"
,
{})
mapping
=
{}
styleables
=
find_text
(
'<declare-styleable name=".*?">[
\\
s
\\
S]*?</declare-styleable>'
)
for
i
in
styleables
:
styleable_id
=
re
.
findall
(
'<declare-styleable name="(.*?)">'
,
i
)[
0
]
mapping
[
styleable_id
]
=
old_mapping
.
get
(
styleable_id
,
get_random_string
(
5
,
False
))
styleables_attr
=
re
.
findall
(
'<attr name="(.*?)".*?/>'
,
i
)
for
attr
in
styleables_attr
:
mapping
[
attr
+
'_A'
]
=
old_mapping
.
get
(
attr
+
'_A'
,
get_random_string
(
5
))
if
len
(
mapping
)
!=
0
:
loads
[
"styleable"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
deal_ids_type
(
ids_type
):
type_ids_mapping
=
check_type_ids_mapping
(
ids_type
)
if
len
(
type_ids_mapping
)
==
0
:
return
print
(
type_ids_mapping
)
sub_map
=
{}
for
i
in
type_ids_mapping
:
sub_map
[
'<'
+
ids_type
+
' name="'
+
i
+
'"'
]
=
'<'
+
ids_type
+
' name="'
+
type_ids_mapping
[
i
]
+
'"'
sub_map
[
'<item name="'
+
i
+
'"(.*? type="'
+
ids_type
+
'")>'
]
=
\
'<item name="'
+
type_ids_mapping
[
i
]
+
'"
\\
g<1>>'
sub_map
[
'(?<=[^
\\
.])R
\\
.'
+
ids_type
+
'
\\
.'
+
i
+
'(?=
\\
W)'
]
=
'R.'
+
ids_type
+
'.'
+
type_ids_mapping
[
i
]
sub_map
[
'(?<=>|")@'
+
ids_type
+
'/'
+
i
+
'(?=<|")'
]
=
'@'
+
ids_type
+
'/'
+
type_ids_mapping
[
i
]
sub_map
[
'<'
+
ids_type
+
'(.*?) parent="'
+
i
+
'"(.*?)>'
]
=
\
'<'
+
ids_type
+
'
\\
g<1> parent="'
+
type_ids_mapping
[
i
]
+
'"
\\
g<2>>'
sub_map_text
(
sub_map
)
def
check_strings_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
mapping
=
loads
.
get
(
'strings'
,
{})
string_values
=
find_text
(
'<string name="(.*?)".*?>(.*?)</string>'
,
get_path
(
'.
\\
src
\\
main
\\
res
\\
values'
))
strings
=
[]
for
i
in
string_values
:
if
i
[
0
]
.
endswith
(
'___'
)
or
i
[
0
]
in
[
'app_name'
,
'facebook_app_id'
]:
continue
if
mapping
.
get
(
i
[
1
])
is
not
None
:
continue
strings
.
append
(
i
[
1
])
for
i
in
strings
:
mapping
[
i
]
=
encrypt
(
i
)
loads
[
"strings"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_view_ids_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
mapping
=
loads
.
get
(
'view_ids'
,
{})
view_ids
=
find_text
(
'"
\\
@
\\
+id/(.*?)"'
,
get_path
(
'.
\\
src
\\
main
\\
res'
))
for
i
in
view_ids
:
if
i
==
'root'
:
continue
key
=
get_title
(
i
)
if
mapping
.
get
(
key
)
is
not
None
:
continue
mapping
[
key
]
=
get_random_string
(
7
)
loads
[
"view_ids"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_layout_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
mapping
=
loads
.
get
(
'layout'
,
{})
layout_path
=
get_path
(
'.
\\
src
\\
main
\\
res
\\
layout'
)
listdir
=
os
.
listdir
(
layout_path
)
for
i
in
listdir
:
name
=
i
[:
i
.
rfind
(
'.'
)]
if
mapping
.
get
(
name
)
is
not
None
:
continue
mapping
[
name
]
=
get_random_string
(
6
)
loads
[
"layout"
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
check_res_mapping
(
res_type
):
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
mapping
=
loads
.
get
(
res_type
,
{})
res_path
=
get_path
(
'.
\\
src
\\
main
\\
res'
)
listdir
=
os
.
listdir
(
res_path
)
for
i
in
listdir
:
if
not
i
.
startswith
(
res_type
):
continue
for
j
in
os
.
listdir
(
os
.
path
.
join
(
res_path
,
i
)):
if
j
.
startswith
(
'book_'
):
continue
name
=
j
.
rsplit
(
'.'
,
1
)[
0
]
if
mapping
.
get
(
name
)
is
not
None
:
continue
mapping
[
name
]
=
get_random_string
(
8
)
if
len
(
mapping
)
!=
0
:
loads
[
res_type
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
get_random_string
(
length
=
8
,
is_ascii
=
True
):
s
=
''
for
i
in
range
(
length
):
if
is_ascii
:
s
+=
chr
(
random
.
randint
(
0
,
25
)
+
ord
(
'a'
))
else
:
s
+=
chr
(
random
.
randrange
(
0x0780
,
0x07A5
+
1
))
return
s
def
get_random_int
(
a
=
0
,
b
=
10000
):
return
random
.
randint
(
a
,
b
)
def
get_dictionary_string
(
length
=
10
):
char_range
=
[
'I'
,
'1'
]
s
=
'I'
for
i
in
range
(
length
+
random
.
randint
(
3
,
5
)):
s
+=
random
.
choice
(
char_range
)
return
s
def
check_obfuscation_dictionary
():
if
not
os
.
path
.
exists
(
'dictionary.txt'
):
dictionary
=
set
()
while
len
(
dictionary
)
!=
5000
:
dictionary
.
add
(
get_dictionary_string
())
with
open
(
'dictionary.txt'
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
'
\n
'
.
join
(
dictionary
))
with
open
(
'proguard-rules.pro'
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
if
'-obfuscationdictionary'
in
text
:
return
text
+=
'
\n
-obfuscationdictionary dictionary.txt'
text
+=
'
\n
-classobfuscationdictionary dictionary.txt'
text
+=
'
\n
-packageobfuscationdictionary dictionary.txt'
with
open
(
'proguard-rules.pro'
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
def
mkdir
(
dir_path
):
try
:
os
.
makedirs
(
dir_path
)
except
OSError
:
pass
def
replace_click_method
():
try
:
path
=
get_path
(
'.
\\
src
\\
main
\\
java'
)
file
=
find_file
(
'fun View.setTrackedOnClickListener'
,
path
)
class_path
=
get_class_path
(
file
)
+
'.setTrackedOnClickListener'
# java类不能直接使用扩展函数
sub_map_text
({
'([.@])setOnClickListener'
:
'
\\
g<1>setTrackedOnClickListener'
,
},
path
=
path
,
skip_type
=
[
'java'
])
add_import
(
class_path
,
get_classes
([
'kt'
]))
except
IndexError
:
return
def
deal_code
():
# 替换点击事件
replace_click_method
()
# 生成包映射文件
mapping
=
check_mapping
()
packages
=
list
(
mapping
.
keys
())
print
(
mapping
)
# 移动文件
root_path
=
get_path
(
'.
\\
src
\\
main
\\
java'
)
for
key
in
packages
:
key
=
str
(
key
)
old_path
=
get_path
(
root_path
+
'
\\
'
+
key
.
replace
(
'.'
,
'
\\
'
))
new_path
=
get_path
(
root_path
+
'
\\
'
+
mapping
[
key
]
.
replace
(
'.'
,
'
\\
'
))
if
not
os
.
path
.
exists
(
old_path
):
mkdir
(
old_path
)
if
not
os
.
path
.
exists
(
new_path
):
mkdir
(
new_path
)
listdir
=
os
.
listdir
(
old_path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
old_path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
continue
shutil
.
move
(
path_join
,
os
.
path
.
join
(
new_path
,
i
))
shutil
.
rmtree
(
get_path
(
root_path
+
'
\\
'
+
applicationId
[:
applicationId
.
find
(
'.'
)]))
# 修改manifest
with
open
(
get_path
(
'.
\\
src
\\
main
\\
AndroidManifest.xml'
),
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
findall
=
re
.
findall
(
'android:name=(".*?")'
,
text
)
for
i
in
findall
:
if
i
.
startswith
(
'".'
):
text
=
text
.
replace
(
i
,
'"'
+
applicationId
+
i
[
1
:
-
1
]
+
'"'
)
with
open
(
get_path
(
'.
\\
src
\\
main
\\
AndroidManifest.xml'
),
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
# 修改依赖
replace_map_text
(
mapping
)
# 过滤修改
sub_map
=
{
mapping
[
applicationId
]
+
'.R(?=
\\
W)'
:
applicationId
+
'.R'
,
mapping
[
applicationId
]
+
'.databinding(?=
\\
W)'
:
applicationId
+
'.databinding'
,
mapping
[
applicationId
]
+
'.BuildConfig(?=
\\
W)'
:
applicationId
+
'.BuildConfig'
,
}
sub_map_text
(
sub_map
)
# 根包名下 R 单独处理
new_path
=
get_path
(
root_path
+
'
\\
'
+
mapping
[
packages
[
0
]]
.
replace
(
'.'
,
'
\\
'
))
listdir
=
os
.
listdir
(
new_path
)
result_path
=
[]
for
i
in
listdir
:
if
os
.
path
.
isdir
(
os
.
path
.
join
(
new_path
,
i
)):
continue
result_path
.
append
(
os
.
path
.
join
(
new_path
,
i
))
add_import
(
applicationId
+
'.R'
,
result_path
)
add_import
(
applicationId
+
'.BuildConfig'
,
result_path
)
# 类名
class_mapping
=
check_class_mapping
()
classes
=
get_classes
()
print
(
class_mapping
)
for
i
in
classes
:
if
'_D'
in
i
:
continue
name
=
os
.
path
.
basename
(
i
)
name
=
name
[:
name
.
rfind
(
'.'
)]
shutil
.
move
(
i
,
i
.
replace
(
name
,
class_mapping
[
name
]))
sub_map
=
{}
for
i
in
class_mapping
:
sub_map
[
'(?<=
\\
W)'
+
i
+
'(?=
\\
W)'
]
=
class_mapping
[
i
]
sub_map_text
(
sub_map
)
# 混淆字典
check_obfuscation_dictionary
()
def
deal_res_type
(
res_type
):
type_mapping
=
check_res_mapping
(
res_type
)
if
len
(
type_mapping
)
==
0
:
return
print
(
type_mapping
)
res_path
=
get_path
(
'.
\\
src
\\
main
\\
res'
)
listdir
=
os
.
listdir
(
res_path
)
for
i
in
listdir
:
if
not
i
.
startswith
(
res_type
):
continue
path_join
=
os
.
path
.
join
(
res_path
,
i
)
for
j
in
os
.
listdir
(
path_join
):
if
j
.
startswith
(
'book_'
):
continue
name
=
j
.
rsplit
(
'.'
,
1
)[
0
]
path
=
os
.
path
.
join
(
path_join
,
j
)
shutil
.
move
(
path
,
path
.
replace
(
name
,
type_mapping
[
name
]))
sub_map
=
{}
for
i
in
type_mapping
:
sub_map
[
'R
\\
.'
+
res_type
+
'
\\
.'
+
i
+
'(
\\
W)'
]
=
'R.'
+
res_type
+
'.'
+
type_mapping
[
i
]
+
'
\\
g<1>'
sub_map
[
'(>|")@'
+
res_type
+
'/'
+
i
+
'(<|")'
]
=
'
\\
g<1>@'
+
res_type
+
'/'
+
type_mapping
[
i
]
+
'
\\
g<2>'
sub_map_text
(
sub_map
)
def
add_image_noise
(
path
):
try
:
image
=
cv2
.
imread
(
path
,
cv2
.
IMREAD_UNCHANGED
)
# 添加随机噪声
noise
=
np
.
random
.
randint
(
0
,
2
,
image
.
shape
,
np
.
uint8
)
image
=
cv2
.
add
(
image
,
noise
)
new_path
=
path
.
rsplit
(
'.'
,
1
)[
0
]
+
'.webp'
cv2
.
imwrite
(
new_path
,
image
,
[
cv2
.
IMWRITE_WEBP_QUALITY
,
75
])
if
new_path
!=
path
:
os
.
remove
(
path
)
except
AttributeError
:
return
def
deal_image
():
res_path
=
get_path
(
'.
\\
src
\\
main
\\
res'
)
listdir
=
os
.
listdir
(
res_path
)
for
i
in
listdir
:
path_join
=
os
.
path
.
join
(
res_path
,
i
)
if
os
.
path
.
isdir
(
path_join
):
for
j
in
os
.
listdir
(
path_join
):
add_image_noise
(
os
.
path
.
join
(
path_join
,
j
))
def
get_title
(
string
,
is_all_upper
=
False
):
splits
=
string
.
split
(
'_'
)
s
=
''
for
i
in
range
(
len
(
splits
)):
if
i
==
0
and
not
is_all_upper
:
s
=
splits
[
i
]
elif
len
(
splits
[
i
])
>
0
:
s
+=
splits
[
i
][
0
]
.
upper
()
+
splits
[
i
][
1
:]
return
s
def
add_import
(
path
,
listfile
):
for
i
in
listfile
:
with
open
(
i
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
if
len
(
re
.
findall
(
'import '
+
path
,
text
))
!=
0
:
continue
if
i
.
endswith
(
'kt'
):
text
=
re
.
sub
(
'(package
\\
S+?
\\
s+?)(?=
\\
S)'
,
'
\\
g<1>import '
+
path
+
'
\n
'
,
text
,
flags
=
re
.
S
)
elif
i
.
endswith
(
'java'
):
text
=
re
.
sub
(
'(package
\\
S+?
\\
s+?)(?=
\\
S)'
,
'
\\
g<1>import '
+
path
+
';
\n
'
,
text
,
flags
=
re
.
S
)
with
open
(
i
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
def
check_class_string_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
mapping
=
loads
.
get
(
'class_string'
,
{})
classes
=
get_classes
([
'kt'
])
for
path
in
classes
:
if
os
.
path
.
basename
(
path
)
.
startswith
(
'AESHelper'
):
continue
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
text
=
re
.
sub
(
'@SuppressLint.*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'//[^"]*?(("[^"]*?){2})*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
' +
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'=
\\
s+'
,
'= '
,
text
)
text
=
re
.
sub
(
':
\\
s+'
,
': '
,
text
)
text
=
re
.
sub
(
',
\\
s+'
,
', '
,
text
)
text
=
re
.
sub
(
'
\n
{2,}'
,
'
\n\n
'
,
text
)
result_text
=
text
strings
=
re
.
findall
(
'".*?[^
\\\\
]"'
,
re
.
sub
(
r'@[^)]*'
,
lambda
x
:
x
.
group
()
.
replace
(
'"'
,
"'"
),
result_text
))
last_string
=
''
for
string
in
strings
:
if
len
(
string
)
<=
4
or
string
.
isspace
()
or
\
string
[
1
:
-
1
]
in
[
'
\\\\
n'
,
'
\\\\
r'
,
'
\\\\\'
'
,
'
\\\\\\
"'
,
'
\\\\
?'
,
'&'
,
'UTF-8'
]:
continue
if
string
in
last_string
:
continue
start
=
text
.
find
(
string
)
if
start
==
-
1
:
continue
index
=
start
+
1
sign_stack
=
[
'"'
]
while
len
(
sign_stack
)
!=
0
:
if
text
[
index
]
==
'
\\
'
:
index
+=
1
elif
text
[
index
]
==
'"'
and
sign_stack
[
-
1
]
==
'"'
:
sign_stack
.
pop
()
elif
text
[
index
]
==
'"'
:
sign_stack
.
append
(
'"'
)
elif
text
[
index
]
==
'}'
and
sign_stack
[
-
1
]
==
'${'
:
sign_stack
.
pop
()
elif
text
[
index
-
1
:
index
+
1
]
==
'${'
:
sign_stack
.
append
(
'${'
)
index
+=
1
string
=
text
[
start
:
index
]
last_string
=
string
params
=
[]
result
=
string
if
'$'
in
string
:
count
=
0
last
=
-
1
res
=
[]
for
i
in
range
(
len
(
string
)):
if
last
==
-
1
and
string
[
i
]
==
'$'
:
if
count
==
0
:
last
=
i
elif
last
!=
-
1
:
if
string
[
i
]
==
'{'
:
count
+=
1
elif
string
[
i
]
==
'}'
:
count
-=
1
if
count
==
0
:
res
.
append
((
last
,
i
+
1
))
last
=
-
1
elif
count
==
0
:
last
=
-
1
for
i
in
re
.
findall
(
'
\\
$
\\
w*'
,
string
):
if
i
==
'$'
:
continue
find
=
string
.
find
(
i
)
res
.
append
((
find
,
find
+
len
(
i
)))
res
=
sorted
(
res
,
key
=
lambda
l
:
l
[
0
])
last_res
=
None
for
i
in
res
:
if
last_res
is
not
None
and
i
[
0
]
<
last_res
[
1
]:
continue
params
.
append
(
string
[
i
[
0
]:
i
[
1
]])
last_res
=
i
for
i
in
params
:
result
=
result
.
replace
(
i
,
"[str]"
)
result
=
result
[
1
:
-
1
]
.
replace
(
'
%
'
,
'
%%
'
)
.
replace
(
'
\\\\
'
,
'
\\
'
)
.
replace
(
'[str]'
,
'
%
s'
)
if
result
==
aesKey
:
continue
if
mapping
.
get
(
result
)
is
not
None
:
continue
mapping
[
result
]
=
get_random_string
(
5
,
False
)
+
'_D'
loads
[
'class_string'
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
deal_class_string
():
# 获取映射
string_mapping
=
check_class_string_mapping
()
print
(
string_mapping
)
with
open
(
get_path
(
'.
\\
src
\\
main
\\
res
\\
values
\\
strings.xml'
),
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
rfind
=
text
.
rfind
(
'
\n
'
)
text_result
=
text
[:
rfind
]
for
i
in
string_mapping
:
string
=
i
string
=
string
.
replace
(
'&'
,
'&'
)
string
=
string
.
replace
(
'?'
,
'
\\
?'
)
string
=
string
.
replace
(
'
\'
'
,
'
\\\'
'
)
text_result
+=
'
\n
<string name="'
+
string_mapping
[
i
]
+
'" translatable="false">'
+
string
+
'</string>'
text_result
+=
text
[
rfind
:]
with
open
(
get_path
(
'.
\\
src
\\
main
\\
res
\\
values
\\
strings.xml'
),
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text_result
)
# 搜索 Int.string() 路径
class_path
=
get_class_path
(
find_file
(
'fun Int.string'
,
get_path
(
'.
\\
src
\\
main
\\
java'
)))
+
'.string'
classes
=
get_classes
([
'kt'
])
for
path
in
classes
:
# 跳过 AESHelper 类中的字符串,避免嵌套
if
os
.
path
.
basename
(
path
)
.
startswith
(
'AESHelper'
):
continue
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
text
=
re
.
sub
(
'@SuppressLint.*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'//[^"]*?(("[^"]*?){2})*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
' +
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'=
\\
s+'
,
'= '
,
text
)
text
=
re
.
sub
(
':
\\
s+'
,
': '
,
text
)
text
=
re
.
sub
(
',
\\
s+'
,
', '
,
text
)
text
=
re
.
sub
(
'
\n
{2,}'
,
'
\n\n
'
,
text
)
result_text
=
text
strings
=
re
.
findall
(
'".*?[^
\\\\
]"'
,
re
.
sub
(
r'@[^)]*'
,
lambda
x
:
x
.
group
()
.
replace
(
'"'
,
"'"
),
result_text
))
last_string
=
''
for
string
in
strings
:
if
len
(
string
)
<=
4
or
string
.
isspace
()
or
\
string
[
1
:
-
1
]
in
[
'
\\\\
n'
,
'
\\\\
r'
,
'
\\\\\'
'
,
'
\\\\\\
"'
,
'
\\\\
?'
,
'&'
,
'UTF-8'
]:
continue
if
string
in
last_string
:
continue
start
=
text
.
find
(
string
)
if
start
==
-
1
:
continue
index
=
start
+
1
sign_stack
=
[
'"'
]
while
len
(
sign_stack
)
!=
0
:
if
text
[
index
]
==
'
\\
'
:
index
+=
1
elif
text
[
index
]
==
'"'
and
sign_stack
[
-
1
]
==
'"'
:
sign_stack
.
pop
()
elif
text
[
index
]
==
'"'
:
sign_stack
.
append
(
'"'
)
elif
text
[
index
]
==
'}'
and
sign_stack
[
-
1
]
==
'${'
:
sign_stack
.
pop
()
elif
text
[
index
-
1
:
index
+
1
]
==
'${'
:
sign_stack
.
append
(
'${'
)
index
+=
1
# 得到完整字符串
string
=
text
[
start
:
index
]
last_string
=
string
# 参数
params
=
[]
result
=
string
# 处理字符串拼接
if
'$'
in
string
:
count
=
0
last
=
-
1
res
=
[]
for
i
in
range
(
len
(
string
)):
if
last
==
-
1
and
string
[
i
]
==
'$'
:
if
count
==
0
:
last
=
i
elif
last
!=
-
1
:
if
string
[
i
]
==
'{'
:
count
+=
1
elif
string
[
i
]
==
'}'
:
count
-=
1
if
count
==
0
:
res
.
append
((
last
,
i
+
1
))
last
=
-
1
elif
count
==
0
:
last
=
-
1
for
i
in
re
.
findall
(
'
\\
$
\\
w*'
,
string
):
if
i
==
'$'
:
continue
find
=
string
.
find
(
i
)
res
.
append
((
find
,
find
+
len
(
i
)))
res
=
sorted
(
res
,
key
=
lambda
l
:
l
[
0
])
last_res
=
None
for
i
in
res
:
if
last_res
is
not
None
and
i
[
0
]
<
last_res
[
1
]:
continue
params
.
append
(
string
[
i
[
0
]:
i
[
1
]])
last_res
=
i
for
i
in
params
:
result
=
result
.
replace
(
i
,
"[str]"
)
for
i
in
range
(
len
(
params
)):
if
params
[
i
][
1
]
==
'{'
:
params
[
i
]
=
params
[
i
][
2
:
-
1
]
else
:
params
[
i
]
=
params
[
i
][
1
:]
# 每个参数加上 toString
params
[
i
]
=
'('
+
params
[
i
]
+
').toString()'
# 拼接部分替换为 %s
result
=
result
[
1
:
-
1
]
.
replace
(
'
%
'
,
'
%%
'
)
.
replace
(
'
\\\\
'
,
'
\\
'
)
.
replace
(
'[str]'
,
'
%
s'
)
# aesKey 密钥不加密
if
result
==
aesKey
:
continue
result_text
=
result_text
.
replace
(
text
[
start
:
index
],
'R.string.'
+
string_mapping
[
result
]
+
'.string('
+
', '
.
join
(
params
)
+
')'
)
# 添加 Int.string() 方法引用
if
len
(
re
.
findall
(
'import '
+
class_path
,
text
))
==
0
:
result_text
=
re
.
sub
(
'(package
\\
S+?
\\
s+?)(?=
\\
S)'
,
'
\\
g<1>import '
+
class_path
+
'
\n
'
,
result_text
,
flags
=
re
.
S
)
# 添加资源 R 引用
if
len
(
re
.
findall
(
'import '
+
applicationId
+
'.R'
,
text
))
==
0
:
result_text
=
re
.
sub
(
'(package
\\
S+?
\\
s+?)(?=
\\
S)'
,
'
\\
g<1>import '
+
applicationId
+
'.R'
+
'
\n
'
,
result_text
,
flags
=
re
.
S
)
# 去除 const
result_text
=
re
.
sub
(
'const (val.*?R
\\
.string
\\
.
\\
S+?
\\
.string
\\
(
\\
))'
,
'
\\
g<1>'
,
result_text
)
with
open
(
path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
result_text
)
def
check_xml_string_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_mapping
=
loads
.
get
(
'xml_string'
,
{})
strings
=
find_text
(
'android:text="(?<!@string/)([^@"]+?)"'
,
get_path
(
'.
\\
src
\\
main
\\
res
\\
layout'
),
skip_name
=
[
'notify'
,
'notity'
])
mapping
=
{}
for
i
in
strings
:
value
=
old_mapping
.
get
(
i
)
if
value
is
not
None
:
mapping
[
i
]
=
value
continue
# 添加 '_D' 结尾,资源混淆时过滤不用再次改名
mapping
[
i
]
=
get_random_string
(
5
,
False
)
+
'_D'
loads
[
'xml_string'
]
=
mapping
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
mapping
def
deal_xml_string
():
# 获取映射
string_mapping
=
check_xml_string_mapping
()
print
(
string_mapping
)
with
open
(
get_path
(
'.
\\
src
\\
main
\\
res
\\
values
\\
strings.xml'
),
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
rfind
=
text
.
rfind
(
'
\n
'
)
text_result
=
text
[:
rfind
]
for
i
in
string_mapping
:
string
=
i
string
=
string
.
replace
(
'&'
,
'&'
)
string
=
string
.
replace
(
'?'
,
'
\\
?'
)
string
=
string
.
replace
(
'
\'
'
,
'
\\\'
'
)
text_result
+=
'
\n
<string name="'
+
string_mapping
[
i
]
+
'" translatable="false">'
+
string
+
'</string>'
text_result
+=
text
[
rfind
:]
with
open
(
get_path
(
'.
\\
src
\\
main
\\
res
\\
values
\\
strings.xml'
),
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text_result
)
sub_map
=
{}
for
i
in
string_mapping
:
sub_map
[
'(?<=android:text=")'
+
re
.
escape
(
i
)
+
'(?=")'
]
=
'@string/'
+
re
.
escape
(
string_mapping
[
i
])
# 替换时跳过通知布局
sub_map_text
(
sub_map
,
path
=
get_path
(
'.
\\
src
\\
main
\\
res
\\
layout'
),
skip_name
=
[
'notify'
,
'notity'
])
def
deal_code_string
():
# 处理布局文件中的明文字符串
deal_xml_string
()
# 处理代码文件中的明文字符串
deal_class_string
()
def
deal_res
():
# 添加字符串为 string 资源
deal_code_string
()
# 替换 TextView 为 AESTextView
aes_text_view
=
get_class_path
(
find_file
(
'class AESTextView'
,
get_path
(
'.
\\
src
\\
main
\\
java'
)))
sub_map
=
{
'(?<=<)TextView'
:
aes_text_view
,
'(?<=<)com.noober.background.view.BLTextView'
:
aes_text_view
}
sub_map_text
(
sub_map
,
path
=
get_path
(
'.
\\
src
\\
main
\\
res
\\
layout'
),
skip_name
=
[
'notify'
,
'notity'
])
# 加密 string
strings_mapping
=
check_strings_mapping
()
print
(
strings_mapping
)
sub_map
=
{}
for
i
in
strings_mapping
:
sub_map
[
'<string(.*?)>'
+
re
.
escape
(
i
)
+
'</string>'
]
=
\
'<string
\\
g<1>>'
+
re
.
escape
(
strings_mapping
[
i
])
+
'</string>'
sub_map_text
(
sub_map
,
path
=
get_path
(
'.
\\
src
\\
main
\\
res
\\
values'
))
# 改 string id
deal_ids_type
(
'string'
)
# 改 color id
deal_ids_type
(
'color'
)
# 改 dimen id
deal_ids_type
(
'dimen'
)
# 改 style id
deal_ids_type
(
'style'
)
# 改 declare-styleable
styleable_mapping
=
check_styleable_mapping
()
print
(
styleable_mapping
)
sub_map
=
{}
styleables
=
find_text
(
'<declare-styleable name=".*?">[
\\
s
\\
S]*?</declare-styleable>'
)
for
i
in
styleables
:
result
=
i
styleable_id
=
re
.
findall
(
'<declare-styleable name="(.*?)">'
,
i
)[
0
]
result
=
result
.
replace
(
'<declare-styleable name="'
+
styleable_id
+
'">'
,
'<declare-styleable name="'
+
styleable_mapping
[
styleable_id
]
+
'">'
)
sub_map
[
'R
\\
.styleable
\\
.'
+
styleable_id
+
'(
\\
W)'
]
=
\
'R.styleable.'
+
styleable_mapping
[
styleable_id
]
+
'
\\
g<1>'
styleables_attr
=
re
.
findall
(
'<attr name="(.*?)".*?/>'
,
i
)
for
attr
in
styleables_attr
:
result
=
result
.
replace
(
'<attr name="'
+
attr
+
'"'
,
'<attr name="'
+
styleable_mapping
[
attr
+
'_A'
]
+
'"'
)
sub_map
[
'R
\\
.styleable
\\
.'
+
styleable_id
+
'_'
+
attr
+
'(
\\
W)'
]
=
\
'R.styleable.'
+
styleable_mapping
[
styleable_id
]
+
'_'
+
styleable_mapping
[
attr
+
'_A'
]
+
'
\\
g<1>'
sub_map
[
'app:'
+
attr
+
'='
]
=
'app:'
+
styleable_mapping
[
attr
+
'_A'
]
+
'='
sub_map
[
re
.
escape
(
i
)]
=
result
sub_map_text
(
sub_map
)
# 改 view id
view_ids
=
find_text
(
'"
\\
@
\\
+id/(.*?)"'
,
get_path
(
'.
\\
src
\\
main
\\
res'
))
view_ids_mapping
=
check_view_ids_mapping
()
print
(
view_ids_mapping
)
sub_map
=
{}
for
i
in
view_ids
:
if
i
==
'root'
:
continue
key
=
get_title
(
i
)
sub_map
[
'(>|")@(
\\
+)?id/'
+
i
+
'(<|")'
]
=
'
\\
g<1>@
\\
g<2>id/'
+
view_ids_mapping
[
key
]
+
'
\\
g<3>'
sub_map
[
'R
\\
.id
\\
.'
+
i
+
'(
\\
W)'
]
=
'R.id.'
+
view_ids_mapping
[
key
]
+
'
\\
g<1>'
sub_map
[
'([bB]inding
\\
??(?:
\\
.
\\
w+)?
\\
??)
\\
.'
+
key
+
'(
\\
W)'
]
=
'
\\
g<1>.'
+
view_ids_mapping
[
key
]
+
'
\\
g<2>'
sub_map
[
'(?<=app:constraint_referenced_ids=".*?)'
+
i
+
'(?=.*?")'
]
=
view_ids_mapping
[
key
]
sub_map_text
(
sub_map
)
# 改 layout 文件名
layout_mapping
=
check_layout_mapping
()
print
(
layout_mapping
)
layout_path
=
get_path
(
'.
\\
src
\\
main
\\
res
\\
layout'
)
listdir
=
os
.
listdir
(
layout_path
)
for
i
in
listdir
:
name
=
i
[:
i
.
rfind
(
'.'
)]
if
layout_mapping
.
get
(
name
)
is
None
:
continue
path_join
=
os
.
path
.
join
(
layout_path
,
i
)
shutil
.
move
(
path_join
,
path_join
.
replace
(
name
,
layout_mapping
[
name
]))
sub_map
=
{}
for
i
in
layout_mapping
:
sub_map
[
'R
\\
.layout
\\
.'
+
i
+
'(
\\
W)'
]
=
'R.layout.'
+
layout_mapping
[
i
]
+
'
\\
g<1>'
# ViewBinding 同步修改
sub_map
[
get_title
(
i
,
True
)
+
'Binding'
]
=
get_title
(
layout_mapping
[
i
],
True
)
+
'Binding'
sub_map
[
'(>|")@layout/'
+
i
+
'(<|")'
]
=
'
\\
g<1>@layout/'
+
layout_mapping
[
i
]
+
'
\\
g<2>'
sub_map_text
(
sub_map
)
# 改 drawable 文件名
deal_res_type
(
'drawable'
)
# 改 mipmap 文件名
deal_res_type
(
'mipmap'
)
# 改 raw 文件名
deal_res_type
(
'raw'
)
# 改 raw 文件名
deal_res_type
(
'xml'
)
# 改图片内容
deal_image
()
def
check_activity_mapping
():
try
:
loads
=
json
.
load
(
open
(
'mapping.json'
,
'r'
,
encoding
=
'utf-8'
))
except
(
ValueError
,
IOError
):
loads
=
{}
old_list
=
loads
.
get
(
'activity'
,
[])
old_paths
=
[]
for
i
in
old_list
:
old_paths
.
append
(
i
[:
i
.
rfind
(
'.'
)])
old_paths
=
sorted
(
set
(
old_paths
),
key
=
old_paths
.
index
)
old_paths
.
sort
(
key
=
len
)
classes
=
get_classes
()
paths
=
[]
root_path
=
get_path
(
'.
\\
src
\\
main
\\
java'
)
for
i
in
classes
:
paths
.
append
(
i
[
len
(
root_path
)
+
1
:
i
.
rfind
(
os
.
sep
)]
.
replace
(
os
.
sep
,
'.'
))
paths
=
sorted
(
set
(
paths
),
key
=
paths
.
index
)
paths
.
sort
(
key
=
len
)
new_list
=
[]
for
i
in
old_list
:
if
i
[:
i
.
rfind
(
'.'
)]
in
paths
:
new_list
.
append
(
i
)
for
i
in
paths
:
if
i
in
old_paths
:
continue
# 每个路径下产生随机个垃圾类
for
_
in
range
(
random
.
randint
(
0
,
3
)):
name
=
get_title
(
get_random_string
(
4
),
True
)
+
'_D'
new_list
.
append
(
i
+
'.'
+
name
)
loads
[
"activity"
]
=
new_list
json
.
dump
(
loads
,
open
(
'mapping.json'
,
'w'
,
encoding
=
'utf-8'
),
indent
=
4
)
return
new_list
def
add_junk_activity
():
root_path
=
get_path
(
'.
\\
src
\\
main
\\
java'
)
new_list
=
check_activity_mapping
()
count
=
len
(
new_list
)
random
.
shuffle
(
new_list
)
for
i
in
range
(
count
):
class_path
=
get_path
(
root_path
+
'
\\
'
+
new_list
[
i
]
.
replace
(
'.'
,
'
\\
'
)
+
'.kt'
)
# 获取垃圾类代码
code
=
get_random_activity_code
(
new_list
[
i
],
new_list
[(
i
+
1
)
%
count
])
with
open
(
class_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
code
)
print
(
class_path
)
with
open
(
get_path
(
'.
\\
src
\\
main
\\
AndroidManifest.xml'
),
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
replace_string
=
'
\n
'
for
i
in
new_list
:
replace_string
+=
' <activity android:name="'
+
i
+
'" />
\n
'
text
=
text
.
replace
(
' </application>'
,
replace_string
+
''' </application>'''
)
with
open
(
get_path
(
'.
\\
src
\\
main
\\
AndroidManifest.xml'
),
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
def
get_random_view
():
return
random
.
choice
(
[
'TextView'
,
'Button'
,
'EditText'
,
'ImageView'
,
'FrameLayout'
,
'LinearLayout'
,
'CalendarView'
,
'CheckBox'
,
'CheckedTextView'
,
'DatePicker'
,
'ExpandableListView'
,
'GridLayout'
,
'GridView'
,
'HorizontalScrollView'
,
'ImageButton'
,
'ListView'
,
'RelativeLayout'
,
'ScrollView'
,
'SearchView'
,
'SeekBar'
,
'TableLayout'
])
def
get_random_activity_code
(
class1
,
class2
):
# 代码中,class1 跳转 class2,确保垃圾类被引用到
package1
=
class1
[:
class1
.
rfind
(
'.'
)]
name1
=
class1
[
len
(
package1
)
+
1
:]
package2
=
class2
[:
class2
.
rfind
(
'.'
)]
name2
=
class2
[
len
(
package2
)
+
1
:]
view
=
get_random_view
()
if
package1
==
package2
:
import_code
=
''
else
:
import_code
=
'
\n
import '
+
class2
code
=
'''package '''
+
package1
+
'''
import android.content.Intent
import android.os.Bundle
import android.widget.'''
+
view
+
'''
import androidx.appcompat.app.AppCompatActivity'''
+
import_code
+
'''
class '''
+
name1
+
''' : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView('''
+
view
+
'''(this))
startActivity(Intent(this, '''
+
name2
+
'''::class.java))
}
}'''
return
code
def
add_junk_method
():
classes
=
get_classes
([
'kt'
])
for
path
in
classes
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
if
text
.
find
(
'data class'
)
!=
-
1
:
continue
text
=
re
.
sub
(
'@SuppressLint.*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'//[^"]*?(("[^"]*?){2})*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
' +
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'=
\\
s+'
,
'= '
,
text
)
text
=
re
.
sub
(
':
\\
s+'
,
': '
,
text
)
text
=
re
.
sub
(
',
\\
s+'
,
', '
,
text
)
text
=
re
.
sub
(
'/
\\
*[
\\
s
\\
S]*?
\\
*/'
,
''
,
text
)
text
=
re
.
sub
(
'
\n
{2,}'
,
'
\n\n
'
,
text
)
result_text
=
text
interfaces
=
re
.
findall
(
'interface .*?{.*?}'
,
text
,
flags
=
re
.
S
)
for
interface
in
interfaces
:
start
=
text
.
find
(
interface
)
start
=
text
[:
start
]
.
rfind
(
'
\n
'
)
+
1
if
start
==
-
1
:
continue
index
=
start
count
=
0
isStart
=
False
while
(
not
isStart
or
count
>
0
)
and
index
<
len
(
text
):
if
text
[
index
]
==
'{'
:
count
+=
1
if
count
==
1
:
isStart
=
True
elif
text
[
index
]
==
'}'
:
count
-=
1
index
+=
1
# 过滤接口及其内容
text
=
text
.
replace
(
text
[
start
:
index
],
''
)
functions
=
re
.
findall
(
'fun (?!interface ).*?
\\
(.*?
\\
)'
,
text
,
flags
=
re
.
S
)
for
function
in
functions
:
start
=
text
.
find
(
function
)
start
=
text
[:
start
]
.
rfind
(
'
\n
'
)
+
1
if
start
==
-
1
:
continue
index
=
start
count
=
0
isStart
=
False
while
(
not
isStart
or
count
>
0
)
and
index
<
len
(
text
):
if
text
[
index
]
==
'('
:
count
+=
1
if
count
==
1
:
isStart
=
True
elif
text
[
index
]
==
')'
:
count
-=
1
index
+=
1
if
'abstract'
in
text
[
start
:
index
]:
continue
count
=
0
isStart
=
False
param_end
=
index
while
(
not
isStart
or
count
>
0
)
and
index
<
len
(
text
):
if
not
isStart
and
text
[
index
]
==
'='
:
break
if
text
[
index
]
==
'{'
:
count
+=
1
if
count
==
1
:
isStart
=
True
param_end
=
index
elif
text
[
index
]
==
'}'
:
count
-=
1
index
+=
1
if
not
isStart
:
continue
fun_text
=
text
[
start
:
index
]
header
=
text
[
start
:
param_end
]
param_start
=
start
+
header
.
find
(
'('
)
context
=
re
.
search
(
'fun (.*?
\\
.)?
\\
w*?'
,
text
[
start
:
param_start
])[
1
]
if
context
is
None
:
context
=
''
param_string
=
text
[
param_start
:
param_end
]
if
'override fun '
in
header
and
'
\n
'
+
\
re
.
sub
(
'(final )?override fun '
,
'super.'
,
header
[:
header
.
index
(
param_string
)])
in
fun_text
:
methods
=
[]
for
i
in
range
(
random
.
randint
(
0
,
5
)):
result
=
add_one_method
(
fun_text
,
param_string
,
header
,
context
)
if
result
is
None
or
len
(
result
)
!=
3
:
continue
fun_text
,
method_text1
,
method_text2
=
result
methods
.
insert
(
0
,
method_text2
)
methods
.
insert
(
0
,
method_text1
)
methods
.
insert
(
0
,
fun_text
)
fun_text
=
'
\n\n
'
.
join
(
methods
)
else
:
methods
=
[]
for
i
in
range
(
random
.
randint
(
0
,
5
)):
result
=
add_one_method
(
fun_text
,
param_string
,
header
,
context
)
if
result
is
None
or
len
(
result
)
!=
2
:
continue
fun_text
,
method_text
=
result
methods
.
insert
(
0
,
method_text
)
methods
.
insert
(
0
,
fun_text
)
fun_text
=
'
\n\n
'
.
join
(
methods
)
result_text
=
result_text
.
replace
(
text
[
start
:
index
],
fun_text
)
with
open
(
path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
result_text
)
def
add_one_method
(
fun_text
,
param_string
,
header
,
context
):
if
'suspend '
in
header
:
suspend
=
'suspend '
else
:
suspend
=
''
body
=
fun_text
[
len
(
header
):]
intent
=
len
(
fun_text
)
-
len
(
fun_text
.
lstrip
())
param
=
param_string
[
1
:
param_string
.
rfind
(
')'
)]
param
=
re
.
sub
(
'<.*?>'
,
''
,
param
)
param
=
re
.
sub
(
'
\\
([^(]*?
\\
)'
,
'()'
,
param
)
param
=
[
i
.
strip
()
.
split
(
':'
)[
0
]
for
i
in
param
.
split
(
','
)]
for
i
in
range
(
len
(
param
)):
if
param
[
i
]
.
startswith
(
'vararg'
):
param
[
i
]
=
'*'
+
param
[
i
]
.
split
(
' '
)[
-
1
]
elif
param
[
i
]
.
startswith
(
'@'
):
param
[
i
]
=
param
[
i
]
.
split
(
' '
)[
-
1
]
if
'override fun '
in
header
and
'
\n
'
+
\
re
.
sub
(
'(final )?override fun '
,
'super.'
,
header
[:
header
.
index
(
param_string
)])
in
fun_text
:
if
':'
in
param_string
[
param_string
.
rfind
(
')'
):]:
return
method_split
=
[
i
.
rstrip
()
for
i
in
fun_text
.
split
(
'
\n
'
)]
if
method_split
[
0
]
.
rstrip
()[
-
1
]
!=
'{'
or
method_split
[
-
1
]
.
lstrip
()[
0
]
!=
'}'
:
return
method_body
=
method_split
[
1
:
-
1
]
super_index
=
-
1
for
i
in
range
(
len
(
method_body
)):
if
'super.'
in
method_body
[
i
]:
if
super_index
>=
0
:
super_index
=
-
1
break
super_index
=
i
if
super_index
<
0
:
return
method_text1
=
method_split
.
copy
()
method_name1
=
get_random_string
(
is_ascii
=
False
)
method_text1
[
0
]
=
method_text1
[
0
]
.
replace
(
header
,
' '
*
intent
+
'private '
+
suspend
+
'fun '
+
context
+
method_name1
+
param_string
)
method_text1
[
1
:
-
1
]
=
method_body
[:
super_index
]
method_text2
=
method_split
.
copy
()
method_name2
=
get_random_string
(
is_ascii
=
False
)
method_text2
[
0
]
=
method_text2
[
0
]
.
replace
(
header
,
' '
*
intent
+
'private '
+
suspend
+
'fun '
+
context
+
method_name2
+
param_string
)
method_text2
[
1
:
-
1
]
=
method_body
[
super_index
+
1
:]
method_body
[
super_index
+
1
:]
=
[
' '
*
(
intent
+
4
)
+
method_name2
+
'('
+
', '
.
join
(
param
)
+
')'
]
method_body
[:
super_index
]
=
[
' '
*
(
intent
+
4
)
+
method_name1
+
'('
+
', '
.
join
(
param
)
+
')'
]
method_split
[
1
:
-
1
]
=
method_body
method_text1
=
'
\n
'
.
join
(
method_text1
)
method_text2
=
'
\n
'
.
join
(
method_text2
)
fun_text
=
'
\n
'
.
join
(
method_split
)
return
fun_text
,
method_text1
,
method_text2
else
:
method_name
=
get_random_string
(
is_ascii
=
False
)
method_text
=
fun_text
if
':'
in
param_string
[
param_string
.
rfind
(
')'
):]:
method_text
=
method_text
.
replace
(
header
,
' '
*
intent
+
'private '
+
suspend
+
'fun '
+
context
+
method_name
+
param_string
)
fun_text
=
fun_text
.
replace
(
body
,
'{
\n
'
+
' '
*
(
intent
+
4
)
+
'return '
+
method_name
+
'('
+
', '
.
join
(
param
)
+
')
\n
'
+
' '
*
intent
+
'}'
)
else
:
method_text
=
method_text
.
replace
(
header
,
' '
*
intent
+
'private '
+
suspend
+
'fun '
+
context
+
method_name
+
param_string
)
fun_text
=
fun_text
.
replace
(
body
,
'{
\n
'
+
' '
*
(
intent
+
4
)
+
method_name
+
'('
+
', '
.
join
(
param
)
+
')
\n
'
+
' '
*
intent
+
'}'
)
return
fun_text
,
method_text
def
add_junk_code
():
classes
=
get_classes
([
'kt'
])
for
path
in
classes
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
if
text
.
find
(
'data class'
)
!=
-
1
:
continue
text
=
re
.
sub
(
'@SuppressLint.*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'//[^"]*?(("[^"]*?){2})*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
' +
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'=
\\
s+'
,
'= '
,
text
)
text
=
re
.
sub
(
':
\\
s+'
,
': '
,
text
)
text
=
re
.
sub
(
',
\\
s+'
,
', '
,
text
)
text
=
re
.
sub
(
'/
\\
*[
\\
s
\\
S]*?
\\
*/'
,
''
,
text
)
text
=
re
.
sub
(
'
\n
{2,}'
,
'
\n\n
'
,
text
)
functions
=
re
.
findall
(
'((?=fun .*?
\\
([^}]*?
\\
)|init |val.*?by lazy)[^}]*?
\\
{)'
,
text
)
result_text
=
text
for
function
in
functions
:
start
=
text
.
find
(
function
)
start
=
text
[:
start
]
.
rfind
(
'
\n
'
)
+
1
if
start
==
-
1
:
continue
index
=
start
count
=
0
isStart
=
False
while
(
not
isStart
or
count
>
0
)
and
index
<
len
(
text
):
if
text
[
index
]
==
'('
:
count
+=
1
if
count
==
1
:
isStart
=
True
elif
text
[
index
]
==
')'
:
count
-=
1
if
not
isStart
and
text
[
index
]
==
'{'
:
break
index
+=
1
count
=
0
isStart
=
False
while
(
not
isStart
or
count
>
0
)
and
index
<
len
(
text
):
if
text
[
index
]
==
'{'
:
count
+=
1
if
count
==
1
:
isStart
=
True
elif
text
[
index
]
==
'}'
:
count
-=
1
index
+=
1
fun_text
=
text
[
start
:
index
]
fun_text
=
fun_text
.
split
(
'
\n
'
)
intent_stack
=
[]
last_intent
=
0
log_indexes
=
[]
skip_text
=
[
False
for
_
in
fun_text
]
i
=
0
while
i
<
len
(
fun_text
):
if
'object : '
in
fun_text
[
i
]
and
fun_text
[
i
]
.
rstrip
()
.
endswith
(
'{'
):
intent
=
len
(
fun_text
[
i
])
-
len
(
fun_text
[
i
]
.
lstrip
())
while
i
<
len
(
fun_text
)
-
1
:
i
+=
1
skip_text
[
i
]
=
True
if
fun_text
[
i
]
.
lstrip
()
.
startswith
(
'}'
)
and
\
len
(
fun_text
[
i
])
-
len
(
fun_text
[
i
]
.
lstrip
())
==
intent
:
break
i
+=
1
for
i
in
range
(
len
(
fun_text
)):
if
skip_text
[
i
]:
continue
intent
=
len
(
fun_text
[
i
])
-
len
(
fun_text
[
i
]
.
lstrip
())
if
intent
==
0
or
intent
%
4
!=
0
:
continue
if
intent
>
last_intent
and
i
>
0
:
j
=
i
-
1
while
len
(
fun_text
[
j
])
-
len
(
fun_text
[
j
]
.
lstrip
())
==
0
and
j
>=
0
:
j
-=
1
if
j
>=
0
:
for
k
in
range
(
int
((
intent
-
last_intent
)
/
4
)):
intent_stack
.
append
(
j
)
elif
intent
<
last_intent
:
for
k
in
range
(
int
((
last_intent
-
intent
)
/
4
)):
intent_stack
.
pop
()
if
len
(
intent_stack
)
>
0
and
intent
>=
last_intent
\
and
len
(
fun_text
[
i
]
.
strip
())
!=
0
and
fun_text
[
i
]
.
lstrip
()[
0
]
!=
')'
:
last_text
=
fun_text
[
intent_stack
[
-
1
]]
if
last_text
[
-
1
]
==
'{'
and
'when'
not
in
last_text
and
'$'
not
in
last_text
and
\
'object'
not
in
last_text
and
'class '
not
in
last_text
and
'interface '
not
in
last_text
:
log_indexes
.
append
((
i
,
intent
))
last_intent
=
intent
log_indexes
.
reverse
()
for
i
in
log_indexes
:
if
random
.
random
()
>
0.35
:
continue
junk_code
=
get_random_junk_code
()
junk_code
=
junk_code
.
replace
(
'
\n
'
,
'
\n
'
+
' '
*
i
[
1
])[
1
:]
fun_text
.
insert
(
i
[
0
],
junk_code
)
result_text
=
result_text
.
replace
(
text
[
start
:
index
],
'
\n
'
.
join
(
fun_text
))
with
open
(
path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
result_text
)
add_import
(
'android.util.Log'
,
classes
)
def
get_random_junk_code
():
junk_code
=
random
.
choice
(
junk_codes
)
count
=
1
while
True
:
random_str
=
get_random_string
(
is_ascii
=
False
)
replace
=
junk_code
.
replace
(
'[str'
+
str
(
count
)
+
']'
,
random_str
)
if
junk_code
==
replace
:
break
junk_code
=
replace
count
+=
1
count
=
1
while
True
:
random_int
=
get_random_int
()
replace
=
junk_code
.
replace
(
'[int'
+
str
(
count
)
+
']'
,
str
(
random_int
))
if
junk_code
==
replace
:
break
junk_code
=
replace
count
+=
1
return
junk_code
junk_codes
=
[
'''
if (System.currentTimeMillis() < [int1]) {
println(System.currentTimeMillis())
}'''
,
'''
Log.d("[str1]", "[str1]")'''
,
'''
val [str1] = [int1]..[int1] + 10
println([str1].random())'''
,
'''
val [str1]: (Int, Int) -> Int = { [str2], [str3] -> [str2] + [str3] }
println([str1].invoke([int1], [int2]))'''
,
'''
class [str1](val [str2]: String, val [str3]: Int)
println([str1]("[str4]", [int1]))'''
,
'''
open class [str1] {
open fun [str2]() {}
}
class [str3] : [str1]() {
override fun [str2]() {
println("[str5]")
}
}
val [str4] = [str3]()
println([str4])'''
,
'''
fun String.[str1](): Boolean {
return this == this.reversed()
}
val [str2] = "[str4]"
val [str3] = [str2].[str1]()
println([str3])'''
,
'''
fun [str1]([str2]: Int, [str3]: Int): Int {
return [str2] + [str3]
}
println([str1]([int1], [int2]))'''
,
'''
val [str1] = { [str2]: String -> println([str2]) }
[str1]("[str3]!")'''
,
'''
fun [str1]([str2]: String) {
println("tag, $[str2]!")
}
[str1]("[str3]")'''
,
'''
fun [str1]() {
fun [str2]([str3]: Int) {
println([str3])
}
[str2]([int1])
[str2]([int2])
[str2]([int3])
}
[str1]()'''
,
'''
class [str1] {
inner class [str2] {
fun [str3]() {
println("[str5]")
}
}
}
val [str4] = [str1]().[str2]()
[str4].[str3]()'''
,
'''
class [str1] {
fun [str2]() {
println("[str3]")
}
}
[str1]().[str2]()'''
,
'''
abstract class [str1] {
abstract fun [str2]()
}
class [str3] : [str1]() {
override fun [str2]() {
println("[str5]")
}
}
val [str4] = [str3]()
[str4].[str2]()'''
,
'''
class [str1]<T>(val [str2]: T)
val [str3] = [str1]([int1])
val [str4] = [str1]("[str5]")
println([str3])
println([str4])'''
,
'''
fun <T> [str1]([str2]: T) {
println([str2])
}
[str1]([int1])
[str1]("[str3]")'''
,
'''
class [str1]<T, K>(val [str3]: T, val [str4]: K) {
init {
println("[str5]")
}
}
val [str2] = [str1]("[str6]", [int1])
println([str2])'''
,
'''
class [str1]<T>(val [str2]: T, val [str3]: Int) {
constructor([str4]: T) : this([str4], [int1]) {
println("[str5]")
}
}
println([str1]("[str6]"))'''
,
'''
val [str1] = [int1]
val [str2] = [int2]
val [str3] = [str1] * [str2] * [str2]
println([str3])'''
,
'''
fun [str1]([str3]: Int, [str4]: Int): Int {
return [str3] + [str4]
}
val [str2] = [str1]([int1], [int2])
println([str2])'''
,
'''
fun [str1]([str4]: Int, [str5]: Int): Int {
fun [str2]([str6]: Int, [str7]: Int): Int {
return [str6] + [str7]
}
return [str2]([str4], [str5])
}
val [str3] = [str1]([int1], [int2])
println([str3])'''
,
'''
class [str1] {
val [str2] by lazy {
println("[str4]")
"[str5]!"
}
}
val [str3] = [str1]()
println([str3].[str2])'''
,
'''
class [str1]<T>([str3]: T) {
init {
println("[str4]: $[str3]")
}
}
println([str1]("[str2]"))'''
,
'''
class [str1]<T>(val [str3]: T, val [str4]: Int) {
constructor([str3]: T) : this([str3], [int1]) {
println("[str4]")
}
}
println([str1]("[str2]"))'''
,
'''
class [str1]<T>(val [str4]: T)
val [str2] = [str1]([int1])
val [str3] = [str1]("[str5]")
println([str2].[str4])
println([str3].[str4])'''
]
def
class_disorder
():
classes
=
get_classes
([
'kt'
])
for
path
in
classes
:
with
open
(
path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
text
=
f
.
read
()
if
text
.
find
(
'data class'
)
!=
-
1
:
continue
text
=
re
.
sub
(
'@SuppressLint.*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'//[^"]*?(("[^"]*?){2})*?
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
' +
\n
'
,
'
\n
'
,
text
)
text
=
re
.
sub
(
'=
\\
s+'
,
'= '
,
text
)
text
=
re
.
sub
(
':
\\
s+'
,
': '
,
text
)
text
=
re
.
sub
(
',
\\
s+'
,
', '
,
text
)
text
=
re
.
sub
(
'/
\\
*[
\\
s
\\
S]*?
\\
*/'
,
''
,
text
)
text
=
re
.
sub
(
'
\n
{2,}'
,
'
\n\n
'
,
text
)
lines
=
text
.
split
(
'
\n
'
)
count
=
0
begin
=
0
result
=
[]
start
=
-
1
end
=
0
for
index
in
range
(
len
(
lines
)):
if
len
(
lines
[
index
]
.
strip
())
==
0
:
continue
if
start
==
-
1
:
if
lines
[
index
][
-
1
]
==
'{'
:
start
=
index
count
+=
1
continue
for
char
in
lines
[
index
]:
if
char
in
[
'{'
,
'('
]:
count
+=
1
elif
char
in
[
'}'
,
')'
]:
if
count
==
1
:
end
=
index
count
-=
1
if
count
==
1
:
if
lines
[
index
][
-
1
]
in
[
'}'
,
')'
]
and
begin
!=
0
:
if
'get()'
in
lines
[
begin
]
or
'set(value)'
in
lines
[
begin
]:
for
i
in
range
(
len
(
result
)):
if
result
[
i
][
1
]
==
begin
:
result
[
i
]
=
(
result
[
i
][
0
],
index
+
1
)
break
else
:
result
.
append
((
begin
,
index
+
1
))
begin
=
0
elif
lines
[
index
][
-
1
]
not
in
[
'{'
,
'('
]:
strip
=
lines
[
index
]
.
lstrip
()
if
strip
[
0
]
==
'.'
or
strip
.
startswith
(
'get() ='
)
or
strip
.
startswith
(
'set(value) = '
):
for
i
in
range
(
len
(
result
)):
if
result
[
i
][
1
]
==
index
:
result
[
i
]
=
(
result
[
i
][
0
],
index
+
1
)
break
else
:
result
.
append
((
index
,
index
+
1
))
elif
count
==
2
and
(
lines
[
index
][
-
1
]
in
[
'{'
,
'('
]
or
lines
[
index
]
.
endswith
(
'->'
))
\
and
lines
[
begin
][
-
1
]
not
in
[
'{'
,
'('
]:
begin
=
index
result_temp
=
[]
i
=
0
while
i
<
len
(
result
):
begin
=
result
[
i
][
0
]
while
lines
[
result
[
i
][
0
]]
.
lstrip
()
.
startswith
(
'@'
):
i
+=
1
result_temp
.
append
([
begin
,
result
[
i
][
1
]])
i
+=
1
result
=
result_temp
random
.
shuffle
(
result
)
result_sort
=
result
.
copy
()
var_weight
=
{}
for
i
in
re
.
findall
(
'va[lr] (
\\
w+)'
,
text
):
var_weight
[
i
]
=
len
(
re
.
findall
(
'
\\
W'
+
re
.
escape
(
i
)
+
'
\\
W'
,
text
))
for
var
in
sorted
(
var_weight
,
key
=
lambda
k
:
var_weight
[
k
]):
for
i
in
range
(
len
(
result
)):
findall
=
re
.
findall
(
'va[lr] ('
+
re
.
escape
(
var
)
+
')'
,
lines
[
result
[
i
][
0
]])
if
len
(
findall
)
==
0
:
continue
result_sort
.
remove
(
result
[
i
])
result_sort
.
insert
(
0
,
result
[
i
])
break
result
=
result_sort
text
=
'
\n
'
.
join
(
lines
[:
start
+
1
])
+
'
\n
'
for
i
in
result
:
text
+=
'
\n
'
+
'
\n
'
.
join
(
lines
[
i
[
0
]:
i
[
1
]])
+
'
\n
'
text
+=
'
\n
'
.
join
(
lines
[
end
:])
with
open
(
path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
text
)
def
deal_junk
():
# 添加垃圾类
add_junk_activity
()
# 添加方法嵌套
add_junk_method
()
# 添加垃圾代码
add_junk_code
()
# 类成员乱序
class_disorder
()
def
get_path
(
path
):
paths
=
path
.
split
(
'
\\
'
)
return
str
(
os
.
path
.
join
(
*
paths
))
def
main
():
if
not
os
.
path
.
exists
(
get_path
(
'.
\\
src
\\
main
\\
java
\\
'
+
applicationId
[:
applicationId
.
find
(
'.'
)])):
return
# 资源混淆
deal_res
()
# 垃圾代码
deal_junk
()
# 代码处理
deal_code
()
if
__name__
==
'__main__'
:
if
os
.
path
.
exists
(
'build.gradle'
):
gradle_path
=
'build.gradle'
elif
os
.
path
.
exists
(
'build.gradle.kts'
):
gradle_path
=
'build.gradle.kts'
else
:
exit
()
applicationId
=
re
.
search
(
'applicationId .*?["
\'
](.*?)["
\'
]'
,
open
(
gradle_path
,
'r'
,
encoding
=
'utf-8'
)
.
read
())[
1
]
aesKey
=
find_text
(
' aesKey ?= ?"(.*?)"'
,
get_path
(
'.
\\
src
\\
main
\\
java'
))[
0
]
print
(
'包名: '
+
applicationId
,
'aesKey: '
+
aesKey
)
main
()
app/src/main/AndroidManifest.xml
View file @
4f6ddc12
...
...
@@ -9,12 +9,14 @@
tools:ignore=
"ScopedStorage"
/>
<application
android:name=
".MyApplicaiton"
android:allowBackup=
"true"
android:dataExtractionRules=
"@xml/data_extraction_rules"
android:fullBackupContent=
"@xml/backup_rules"
android:icon=
"@mipmap/zsd_589878"
android:label=
"@string/app_name"
android:roundIcon=
"@mipmap/ic_launcher_round"
android:requestLegacyExternalStorage=
"true"
android:requestRawExternalStorageAccess=
"true"
android:supportsRtl=
"true"
android:theme=
"@style/Theme.SolarMasterAce"
tools:targetApi=
"31"
>
...
...
app/src/main/assets/9053C0AF2FA872AE3293A5516E69AD0B.png
0 → 100644
View file @
4f6ddc12
app/src/main/java/com/zxhy/solarmasterace/MyApplicaiton.kt
0 → 100644
View file @
4f6ddc12
package
com.zxhy.solarmasterace
import
android.app.Application
class
MyApplicaiton
:
Application
()
{
companion
object
{
lateinit
var
context
:
MyApplicaiton
}
override
fun
onCreate
()
{
super
.
onCreate
()
context
=
this
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/solarmasterace/tools/AESHelper.kt
0 → 100644
View file @
4f6ddc12
package
com.zxhy.solarmasterace.tools
import
android.util.Base64
import
java.security.SecureRandom
import
javax.crypto.Cipher
import
javax.crypto.spec.GCMParameterSpec
import
javax.crypto.spec.SecretKeySpec
object
AESHelper
{
private
const
val
aesKey
=
""
private
val
cipher
by
lazy
{
Cipher
.
getInstance
(
"AES/GCM/NoPadding"
)
}
fun
encrypt
(
content
:
String
):
String
{
try
{
val
iv
=
ByteArray
(
12
).
apply
{
SecureRandom
().
nextBytes
(
this
)
}
val
contentBytes
=
content
.
toByteArray
(
Charsets
.
UTF_8
)
val
params
=
GCMParameterSpec
(
128
,
iv
)
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
secretKey
,
params
)
val
encryptData
=
cipher
.
doFinal
(
contentBytes
)
assert
(
encryptData
.
size
==
contentBytes
.
size
+
16
)
val
message
=
ByteArray
(
12
+
contentBytes
.
size
+
16
)
System
.
arraycopy
(
iv
,
0
,
message
,
0
,
12
)
System
.
arraycopy
(
encryptData
,
0
,
message
,
12
,
encryptData
.
size
)
return
String
(
Base64
.
encode
(
message
,
Base64
.
NO_WRAP
),
Charsets
.
UTF_8
)
}
catch
(
_
:
Exception
)
{
}
return
content
}
@Synchronized
fun
decrypt
(
content
:
String
):
String
{
try
{
val
con
=
content
.
replace
(
" "
.
toRegex
(),
"+"
)
val
contentByte
=
Base64
.
decode
(
con
,
Base64
.
NO_WRAP
)
require
(
contentByte
.
size
>=
12
+
16
)
val
params
=
GCMParameterSpec
(
128
,
contentByte
,
0
,
12
)
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
secretKey
,
params
)
val
decryptData
=
cipher
.
doFinal
(
contentByte
,
12
,
contentByte
.
size
-
12
)
return
String
(
decryptData
,
Charsets
.
UTF_8
)
}
catch
(
_
:
Exception
)
{
}
return
content
}
private
val
secretKey
by
lazy
{
SecretKeySpec
(
aesKey
.
toByteArray
(),
"AES"
)
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/solarmasterace/tools/AESTextView.kt
0 → 100644
View file @
4f6ddc12
package
com.zxhy.solarmasterace.tools
import
android.content.Context
import
android.util.AttributeSet
import
com.zxhy.solarmasterace.tools.KotlinExt.decode
class
AESTextView
@JvmOverloads
constructor
(
context
:
Context
,
attrs
:
AttributeSet
?
=
null
)
:
androidx
.
appcompat
.
widget
.
AppCompatTextView
(
context
,
attrs
)
{
override
fun
setText
(
text
:
CharSequence
?,
type
:
BufferType
?)
{
super
.
setText
(
text
?.
toString
().
takeIf
{
it
!=
it
?.
decode
()
}
?.
decode
()
?:
text
,
type
)
}
}
\ No newline at end of file
app/src/main/java/com/zxhy/solarmasterace/tools/KotlinExt.kt
0 → 100644
View file @
4f6ddc12
package
com.zxhy.solarmasterace.tools
import
android.view.View
import
com.zxhy.solarmasterace.MyApplicaiton
import
java.text.SimpleDateFormat
import
java.util.Locale
object
KotlinExt
{
private
val
aesMap
=
mutableMapOf
<
Int
,
String
>()
fun
Int
.
string
(
vararg
arg
:
Any
)
=
try
{
(
aesMap
[
this
]
?:
MyApplicaiton
.
context
.
getString
(
this
).
decode
()).
run
{
aesMap
[
this
@string
]
=
this
String
.
format
(
this
,
*
arg
)
}
}
catch
(
_
:
Exception
)
{
""
}
fun
String
.
decode
()
=
AESHelper
.
decrypt
(
this
)
.
replace
(
"\\n"
,
"\n"
)
.
replace
(
"\\'"
,
"'"
)
.
replace
(
"\\?"
,
"?"
)
.
replace
(
"&"
,
"&"
)
fun
Collection
<
View
>.
setOnClickListener
(
listener
:
(
View
)
->
Unit
)
{
this
.
forEach
{
it
.
setOnClickListener
(
listener
)
}
}
fun
Number
.
toFormatSize
(
count
:
Int
=
2
):
String
{
var
suffix
=
"B"
var
fSize
=
this
.
toDouble
()
if
(
fSize
>
1024
)
{
suffix
=
"KB"
fSize
/=
1024.0
}
if
(
fSize
>
1024
)
{
suffix
=
"MB"
fSize
/=
1024.0
}
if
(
fSize
>
1024
)
{
suffix
=
"GB"
fSize
/=
1024.0
}
return
String
.
format
(
"%.${count}f %s"
,
fSize
,
suffix
)
}
fun
Long
.
toFormatTime
():
String
{
return
SimpleDateFormat
(
"MMM dd,yyyy"
,
Locale
.
getDefault
()).
format
(
this
)
}
}
\ No newline at end of file
settings.gradle.kts
View file @
4f6ddc12
...
...
@@ -16,7 +16,14 @@ dependencyResolutionManagement {
repositories
{
google
()
mavenCentral
()
maven
(
"https://packages.aliyun.com/maven/repository/2279663-release-AiyNZM/"
)
{
credentials
{
username
=
"621344be8af5d39eb3f17f3e"
password
=
"(ijI1DwR7[wG"
}
}
}
}
rootProject
.
name
=
"Solar Master Ace"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment