由于在加壳时插入了System.loadLibrary("advmp");,看一下JNI_OnLoad

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL; if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
return JNI_ERR;
} // 注册本地方法。
registerFunctions(env); // 获得apk路径。
gAdvmp.apkPath = GetAppPath(env);
MY_LOG_INFO("apk path:%s", gAdvmp.apkPath); // 释放yc文件。
gAdvmp.ycSize = ReleaseYcFile(gAdvmp.apkPath, &gAdvmp.ycData);
if (0 == gAdvmp.ycSize) {
MY_LOG_WARNING("release Yc file fail!");
goto _ret;
} // 解析yc文件。
gAdvmp.ycFile = new YcFile;
if (!gAdvmp.ycFile->parse(gAdvmp.ycData, gAdvmp.ycSize)) {
MY_LOG_WARNING("parse Yc file fail.");
goto _ret;
} _ret:
return JNI_VERSION_1_4;
}
在这里解析了yc文件,并保存在了内存中(gAdvmp.ycFile)

看一下MainActivity

public class MainActivity extends Activity {

	public static final String TAG = "debug";

	private Button mbtnTest;

	@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); nativeLog(); mbtnTest = (Button) findViewById(R.id.btnTest);
mbtnTest.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
int result = separatorTest(2);
Log.i(TAG, "separatorTest result:" + result);
}
});
} private native int separatorTest(int value); private native static void nativeLog(); static {
System.loadLibrary("advmp");
} }

壳已经将separatorTest 转化为了native方法了

当执行时就会执行到相应的native方法(也是之前壳生成的cpp代码)

jint separatorTest(JNIEnv* env, jobject thiz, jint value) {
MY_LOG_INFO("separatorTest - value=%d", value);
jvalue result = BWdvmInterpretPortable(gAdvmp.ycFile->GetSeparatorData(0), env, thiz, value);
return result.i;
}
关键就是BWdvmInterpretPortable,这个函数实现的转发,看一下它的实现, 它在InterpC.cpp,这是一个自定义指令解释器的实现

InterpC.cpp::BWdvmInterpretPortable

