Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
R
RatelApi
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ratel
RatelApi
Commits
33c76c49
Commit
33c76c49
authored
Sep 29, 2020
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
脱壳组件功能基本完善
parent
c41ec9fe
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
192 additions
and
3 deletions
+192
-3
RatelUnpack.java
src/main/java/com/virjar/ratel/api/RatelUnpack.java
+14
-3
UnPackerToolKit.java
src/main/java/com/virjar/ratel/api/UnPackerToolKit.java
+178
-0
No files found.
src/main/java/com/virjar/ratel/api/RatelUnpack.java
View file @
33c76c49
package
com
.
virjar
.
ratel
.
api
;
import
java.io.File
;
import
java.lang.reflect.Member
;
import
java.lang.reflect.Method
;
import
java.nio.ByteBuffer
;
import
java.util.List
;
...
...
@@ -10,14 +11,24 @@ import java.util.List;
* 不支持vmp、不支持主动调用(可以在业务层模拟主动调用)
*/
public
interface
RatelUnpack
{
/**
* 获取当前的工作目录,在enableUnPack调用之后才会有值
*
* @return 脱壳机工作目录
*/
File
getWorkDir
();
/**
* 开启脱壳机,ratel框架将会影响虚拟机代码执行流程。这可能导致框架不稳定,已经影响app执行性能<br>
* 一般情况不建议随时开启脱壳机<br>
* 请注意,脱壳机开启需要在app运行前执行,否则错过dump时间
*
* @param workDir 需要指定一个工作目录,让脱壳机dump相关加密的指令.参数可以为空,为空系统自动分配
* @param workDir 需要指定一个工作目录,让脱壳机dump相关加密的指令.参数可以为空,为空系统自动分配
* @param dumpMethod 是否需要dump指令,大部分情况下dex整体dump就可以,此时不存在dex修复过程,相对来说性能更好
*/
void
enableUnPack
(
File
workDir
);
void
enableUnPack
(
File
workDir
,
boolean
dumpMethod
);
/**
* 根据一个className搜索dex,在存在热修复等场景下可能有多个dex,每个dex使用字节数组传递二进制内容
...
...
@@ -34,5 +45,5 @@ public interface RatelUnpack {
* @param method 一个特定的方法,可以通过各种hook手段、反射手段获取到
* @return 内存数据
*/
byte
[]
methodDex
(
Me
thod
method
);
byte
[]
methodDex
(
Me
mber
method
);
}
src/main/java/com/virjar/ratel/api/UnPackerToolKit.java
0 → 100644
View file @
33c76c49
package
com
.
virjar
.
ratel
.
api
;
import
android.app.Activity
;
import
android.util.Log
;
import
com.virjar.ratel.api.inspect.ClassLoadMonitor
;
import
com.virjar.ratel.api.rposed.RC_MethodHook
;
import
com.virjar.ratel.api.rposed.RposedHelpers
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Method
;
import
java.util.Enumeration
;
import
java.util.List
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipFile
;
import
java.util.zip.ZipOutputStream
;
import
external.org.apache.commons.io.IOUtils
;
import
external.org.apache.commons.lang3.NumberUtils
;
import
external.org.apache.commons.lang3.StringUtils
;
/**
* 脱壳工具类封装
*
* @author virjar
* @since 1.3.5
*/
public
class
UnPackerToolKit
{
private
static
final
String
TAG_UNPACK
=
"unpack"
;
/**
* 开启脱壳机,ratel框架将会影响虚拟机代码执行流程。这可能导致框架不稳定,已经影响app执行性能<br>
* 一般情况不建议随时开启脱壳机<br>
* 请注意,脱壳机开启需要在app运行前执行,否则错过dump时间
*
* @param workDir 需要指定一个工作目录,让脱壳机dump相关加密的指令.参数可以为空,为空系统自动分配
* @param dumpMethod 是否需要dump指令,大部分情况下dex整体dump就可以,此时不存在dex修复过程,相对来说性能更好
*/
public
void
enableUnPack
(
File
workDir
,
boolean
dumpMethod
)
{
RatelToolKit
.
ratelUnpack
.
enableUnPack
(
workDir
,
dumpMethod
);
}
public
void
enableUnPack
(
File
workDir
)
{
RatelToolKit
.
ratelUnpack
.
enableUnPack
(
workDir
,
false
);
}
public
void
enableUnPack
()
{
RatelToolKit
.
ratelUnpack
.
enableUnPack
(
new
File
(
RatelToolKit
.
sContext
.
getFilesDir
(),
"ratel_unpack"
),
false
);
}
/**
* 自动开启脱壳组件,他在
*/
public
static
void
autoEnable
()
{
autoEnable
(
false
);
}
/**
* 自动开启脱壳组件
*
* @param dumpMethod 标志进行整体dump还是方法粒度dump。方法粒度dump可能引发更多不稳定性
*/
public
static
void
autoEnable
(
boolean
dumpMethod
)
{
if
(
RatelToolKit
.
processName
.
equals
(
RatelToolKit
.
packageName
))
{
RposedHelpers
.
findAndHookMethod
(
Activity
.
class
,
"onResume"
,
new
RC_MethodHook
()
{
@Override
protected
void
afterHookedMethod
(
MethodHookParam
param
)
throws
Throwable
{
RatelToolKit
.
ratelUnpack
.
enableUnPack
(
new
File
(
RatelToolKit
.
sContext
.
getFilesDir
(),
"ratel_unpack"
),
dumpMethod
);
}
});
}
}
/**
* 确保某个特定的class被脱壳。
*
* @param className 一个特定的class
*/
public
static
void
ensureClassDump
(
String
className
)
{
if
(
StringUtils
.
isBlank
(
className
))
{
Log
.
e
(
TAG_UNPACK
,
"empty class:"
+
className
);
return
;
}
ClassLoadMonitor
.
addClassLoadMonitor
(
className
,
clazz
->
{
//make sure unpack component enable
RatelToolKit
.
ratelUnpack
.
enableUnPack
(
new
File
(
RatelToolKit
.
sContext
.
getFilesDir
(),
"ratel_unpack"
),
false
);
// force set dex image valid
Method
[]
declaredMethods
=
clazz
.
getDeclaredMethods
();
if
(
declaredMethods
.
length
>
0
)
{
RatelToolKit
.
ratelUnpack
.
methodDex
(
declaredMethods
[
0
]);
return
;
}
Constructor
<?>[]
declaredConstructors
=
clazz
.
getDeclaredConstructors
();
if
(
declaredConstructors
.
length
>
0
)
{
RatelToolKit
.
ratelUnpack
.
methodDex
(
declaredConstructors
[
0
]);
return
;
}
Log
.
w
(
TAG_UNPACK
,
"no available method found for class:"
+
className
);
});
}
/**
* 直接将所有的dex组装到一个apk文件中,这样可以方便使用各种自动化的反编译工具打开
*
* @return 一个apk文件,该文件包括代码和资源,但是没有签名,无法直接运行
*/
public
File
constructUnpackedApk
()
{
try
{
return
constructUnpackedApkInternal
();
}
catch
(
IOException
e
)
{
Log
.
w
(
TAG_UNPACK
,
"construct unpacked apk failed"
,
e
);
return
null
;
}
}
public
static
final
Pattern
classesIndexPattern
=
Pattern
.
compile
(
"classes(\\d+)\\.dex"
);
private
File
constructUnpackedApkInternal
()
throws
IOException
{
File
workDir
=
RatelToolKit
.
ratelUnpack
.
getWorkDir
();
if
(
workDir
==
null
||
!
workDir
.
exists
())
{
return
null
;
}
List
<
byte
[]>
dumpedDex
=
RatelToolKit
.
ratelUnpack
.
findDumpedDex
(
null
);
File
apkFile
=
new
File
(
RatelToolKit
.
sContext
.
getPackageCodePath
());
int
maxIndex
=
0
;
File
target
=
new
File
(
workDir
,
"unpacked.apk"
);
try
(
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
target
))
{
try
(
ZipOutputStream
zipOutputStream
=
new
ZipOutputStream
(
fileOutputStream
))
{
try
(
ZipFile
zipFile
=
new
ZipFile
(
apkFile
))
{
Enumeration
<?
extends
ZipEntry
>
entries
=
zipFile
.
entries
();
while
(
entries
.
hasMoreElements
())
{
ZipEntry
zipEntry
=
entries
.
nextElement
();
if
(
zipEntry
.
getName
().
startsWith
(
"META-INF/"
))
{
// 去掉签名内容
continue
;
}
zipOutputStream
.
putNextEntry
(
zipEntry
);
IOUtils
.
copy
(
zipFile
.
getInputStream
(
zipEntry
),
zipOutputStream
);
Matcher
matcher
=
classesIndexPattern
.
matcher
(
zipEntry
.
getName
());
if
(
matcher
.
matches
())
{
int
nowIndex
=
NumberUtils
.
toInt
(
matcher
.
group
(
1
));
if
(
nowIndex
>
maxIndex
)
{
maxIndex
=
nowIndex
;
}
}
}
// append dex
for
(
byte
[]
dexData
:
dumpedDex
)
{
zipOutputStream
.
putNextEntry
(
new
ZipEntry
(
"classes"
+
maxIndex
+
".dex"
));
zipOutputStream
.
write
(
dexData
);
maxIndex
++;
}
zipOutputStream
.
flush
();
}
}
}
return
target
;
}
}
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