当前位置: 首页 > news >正文

开发的角度认识一下防止模拟执行和反调试函数(RC4算法)

一:几种常见的情况介绍

在软件开发中,尤其是在安全敏感的应用程序(如加密软件、游戏、反病毒软件等)中,防止模拟执行(也称为反模拟)和反调试是非常重要的。模拟执行和调试可以被攻击者用来逆向工程软件,提取敏感信息或绕过安全机制。

1. 防止模拟执行

模拟执行是指攻击者使用虚拟机或模拟器来运行应用程序,以便分析其行为和提取信息。为了防止这种情况,开发者可以采取以下措施:

a. 检测虚拟机环境
  • 检查特征: 通过检查系统特征(如硬件信息、驱动程序、特定文件等)来判断程序是否在虚拟机中运行。例如,某些虚拟机可能会在系统中留下特定的标识符。
  • 使用 API: 利用系统 API 检测是否在虚拟机中运行。例如,在 Windows 中,可以使用 IsDebuggerPresentCheckRemoteDebuggerPresent 函数。
b. 代码混淆
  • 混淆代码: 通过代码混淆技术使得逆向工程变得更加困难。混淆可以包括重命名变量、改变控制流、插入无用代码等。
  • 动态加载: 将关键代码动态加载或加密,只有在运行时才解密和执行。
c. 反模拟技术
  • 时间检查: 通过测量代码执行的时间来检测是否在模拟环境中运行。虚拟机的执行速度可能与真实硬件不同。
  • 环境变量检查: 检查特定的环境变量或系统配置,以识别是否在模拟环境中。

2. 防止反调试

反调试是指防止攻击者使用调试器来分析和修改程序的行为。开发者可以使用以下技术来实现反调试:

a. 检测调试器
  • 使用系统 API: 在 Windows 中,可以使用 IsDebuggerPresent 函数来检查当前进程是否被调试。如果返回值为真,则表示有调试器附加。
  • 检查调试标志: 通过检查进程的状态标志(如 PROCESS_DEBUG_PORT)来判断是否有调试器附加。
b. 代码完整性检查
  • 哈希校验: 在程序运行时计算关键代码段的哈希值,并与预先计算的值进行比较。如果值不匹配,可能表示代码被修改或调试。
  • 自我保护: 在程序中嵌入自我保护机制,检测是否有调试器附加,并在检测到时采取措施(如退出程序)。
c. 反调试技术
  • 异常处理: 利用异常处理机制来检测调试器的存在。例如,调试器在程序中设置断点时,可能会引发异常。
  • 时间延迟: 在关键代码段中引入时间延迟,调试器可能会影响执行时间,从而暴露其存在。

以下是一些示例代码,展示如何在 C/C++ 中实现防止模拟执行和反调试的基本技术。这些示例包括检测调试器、检查虚拟机环境以及简单的代码混淆。

二:代码举例

1. 检测调试器

在 Windows 中,可以使用 IsDebuggerPresent 函数来检测当前进程是否被调试。

#include <windows.h>
#include <stdio.h>void checkDebugger() {if (IsDebuggerPresent()) {printf("Debugger detected! Exiting...\n");ExitProcess(1); // 退出程序} else {printf("No debugger detected.\n");}
}int main() {checkDebugger();// 其他代码...return 0;
}

2. 检查虚拟机环境

可以通过检查特定的系统特征来判断程序是否在虚拟机中运行。检查是否在 VMware 虚拟机中运行。

#include <windows.h>
#include <stdio.h>bool isRunningInVM() {// 检查特定的硬件信息HKEY hKey;if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {char value[256];DWORD value_length = sizeof(value);if (RegQueryValueEx(hKey, "SystemBiosVersion", NULL, NULL, (LPBYTE)value, &value_length) == ERROR_SUCCESS) {if (strstr(value, "VMware") != NULL) {RegCloseKey(hKey);return true; // 在 VMware 中运行}}RegCloseKey(hKey);}return false; // 不在虚拟机中运行
}int main() {if (isRunningInVM()) {printf("Running in a virtual machine! Exiting...\n");ExitProcess(1);} else {printf("Not running in a virtual machine.\n");}// 其他代码...return 0;
}

3. 代码混淆示例

以下是一个简单的代码混淆示例,通过重命名变量和函数来增加逆向工程的难度。

#include <stdio.h>int obfuscatedFunction(int a, int b) {return a + b; // 原本简单的加法
}int main() {int x = 5;int y = 10;int result = obfuscatedFunction(x, y);printf("Result: %d\n", result);return 0;
}

