加固与脱壳07 - 修改源码脱壳

news/2024/10/22 14:36:18/

​上文我们讨论了该如何脱壳,现在就开始实现吧。

本文不介绍如何编译源码,这块内容之前已经单独发过了,可以使用虚拟机或者 WSL,WSL体验要好些,虚拟机更方便。

看开源项目:

  • https://github.com/dqzg12300/FartExt

分析其实现思路,注意,如果需要自己编译源码,直接使用开源项目的代码是很容易被针对的,一定要将方法名等特征给改掉。

该项目的整体思路:

  1. 传递特殊的模拟参数,主动调用类方法

  2. 将指令解释器模式改为 switch 模式,这个模式源码比较简单。

  3. 在解释器执行指令的时候,判断是否为深度隐藏指令,如果是的话,对比指令特征直到触发真实指令后再 dump 方法,否则直接 dump 方法。

下面按文件顺序分析改动。

interpreter_switch_impl-inl.h

第一处

  //addint32_t regvalue=ctx->result_register.GetI();ctx->result_register=JValue();int inst_count = -1;bool flag=false;//add end

初始化一些变量,后续使用。

regvalue 是获取的返回值寄存器的值,这个值是我们在别处设置的,固定为 111111。

第二处

    //addinst_count++;uint8_t opcode = inst->Opcode(inst_data);if(regvalue==111111){if(inst_count == 0){if(opcode == Instruction::GOTO || opcode == Instruction::GOTO_16 || opcode == Instruction::GOTO_32){LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=0 opcode==GOTO "<<shadow_frame.GetMethod()->PrettyMethod().c_str();flag=true;}else{LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=0 opcode!=GOTO "<<shadow_frame.GetMethod()->PrettyMethod().c_str();dumpArtMethod(shadow_frame.GetMethod());break;}}if(inst_count == 1){if(opcode >= Instruction::CONST_4 && opcode <= Instruction::CONST_WIDE_HIGH16){LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=1 opcode==CONST "<<shadow_frame.GetMethod()->PrettyMethod().c_str();flag=true;}else{LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=1 opcode!=CONST "<<shadow_frame.GetMethod()->PrettyMethod().c_str();dumpArtMethod(shadow_frame.GetMethod());break;}}}//add end

利用 regvalue 来判断是否为脱壳主动调用触发的函数执行。

然后就是匹配指令特征,上一篇我们分析了原因,一些指令抽取壳除了隐藏指令外,还会利用跳转额外做一层指令隐藏。

没有匹配特征就执行 dump 方法指令,匹配到了就将 flag 设置为 true。

第三处

    //addif(regvalue==111111){if(inst_count==2&&flag){if(opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE){LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch INVOKE_STATIC over "<<shadow_frame.GetMethod()->PrettyMethod().c_str();dumpArtMethod(shadow_frame.GetMethod());break;}}if(inst_count>2){LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count>2 " <<shadow_frame.GetMethod()->PrettyMethod().c_str();dumpArtMethod(shadow_frame.GetMethod());break;}}//add end

因为指令跳转隐藏的特征是第3条指令是 INVOKE_STATIC 或者 INVOKE_STATIC_RANGE,所以需要等这个指令执行完之后,再去 dump 才能拿到真实指令。

至于第二个 if 感觉是个兜底行为。

如果遇到了其他的指令隐藏方式,可以自行修改这里的逻辑匹配上就行。

interpreter.cc

第一处

 //addextern "C" void dumpdexfilebyExecute(ArtMethod* artmethod);//addend

这里添加了一个方法,是一个特征,可以改成自己的名字。

第二处

  //addif(result_register.GetI()==111111){LOG(ERROR) << "fartext Execute start "<<shadow_frame.GetMethod()->PrettyMethod().c_str();}if(strstr(shadow_frame.GetMethod()->PrettyMethod().c_str(),"<clinit>")){if(ShouldUnpack()){dumpdexfilebyExecute(shadow_frame.GetMethod());}}//add end

由于一些加固厂商会禁用  dex2oat,所以它不是一个好的脱壳点。而 clinit 方法不会被 dex2oat,所以这个方法一定会走解释器执行。

这里是 fart 选取的整体脱壳点。