jvalue BWdvmInterpretPortable(const SeparatorData* separatorData, JNIEnv* env, jobject thiz, ...) {
jvalue* params = NULL; // 参数数组。
jvalue retval; // 返回值。 const u2* pc; // 程序计数器。
u4 fp[65535]; // 寄存器数组。
u2 inst; // 当前指令。
u2 vsrc1, vsrc2, vdst; // usually used for register indexes unsigned int startIndex; // 处理参数。
va_list args;
va_start(args, thiz);
params = getParams(separatorData, args);
va_end(args); // 获得参数寄存器个数。
size_t paramRegCount = getParamRegCount(separatorData); // 设置参数寄存器的值。
if (isStaticMethod(separatorData)) {
startIndex = separatorData->registerSize - separatorData->paramSize;
} else {
startIndex = separatorData->registerSize - separatorData->paramSize;
fp[startIndex++] = (u4)thiz;
}
for (int i = startIndex, j = 0; j < separatorData->paramSize; j++ ) {
if ('D' == separatorData->paramShortDesc.str[i] || 'J' == separatorData->paramShortDesc.str[i]) {
fp[i++] = params[j].j & 0xFFFFFFFF;
fp[i++] = (params[j].j >> 32) & 0xFFFFFFFF;
} else {
fp[i++] = params[j].i;
}
} pc = separatorData->insts; /* static computed goto table */
DEFINE_GOTO_TABLE(handlerTable); // 抓取第一条指令。
FINISH(0); /*--- start of opcodes ---*/ /* File: c/OP_NOP.cpp */
HANDLE_OPCODE(OP_NOP)
FINISH(1);
OP_END

这里通过数组来模拟寄存器, 通过int指针来模拟pc寄存器,完成各个指令的运算,比如

HANDLE_OPCODE(OP_MOVE /*vA, vB*/)
vdst = INST_A(inst);
vsrc1 = INST_B(inst);
MY_LOG_VERBOSE("|move%s v%d,v%d %s(v%d=0x%08x)",
(INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
kSpacing, vdst, GET_REGISTER(vsrc1));
SET_REGISTER(vdst, GET_REGISTER(vsrc1));
FINISH(1);
OP_END

HANDLE_OPCODE(OP_MOVE /*vA, vB*/),生成的是一个goto用的标签,

在FINISH中会有一个goto来实现跳转

# define FINISH(_offset) {                                                  \
ADJUST_PC(_offset); \
inst = FETCH(0); \
/*if (self->interpBreak.ctl.subMode) {*/ \
/*dvmCheckBefore(pc, fp, self);*/ \
/*}*/ \
goto *handlerTable[INST_INST(inst)]; \
}

通过这种方式来完成多行指令的顺序执行

知道遇到RETURN指令结束执行,返回返回值

HANDLE_OPCODE(OP_RETURN /*vAA*/)
vsrc1 = INST_AA(inst);
MY_LOG_VERBOSE("|return%s v%d",
(INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1);
retval.i = GET_REGISTER(vsrc1);
/*GOTO_returnFromMethod();*/
GOTO_bail();
OP_END
..... bail:
if (NULL != params) {
delete[] params;
}
MY_LOG_INFO("|-- Leaving interpreter loop");
return retval;
执行原理结论:
  1. JNI_OnLoad 读取yc文件,获取指令
  2. native 中执行BWdvmInterpretPortable 主要入参为从yc中获得的separatorData
  3. 通过自定义解释器逐行指令指令,并返回返回值

ps: 这个开源项目提供的是一种思路,不可用于商用, 仅支持计算用指令的解释, 引用类的指令解释未实现

ADVMP 三代壳(vmp加固)原理分析(执行流程)的更多相关文章

  1. SpringBoot项目构建、测试、热部署、配置原理、执行流程

    SpringBoot项目构建.测试.热部署.配置原理.执行流程 一.项目构建 二.测试和热部署 三.配置原理 四.执行流程

  2. [Hadoop]浅谈MapReduce原理及执行流程

    MapReduce MapReduce原理非常重要,hive与spark都是基于MR原理 MapReduce采用多进程,方便对每个任务资源控制和调配,但是进程消耗更多的启动时间,因此MR时效性不高.适 ...

  3. Yarn的运行原理(执行流程)

    服务功能 ResouceManager:     1.处理客户端的请求     2.启动和监控ApplicationMaster     3.监控nodemanager     4.资源的分配和调度 ...

  4. Django drf:序列化增删改查、局部与全局钩子源码流程、认证源码分析、执行流程

    一.序列化类的增.删.改.查 用drf的序列化组件   -定义一个类继承class BookSerializer(serializers.Serializer):   -写字段,如果不指定source ...

  5. 深入理解java中HelloWorld的执行流程

    HelloWorld.java是我们学习java的第一个程序,简单的再也不能简单了,可是里面的原理以及执行流程大家都知道吗?最近在复习java知识,特地钻研了一番分享给大家! 贴出HelloWorld ...

  6. SpringMVC--从理解SpringMVC执行流程到SSM框架整合

    前言 SpringMVC框架是SSM框架中继Spring另一个重要的框架,那么什么是SpringMVC,如何用SpringMVC来整合SSM框架呢?下面让我们详细的了解一下. 注:在学习SpringM ...

  7. Servlet、Struts2、SpringMVC执行流程

    Servlet 有以下四个阶段: 1.加载和实例化 Servlet容器负责加载和实例化Servlet. 当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Ser ...

  8. Struts2 执行流程

    struts2执行原理(执行流程) 一个请求在Struts2框架中的处理大概分为以下几个步骤: 1 客户端发送请求:(HttpServletRequest)2 这个请求经过一系列的过滤器(Filter ...

  9. python编程系列---多个装饰器装饰一个函数的执行流程

    首先看一个例子 ''' 多个装饰器装饰一个函数 ''' # 定义第一个装饰器 def set_func1(func): def wrapper1(*args,**kwargs): print('装饰内 ...

  10. 老李推荐:第5章7节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles

    老李推荐:第5章7节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 循环获取并执行事件 - runMonkeyCycles   poptest是国内唯一一家培养测试开 ...

随机推荐

  1. [转帖]linux按行读取 (while read line与for-loop)

    https://cloud.tencent.com/developer/article/1655435 在linux下一般用while read line与for循环按行读取文件.现有如下test.t ...

  2. [转帖]Jmeter中线程组和setUP线程组、tearDown线程组的区别

    JMETER: setUP线程组:在测试任务ThreadGroup 运行前先被运行.通常用在运行测试任务前,做初始化工作.例如建立数据库连接初始分化工作.用户登录 tearDown线程组:在测试任务线 ...

  3. [转帖]360孵化奇安信科创板上市,IPO前清空股权赚37亿元分手费

      https://baijiahao.baidu.com/s?id=1666485645739027654&wfr=spider&for=pc 来源:IPO头条 来源:IPO头条原创 ...

  4. 【转帖】50.设置HotSpot采用解释器还是JIT编译器(-Xint、-Xcomp、Xmixed以及-Server、-Client)

    目录 1.设置HotSpot 1.设置HotSpot 1.设置采用解释器还是JIT编译器 -Xint: 完全采用解释器模式执行程序. -Xcomp: 完全采用即时编译器模式执行程序.如果即时编译出现问 ...

  5. [转帖]将 Cloudflare 连接到互联网的代理——Pingora 的构建方式

    https://zhuanlan.zhihu.com/p/575228941 简介 今天,我们很高兴有机会在此介绍 Pingora,这是我们使用 Rust 在内部构建的新 HTTP 代理,它每天处理超 ...

  6. [读书笔记]SQLSERVER企业级平台管理实践读书笔记--从等待事件判断性能瓶颈

    用到的系统试图主要有: select * from sys.dm_os_wait_statsselect * from sys.sysprocessesselect * from sys.dm_exe ...

  7. 在不同电脑间同步pycharm的配置

    备份文件同步法 最传统的方法就是把配置文件备份,然后在其它电脑上通过导入的方式来恢复,这种方法在很多软件中都实测可行. 具体对应到pycharm中,可以在pycharm菜单栏file - export ...

  8. 从零开始匹配vim(1)——选项设置

    前面我们算是对 vimscript 做了一个入门,并且实现了一个 输出 hello world 的语句.现在我们继续进行 vimscript 的学习. set语句 之前在介绍 vim 基础的时候,我们 ...

  9. springboot security 权限控制 -- @PreAuthorize 的使用

    1. 说明 security 鉴权方式常用的有两种配置,1.配置文件中配置:2.使用注解标注:他们都是基于 acess 表达式,如果需要自定义逻辑的鉴权认证,只需要自定义 access 表达式即可.本 ...

  10. 【Jmeter】Request1输出作为Request2输入-后置处理器

    [Jmeter]基础介绍-详细 接上文,继续介绍Jmeter,本文关注点为如何解决上文中提到的第一个问题,即: 需要实现Request1的返回作为Request2的RequestBody或Header ...