在实际应用中,可以使用更复杂的混淆技术,例如将函数体拆分、插入无用代码等。

4. 反调试技术示例

以下是一个使用异常处理来检测调试器的示例:

#include <windows.h>
#include <stdio.h>LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) {printf("Exception detected! Possible debugger presence.\n");ExitProcess(1);return EXCEPTION_CONTINUE_SEARCH;
}void checkForDebugger() {SetUnhandledExceptionFilter(ExceptionHandler);__try {// 故意引发异常int* p = NULL;*p = 0; // 访问空指针} __except (EXCEPTION_EXECUTE_HANDLER) {printf("Caught an exception.\n");}
}int main() {checkForDebugger();// 其他代码...return 0;
}

三:关键代码认识一下

1.检测android系统cpu相关的信息文件,防止模拟执行

static __attribute__((always_inline)) void checkCpu()
//static: 表示该函数的作用域仅限于当前文件,不能被其他文件访问。
//__attribute__((always_inline)): 这是一个编译器指令,提示编译器尽可能将该函数内联,以提高性能。
{/*** check** /sys/devices/system/cpu/possible** /sys/devices/system/cpu/present** /proc/cpuinfo** */LOGD("%s", "进入checkCpu"); //使用 LOGD 函数记录进入 checkCpu 函数的日志,通常用于调试目的。FILE* file =NULL;//打开 /sys/devices/system/cpu/possible 文件file=fopen("/sys/devices/system/cpu/possible","rb"); //二进制读取模式打开char cpuStr[128];if(file==NULL){LOGD("%s", "11111");asm_exit();}//读取文件内容char* readInPtr = fgets(cpuStr, 128, file);//使用 fgets 从文件中读取一行内容到 cpuStr 数组中fclose(file);if(NULL == readInPtr) //关闭文件后,如果读取失败,则尝试打开 /sys/devices/system/cpu/present 文件,该文件包含当前可用的 CPU 列表。{file=fopen("/sys/devices/system/cpu/present","rb");readInPtr=fgets(cpuStr,128,file);LOGD("%s", cpuStr);if(readInPtr==NULL){LOGD("%s", "22222");asm_exit();}}//记录读取的 CPU 信息LOGD("%s", cpuStr);fclose(file);readInPtr=NULL;memset(cpuStr,128,sizeof(char)); //将 readInPtr 设为 NULL,并清空 cpuStr 数组。//打开 /proc/cpuinfo 文件file =fopen("/proc/cpuinfo","rb"); //cpuinfo文件包含关于 CPU 的详细信息if(file==NULL){LOGD("%s", "33333");asm_exit();}readInPtr = fgets(cpuStr, 128, file);if(NULL==readInPtr){LOGD("%s", "44444");asm_exit();}LOGD("%s", cpuStr);}

2.反调试函数,主要用来检测TracerPid是否为0

static __attribute__((always_inline)) void checkTracerpid()
{int pid = getpid(); //使用 getpid() 函数获取当前进程的进程 IDint bufsize = 256;char filename[bufsize];char line[bufsize];int tracerpid;FILE *fp;sprintf(filename, "/proc/%d/status", pid); //用 sprintf 构建当前进程的状态文件路径,格式为 /proc/<pid>/statusfp = fopen(filename, "r"); //以只读模式打开状态文件//读取文件内容if (fp != NULL) {  //如果文件成功打开,逐行读取文件内容。while (fgets(line, bufsize, fp)) {if (strstr(line, "TracerPid") != NULL) {  //用 strstr 查找包含 "TracerPid" 的行tracerpid = atoi(&line[10]);  //通过 atoi 将 "TracerPid" 后的值转换为整数if (tracerpid != 0) {asm_exit();  //如果 tracerpid 不为 0,调用 asm_exit(),表示当前进程被跟踪。}break;}}fclose(fp);}
}void*  threadCheckTracerPid(void *pVoid) {int pid = getpid(); //同样使用 getpid() 获取当前进程的进程 ID。char file_name[20] = {'\0'};sprintf(file_name, "/proc/%d/status",pid); //构建当前进程的状态文件路径。char linestr[256];int i=0, traceid;FILE *fp;//进入一个无限循环,定期检查进程的状态文件。while(1){fp = fopen(file_name,"r");if(fp == NULL){break;  //如果文件无法打开,退出循环。}//读取文件内容while(!feof(fp)){fgets(linestr, 256, fp);while (fgets(linestr, 256, fp)) {if (strstr(linestr, "TracerPid") != NULL) {  //逐行读取文件内容,查找 "TracerPid" 行。//如果找到,解析 tracerpid,并在不为 0 时调用 asm_exit()。traceid = atoi(&linestr[10]);if (traceid != 0) {asm_exit();}break;}}}fclose(fp);sleep(5); //线程休眠 5 秒,然后重新检查。}}

四:再来看看RC4算法叭

RC4(Rivest Cipher 4)是一种流加密算法,由 Ronald Rivest 设计。它以其简单性和速度而闻名,广泛用于各种加密协议中,如 SSL/TLS 和 WEP。以下是 RC4 算法的基本原理、加密和解密过程的详细说明。

RC4 算法概述

RC4 是一种对称密钥加密算法,意味着加密和解密使用相同的密钥。它的工作原理基于一个伪随机生成器,生成一个密钥流,然后将该密钥流与明文进行异或运算以生成密文。

主要步骤

  1. 密钥调度算法(KSA):

    • 初始化一个状态数组 S,其大小为 256。
    • 使用给定的密钥对状态数组进行置换。
  2. 伪随机生成算法(PRGA):

    • 生成伪随机字节流。
    • 将生成的字节流与明文进行异或运算以生成密文。

加密过程

  1. 密钥调度算法(KSA):

    def KSA(key):key_length = len(key)S = list(range(256))j = 0for i in range(256):j = (j + S[i] + key[i % key_length]) % 256S[i], S[j] = S[j], S[i]  # 交换return S
    
  2. 伪随机生成算法(PRGA):

    def PRGA(S):i = 0j = 0while True:i = (i + 1) % 256j = (j + S[i]) % 256S[i], S[j] = S[j], S[i]  # 交换K = S[(S[i] + S[j]) % 256]yield K  # 生成伪随机字节
    
  3. 加密函数:

    def RC4_encrypt(key, plaintext):S = KSA(key)keystream = PRGA(S)ciphertext = bytearray()for byte in plaintext:ciphertext.append(byte ^ next(keystream))  # 异或运算return bytes(ciphertext)
    

解密过程

RC4 的解密过程与加密过程相同,因为它是对称的。只需使用相同的密钥和密文进行异或运算即可恢复明文。

def RC4_decrypt(key, ciphertext):return RC4_encrypt(key, ciphertext)  # 使用相同的函数

示例

以下是一个完整的示例,展示如何使用 RC4 算法进行加密和解密:

def main():key = b'SecretKey'  # 密钥plaintext = b'Hello, World!'  # 明文# 加密ciphertext = RC4_encrypt(key, plaintext)print(f'Ciphertext: {ciphertext}')# 解密decrypted_text = RC4_decrypt(key, ciphertext)print(f'Decrypted: {decrypted_text}')if __name__ == "__main__":main()

注意事项

  1. 密钥长度: RC4 支持任意长度的密钥,但通常使用 40 到 2048 位的密钥。较长的密钥提供更高的安全性。
  2. 安全性: RC4 已被发现存在多种安全漏洞,尤其是在使用不当时。因此,在新的应用中,建议使用更安全的加密算法,如 AES。
  3. 流加密: RC4 是流加密算法,适合处理任意长度的数据流,但不适合处理大块数据。

案例:

bool rc4_crypt(JNIEnv* env,const char* ss)__attribute((__annotate__(("bbb"))))
{jclass fkclazz = NULL;jmethodID fkId = NULL;jobject fkObj = NULL;jmethodID fkMethodIdadd = NULL;jmethodID fkMethodIdsub = NULL;jmethodID fkMethodIdxor = NULL;fkclazz		= env->FindClass( "com/xxx");fkId		= env->GetMethodID( fkclazz, "<init>", "()V" );   /* 获取构造函数 */fkObj		= env->NewObject( fkclazz, fkId );                /* 创建对象 */fkMethodIdadd	= env->GetMethodID( fkclazz, "add", "(II)I" );fkMethodIdsub	= env->GetMethodID( fkclazz, "sub", "(II)I" );fkMethodIdxor	= env->GetMethodID( fkclazz, "xor", "(II)I" );//flag{You_f1nd_ME_X1a}unsigned  char s[256];memset(s,0,256);//char key[256] = "0x123456789";unsigned char key[256];memset(key,0,256);// 计算 key 数组的其他元素key[0]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,20,28);key[1]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],72);key[2]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],1);key[3]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],2);key[4]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],3);key[5]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],4);key[6]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],5);key[7]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],6);key[8]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],7);key[9]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],8);key[10]	= (int)env->CallIntMethod( fkObj, fkMethodIdsub,key[0],9);key[11]	= (int)env->CallIntMethod( fkObj, fkMethodIdxor,key[0],key[0]);unsigned  char data[256];memset(data,0,256);int result = 0;memcpy(data,ss, strlen(ss)+1);//char data[512] = {228,21,196,237,166,47,86,16,187,19,235,173,117,86,199,187,233,185,204,2,58,80,159,54,144,105,190,125,66,68,202,198,212,36,92,210,185,36,193,24,147,179,234};    //找到规律,小数保留(max117),大数减去256(min125)unsigned  char datas[45]={115,3,112,89,84,0,218,91,44,20,154,4,97,87,232,255,24,222,95,226,189,204,61,124,116,119,63,225,154,170,191,158,195,47,108,102,1,0,83,24,20,245};unsigned long len = 42;jint demo=datas[0];result	= (int)env->CallIntMethod( fkObj, fkMethodIdadd, 6, demo );datas[0]=result;demo=datas[20];//189result	= (int)env->CallIntMethod( fkObj, fkMethodIdadd, len, demo );demo=datas[19];//226result	= (int)env->CallIntMethod( fkObj, fkMethodIdxor, demo, result );result	= (int)env->CallIntMethod( fkObj, fkMethodIdadd, (jint)result, 331);int ii=0;int jj=0;unsigned char kk[256]={};memset(kk,0,256);unsigned char temps = 0;// RC4 算法的核心部分for(ii=0;ii<result;ii++){s[ii]=ii;         //0-255赋给skk[ii]=key[ii%len];   //将k重新计算}for(ii=0;ii<result;ii++){jj=(jj+s[ii]+kk[ii])%result;    //给j赋temps=s[ii];checkTracerpid();s[ii]=s[jj];s[jj]=temps;    //s[i]和s[j]交换}int i=0,j=0,t=0;jint demos=0;unsigned long k=0;unsigned char temp;checkTracerpid();for(k=0;k<len;k++){i=(i+1)%result;            //固定方式生成的ij=(j+s[i])%result;          //固定方式生成的jtemp=s[i];s[i]=s[j];s[j]=temp;             //交换t=(s[i]+s[j])%result;      //固定方式生成的t//data[k]^=s[t];          //异或运算demo=data[k];demos=s[t];data[k]	= (int)env->CallIntMethod( fkObj, fkMethodIdadd, demo ,demos);}// 验证数据for(k=0;k<len;k++){checkTracerpid();if(data[k]!=datas[k]){return false;}}return true;
}

http://www.mrgr.cn/news/66933.html

相关文章:

  • 「Mac畅玩鸿蒙与硬件17」鸿蒙UI组件篇7 - Animation 组件基础
  • 关于基于AGI和大模型技术下养老服务高质量发展解决方案项目,以及实现代码过程实战
  • Type-C接口 PD 受电端(sink)快充协议芯片,XSP08Q应用小家电领域的方案
  • 计算机网络:网络层 —— 网络地址转换 NAT
  • MySQL初学之旅(1)配置与基础操作
  • 【数据结构】构造函数和析构函数
  • mysql约束和高级sql
  • 【三维重建】Semantic Gaussians:开放词汇的3DGS场景理解
  • 【CAP理论:概念、解释与应用】
  • 画动态爱心(Python-matplotlib)
  • Django的manage.py命令用法
  • element-plus table tableRowClassName 无效
  • 最新三维视觉下的扩散模型综述——Diffusion Models in 3D Vision: A Survey
  • windows10显示计算机设置,我的电脑,此电脑设置
  • 软媒市场自助发稿平台的优势解析
  • 怎样用云手机进行FB矩阵运营而不被封号?
  • 【Ag-Grid】 使用笔记 Vue3 + Vite(一)
  • Kafka自动生产消息软件(自动化测试Kafka)
  • gomarkdown漏洞CVE-2024-44337--手把手教你go-fuzz模糊测试引擎如何进行漏洞挖掘
  • Modbus解析流程全面升级:体验全新核心与终极优化!
  • SpringBoot中使用SpringTask实现定时任务
  • 设置Three.js响应式画布
  • Android RecyclerView ,使用ItemDecoration设置边距的大坑:左右边距不均匀/不同,已解决。
  • 【C++课程学习】:string的模拟实现
  • 防患于未然才是预警大屏的意义所在,看它是如何做的?
  • java-web-day14-项目属性配置和bean的管理