第三处

    //addif(result!=nullptr&&result->GetI()==111111){shadow_frame->SetVReg(cur_reg, args[0]);}else{CHECK(receiver != nullptr);shadow_frame->SetVRegReference(cur_reg, receiver);}//add end// 原逻辑shadow_frame->SetVRegReference(cur_reg, receiver);

这里没有看太懂。

猜测一下:解释器的逻辑中有个不得不提的操作就是它会通过SetVRegReference去追溯参数内容,因此如果参数不合法,会造成异常。

对于指令跳转隐藏的壳,我们需要先执行几条指令,由于我们的参数是自己构造的,所以可能运行会有问题,这里应该是做了一些修正操作。

自定义传递的参数有一些是NULL或者因无法实际获取其真实的内容而随意构造的参数。因为不能让参数真正地解引用,即不能执行其中的SetVRegReference方法,所以我们选择使用SetVReg来替代。原理是源码在非解引用的情况中均使用SetVReg和SetVRegLong来进行参数的传递,而不去解引用我们构造的参数。

第四处

        //addif(result!=nullptr&&result->GetI()==111111){shadow_frame->SetVReg(cur_reg, args[0]);break;}//add end// 原逻辑下面会调用 SetVRegReference 进行解引用,我们使用 SetVReg 代替,然后做个 break 跳出

同上。

dalvik_system_DexFile.cc

第一处

//add
#include "scoped_fast_native_object_access.h"
//add end

引入文件头

第二处

//add
extern "C" void fartextInvoke(ArtMethod* artmethod);
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod);
//add end

添加了两个函数,改下名。

第三处

//add function 将java的Method转换成ArtMethod。然后主动调用
static void DexFile_fartextMethodCode(JNIEnv* env, jclass,jobject method) {if(method!=nullptr){ArtMethod* proxy_method = jobject2ArtMethod(env, method);fartextInvoke(proxy_method);}return;
}
//add end

第四处

  //addNATIVE_METHOD(DexFile, fartextMethodCode,"(Ljava/lang/Object;)V")//add end

注册了一个JNI方法。

java_lang_reflect_Method.cc

第一处

//add
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod) {ScopedFastNativeObjectAccess soa(env);ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);return method;
}
//add end

添加了一个方法,用于将 java 方法转成 ArtMethod。

Android.bp

第一处

            cflags: [// ART is allowed to link to libicuuc directly// since they are in the same module"-DANDROID_LINK_SHARED_ICU4C","-Wno-error","-DART_USE_CXX_INTERPRETER=1"],

通过编译参数来控制,把运行模式给改成使用Switch解释器。

art_method.cc

第一处

//added code#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "runtime.h"
#include <android/log.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>#define gettidv1() syscall(__NR_gettid)
#define LOG_TAG "ActivityThread"
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
//add end

第二处

 // adduint8_t *codeitem_end(const uint8_t **pData){uint32_t num_of_list = DecodeUnsignedLeb128(pData);for (; num_of_list > 0; num_of_list--){int32_t num_of_handlers = DecodeSignedLeb128(pData);int num = num_of_handlers;if (num_of_handlers <= 0){num = -num_of_handlers;}for (; num > 0; num--){DecodeUnsignedLeb128(pData);DecodeUnsignedLeb128(pData);}if (num_of_handlers <= 0){DecodeUnsignedLeb128(pData);}}return (uint8_t *)(*pData);}extern "C" char *base64_encode(char *str, long str_len, long *outlen){long len;char *res;int i, j;const char *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";if (str_len % 3 == 0)len = str_len / 3 * 4;elselen = (str_len / 3 + 1) * 4;res = (char *)malloc(sizeof(char) * (len + 1));res[len] = '\0';*outlen = len;for (i = 0, j = 0; i < len - 2; j += 3, i += 4){res[i] = base64_table[str[j] >> 2];res[i + 1] = base64_table[(str[j] & 0x3) << 4 | (str[j + 1] >> 4)];res[i + 2] = base64_table[(str[j + 1] & 0xf) << 2 | (str[j + 2] >> 6)];res[i + 3] = base64_table[str[j + 2] & 0x3f];}switch (str_len % 3){case 1:res[i - 2] = '=';res[i - 1] = '=';break;case 2:res[i - 1] = '=';break;}return res;}// 在函数即将调用解释器执行前进行dump。extern "C" void dumpdexfilebyExecute(ArtMethod *artmethod) REQUIRES_SHARED(Locks::mutator_lock_){char *dexfilepath = (char *)malloc(sizeof(char) * 1000);if (dexfilepath == nullptr){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,methodname:" << artmethod->PrettyMethod().c_str() << "malloc 1000 byte failed";return;}int result = 0;int fcmdline = -1;char szCmdline[64] = {0};char szProcName[256] = {0};int procid = getpid();sprintf(szCmdline, "/proc/%d/cmdline", procid);fcmdline = open(szCmdline, O_RDONLY, 0644);if (fcmdline > 0){result = read(fcmdline, szProcName, 256);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,open cmdline file error";}close(fcmdline);}if (szProcName[0]){const DexFile *dex_file = artmethod->GetDexFile();const uint8_t *begin_ = dex_file->Begin(); // Start of data.size_t size_ = dex_file->Size();           // Length of data.memset(dexfilepath, 0, 1000);int size_int_ = (int)size_;memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "%s", "/sdcard/fext");mkdir(dexfilepath, 0777);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s", szProcName);mkdir(dexfilepath, 0777);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s/%d_dexfile_execute.dex", szProcName, size_int_);int dexfilefp = open(dexfilepath, O_RDONLY, 0666);if (dexfilefp > 0){close(dexfilefp);dexfilefp = 0;}else{int fp = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);if (fp > 0){result = write(fp, (void *)begin_, size_);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,open dexfilepath error";}fsync(fp);close(fp);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s/%d_classlist_execute.txt", szProcName, size_int_);int classlistfile = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);if (classlistfile > 0){for (size_t ii = 0; ii < dex_file->NumClassDefs(); ++ii){const dex::ClassDef &class_def = dex_file->GetClassDef(ii);const char *descriptor = dex_file->GetClassDescriptor(class_def);result = write(classlistfile, (void *)descriptor, strlen(descriptor));if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";}const char *temp = "\n";result = write(classlistfile, (void *)temp, 1);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";}}fsync(classlistfile);close(classlistfile);}}}}if (dexfilepath != nullptr){free(dexfilepath);dexfilepath = nullptr;}}extern "C" bool ShouldUnpack(){int result = 0;int fcmdline = -1;char szCmdline[64] = {0};char szProcName[256] = {0};int procid = getpid();sprintf(szCmdline, "/proc/%d/cmdline", procid);fcmdline = open(szCmdline, O_RDONLY, 0644);if (fcmdline > 0){result = read(fcmdline, szProcName, 256);if (result < 0){LOG(ERROR) << "fartext ArtMethod::ShouldUnpack,open cmdline file file error";}close(fcmdline);}if (szProcName[0]){const char *UNPACK_CONFIG = "/data/local/tmp/fext.config";std::ifstream config(UNPACK_CONFIG);std::string line;if (config){while (std::getline(config, line)){std::string package_name = line.substr(0, line.find(':'));if (strstr(package_name.c_str(), szProcName)){return true;}}}return false;}return false;}// 主动调用函数的dump处理extern "C" void dumpArtMethod(ArtMethod *artmethod) REQUIRES_SHARED(Locks::mutator_lock_){LOG(ERROR) << "fartext ArtMethod::dumpArtMethod enter " << artmethod->PrettyMethod().c_str();char *dexfilepath = (char *)malloc(sizeof(char) * 1000);if (dexfilepath == nullptr){LOG(ERROR) << "fartext ArtMethod::dumpArtMethodinvoked,methodname:" << artmethod->PrettyMethod().c_str() << "malloc 1000 byte failed";return;}int result = 0;int fcmdline = -1;char szCmdline[64] = {0};char szProcName[256] = {0};int procid = getpid();sprintf(szCmdline, "/proc/%d/cmdline", procid);fcmdline = open(szCmdline, O_RDONLY, 0644);if (fcmdline > 0){result = read(fcmdline, szProcName, 256);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,open cmdline file file error";}close(fcmdline);}if (szProcName[0]){const DexFile *dex_file = artmethod->GetDexFile();const uint8_t *begin_ = dex_file->Begin(); // Start of data.size_t size_ = dex_file->Size();           // Length of data.memset(dexfilepath, 0, 1000);int size_int_ = (int)size_;memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "%s", "/sdcard/fext");mkdir(dexfilepath, 0777);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s", szProcName);mkdir(dexfilepath, 0777);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s/%d_dexfile.dex", szProcName, size_int_);int dexfilefp = open(dexfilepath, O_RDONLY, 0666);if (dexfilefp > 0){close(dexfilefp);dexfilefp = 0;}else{int fp = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);if (fp > 0){result = write(fp, (void *)begin_, size_);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,open dexfilepath file error";}fsync(fp);close(fp);memset(dexfilepath, 0, 1000);sprintf(dexfilepath, "/sdcard/fext/%s/%d_classlist.txt", szProcName, size_int_);int classlistfile = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);if (classlistfile > 0){for (size_t ii = 0; ii < dex_file->NumClassDefs(); ++ii){const dex::ClassDef &class_def = dex_file->GetClassDef(ii);const char *descriptor = dex_file->GetClassDescriptor(class_def);result = write(classlistfile, (void *)descriptor, strlen(descriptor));if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";}const char *temp = "\n";result = write(classlistfile, (void *)temp, 1);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";}}fsync(classlistfile);close(classlistfile);}}}const dex::CodeItem *code_item = artmethod->GetCodeItem();const DexFile *dex_ = artmethod->GetDexFile();CodeItemDataAccessor accessor(*dex_, dex_->GetCodeItem(artmethod->GetCodeItemOffset()));if (LIKELY(code_item != nullptr)){int code_item_len = 0;uint8_t *item = (uint8_t *)code_item;if (accessor.TriesSize() > 0){const uint8_t *handler_data = accessor.GetCatchHandlerData();uint8_t *tail = codeitem_end(&handler_data);code_item_len = (int)(tail - item);}else{code_item_len = 16 + accessor.InsnsSizeInCodeUnits() * 2;}memset(dexfilepath, 0, 1000);int size_int = (int)dex_file->Size();uint32_t method_idx = artmethod->GetDexMethodIndex();sprintf(dexfilepath, "/sdcard/fext/%s/%d_ins_%d.bin", szProcName, size_int, (int)gettidv1());int fp2 = open(dexfilepath, O_CREAT | O_APPEND | O_RDWR, 0666);if (fp2 > 0){lseek(fp2, 0, SEEK_END);memset(dexfilepath, 0, 1000);int offset = (int)(item - begin_);sprintf(dexfilepath, "{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:", artmethod->PrettyMethod().c_str(), method_idx, offset, code_item_len);int contentlength = 0;while (dexfilepath[contentlength] != 0)contentlength++;result = write(fp2, (void *)dexfilepath, contentlength);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write ins file error";}long outlen = 0;char *base64result = base64_encode((char *)item, (long)code_item_len, &outlen);result = write(fp2, base64result, outlen);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write ins file error";}result = write(fp2, "};", 2);if (result < 0){LOG(ERROR) << "fartext ArtMethod::dumpdexfilebyArtMethod,write ins file error";}fsync(fp2);close(fp2);if (base64result != nullptr){free(base64result);base64result = nullptr;}}}}if (dexfilepath != nullptr){free(dexfilepath);dexfilepath = nullptr;}LOG(ERROR) << "fartext ArtMethod::dumpArtMethod over " << artmethod->PrettyMethod().c_str();}extern "C" void fartextInvoke(ArtMethod *artmethod) REQUIRES_SHARED(Locks::mutator_lock_){if (artmethod->IsNative() || artmethod->IsAbstract()){return;}JValue result;Thread *self = Thread::Current();uint32_t temp[100] = {0};uint32_t *args = temp;uint32_t args_size = (uint32_t)ArtMethod::NumArgRegisters(artmethod->GetShorty());if (!artmethod->IsStatic()){args_size += 1;}result.SetI(111111);LOG(ERROR) << "fartext fartextInvoke";artmethod->Invoke(self, args, args_size, &result, artmethod->GetShorty());}// addend

dumpArtMethod这个方法是在我们主动调用的时候,会 dump 方法指令。

ShouldUnpack这个方法是读取配置文件,看看该app是否需要脱壳。

fartextInvoke这个方法用来触发方法的主动调用的,这个方法里面设置了调用参数。

dumpdexfilebyExecute 这个方法是dump整体的 dex,指令的填补在 dumpArtMethod 方法里面。

剩下两个方法是计算一些参数,与DEX结构有关,不展开。

ActivityThread.java

第一处

//add
import cn.mik.Fartext;
//add end

第二处

            //addFartext.fartthread();//add end

Fartext.java

整个文件都是新增的,就不贴代码了,自行查看。

主要就是获取了 ClassLoader 之后,遍历所有类的所有方法,并触发该方法:

loadClassAndInvoke(appClassloader, line, dumpMethodCode_method);

dumpMethodCode_method就是上面在 DexFile.cc 中新加的 JNI 方法 fartextMethodCode

DexFile.java

第一处

    //addprivate static native void fartextMethodCode(Object m);//add end

与 DexFile.cc 对应,形成一个 JNI 方法。

所有源码分析完成,看起来还是很清晰的,后续就是改名然后编译一个 release 脱壳机了。

关注我的公众号:二手的程序员。


http://www.ppmy.cn/news/1538981.html

相关文章

Golang | Leetcode Golang题解之第462题最小操作次数使数组元素相等II

题目&#xff1a; 题解&#xff1a; func partition(a []int, l, r int) int {x : a[r]i : l - 1for j : l; j < r; j {if a[j] < x {ia[i], a[j] a[j], a[i]}}a[i1], a[r] a[r], a[i1]return i 1 }func randomPartition(a []int, l, r int) int {i : rand.Intn(r-l1…

【进阶OpenCV】 (12)--人脸检测识别

文章目录 人脸识别一、获取分类器二、代码实现1. 图片预处理2. 加载人脸检测分类器3. 检测人脸4. 标注人脸 总结 人脸识别 要实现人脸识别首先要判断当前图像中是否出现了人脸&#xff0c;这就是人脸检测。只有检测到图像中出现了人脸&#xff0c;才能据此判断这个人到底是谁。…

QD1-P23 CSS 基础选择器

本节学习&#xff1a;CSS 基础选择器&#xff08;5种&#xff09; 本节视频 https://www.bilibili.com/video/BV1n64y1U7oj?p23 基础选择器是 CSS 中最常用的选择器类型&#xff0c;它们用于选择 HTML 文档中的元素。以下是基础选择器的列表以及它们的优先级&#xff08;权重…

Redis 列表(List)

Redis 列表(List) Redis 是一个开源的&#xff0c;内存中的数据结构存储系统&#xff0c;可以用作数据库、缓存和消息中介。它支持多种类型的数据结构&#xff0c;包括字符串、哈希、列表、集合、有序集合等。本文将重点介绍 Redis 中的列表&#xff08;List&#xff09;数据结…

docker详解介绍+基础操作 (四)容器镜像

一.镜像结构和原理 Docker 镜像是 Docker 技术的核心组成部分之一&#xff0c;它用于封装应用程序及其依赖项&#xff0c;以便在任何支持 Docker 的环境中运行。了解 Docker 镜像的结构和原理对于有效使用 Docker 至关重要。以下是对 Docker 镜像结构和原理的详细介绍。 Dock…

318页PPT5G智慧校园顶层设计方案

2019年&#xff0c;中共中央、国务院印发的《中国教育现代化2035》提出从构建更为完善信息化基础环境、配置学校数字教学资源、建立学校信息化系统运行维护长效机制、统筹建设一体化智能化教学和管理与服务平台等方面建设智能化校园。因此&#xff0c;未来随着信息通信技术的不…

盒子模型的简单运用

1.块内元素与行内元素 HTML_code <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</titl…

树莓派应用--AI项目实战篇来啦-12.OpenCV摄像头云台物体追踪

1. 介绍 本项目主要是实现OpenCV识别物体&#xff0c;找出中心位置&#xff0c;根据中心位置的偏离情况来修正二维云台&#xff0c;让物体的中心位置始终处于图像的中心位置&#xff0c;要保证追踪的流畅性&#xff0c;这里引入了 PID算法来抑制云台的抖动。 2. PID算法 在实际…