首页
社区
课程
招聘
最新版shield纯算分析
发表于: 2024-5-6 09:55 11114

最新版shield纯算分析

2024-5-6 09:55
11114

版本是8.32.0,2024/04/19发布的,侵权系删!!!
抓包就不抓了,每个包headers里面都有一个shield,格式是XYAAAAAQAAAAEAAABTAAAAUzUWEe0xG1IbD9/c+qCLOlKGmTtFa+lG434NfuFQTKRBzI2yneNkH53+rOYKz8Mi1cx+2KY3QQwYRWWLN7/z1S011Oc8PZAcZStVDuw6DCul1soQ
这个参数这个版本搜不到,它是通过okhttp3的Interceptor添加上去的,添加的位置在so层.这个app有点特殊,不能说太多,关键位置都打码了.此篇是删减版

既然是加密就得确认入参,先打印看看有哪些Interceptor,需要以spawn的形式启动,新版有libmsaoaidsec.so的frida反调试,拆开来是msa open aid security 不是读msao,很多人读错了,网上自行寻找过frida检测的方法,我这里不介绍.

hook验证发现是在com.xxxxx.shield.http.xxxHttpInterceptor类的intercept方法把shield添加上去的,传入的时候头部没有shield,下一个拦截器y62.b里出现了,所以是这个位置

有敏感信息,不展示

后续的unidbg中的入参来自这组hook的结果,这组是get请求,post请求自己hook一个,没什么区别,只是多传了组参数.
com.xxxxx.shield.http.xxxHttpInterceptor类下的intercept方法,是一个native方法,用的jadx1.5.0,感觉更新后反编译速度快了,不那么占内存了,搜索也不卡,快去试试吧!
图片不展示,有敏感信息
上面还有3个native方法,看名字的意思就知道初始化,用unidbg跑的话必须要先初始化,否则会跑不出结果或者异常结果,这得益于这个app没有把名字去掉可以看出来,有的没有这种init字眼,或者只有一个native方法,根据不同的入参数值来进行初始化,初始化和入参共一个函数,比如mt的和阿里的.网上也有unidbg的代码,我这里直接放代码了,后续用unidbg来还原算法

先确认下是哪个so

结果

libxyass.so,这个版本只有64位的so,拉到ida64先看一下
在这里插入图片描述
都是seg段,加固了,用sodump dump下来再修复,之前星球发过这个工具
dump下来的是正常的,上面的"进度条"变蓝了
c

同时可以看到这个so真正的名字是libshield.so

上面hook的结果中headers有很多参数,我测试过只有url,xy-common-params,xy-direction会影响最终结果,除了这些参数外,unidbg中补的环境也有影响结果的参数,deviceId和main_hmac.除此以外还有些小参数在xy-common-params会有体现,后面我们遇到的时候再说.

先trace一份结果,后面遇到需要trace的地方看这份结果就行了,时机的话选择so刚加载进来的位置
在这里插入图片描述

trace的结果10多万行,先放一边,这种入参那么多的肯定是从后往前推效果更好.
先从jni的日志中开始定位,0xbcf2c
在这里插入图片描述
在这里插入图片描述
这里newstringutf是将cstring转jstring,所以要追踪v89的来源,600多行直接找太麻烦,直接监控v89这块内存的写入
看下这处的汇编在这里插入图片描述
BCF2C执行完后跳到13a18执行,13a18应该是newstringutf,这样的话x1就是第一个参数,下断看下

在这里插入图片描述
地址是0x40461018,监控这块内存的写入,最终结果是134字节

在这里插入图片描述
前两个字节是5859是XY拼接的,看后132字节就行了
在这里插入图片描述
[libc.so]0x1c1f8 pc指向libc的话一般用的是memcpy函数,直接看lr寄存器的位置就可以了,0x9f0f0
在这里插入图片描述
0x9f0f0的上一行memcpy,9F0EC下断看下内存地址,memcpy的第一个参数buffer,第二个源数据,第三个源数据长度,看第二个参数地址
在这里插入图片描述
继续监控0x4059a018
在这里插入图片描述
0x4bfac跳过去看一下
在这里插入图片描述
有base64 table,跳过去看了下是标准的,同时看到了v3 += 3LL;和v4 += 4;确实符合base64的是3字节转4字节数据,数据来源是a2,hook验证下是不是标准的,4BF44的第二个参数
在这里插入图片描述
在这里插入图片描述
验证了,是标准base64

第3个参数0x63是参数2的长度所以目标变成了追踪这0x63个字节,地址是0x40456098
在这里插入图片描述
还是之前的trace追踪方法
在这里插入图片描述
0x40593000,接着跟
在这里插入图片描述

发现前16字节和后面字节的赋值位置不一样,先看前面的,0x497b4
在这里插入图片描述
来自v4,v4来自a1,函数是sub_49650,在49650下断
在这里插入图片描述
在这里插入图片描述
是个指针,小端序
在这里插入图片描述
跟踪0x40453196偏移16字节的位置
在这里插入图片描述
0x4930c
在这里插入图片描述
上面的两个01是固定在so里面的,看后面的两个0x53,都来自于v7,v7和a1有关,看下a1是什么
sub_4926C
在这里插入图片描述
一个指针,指向0x4058e010
在这里插入图片描述
(*(*a1 + 20LL) + *(*a1 + 24LL) + *(*a1 + 28LL) + 24);这行代码的结果是0x53怎么来的?
0x07+0x24+0x10+0x18=0x53,前3个数字就是上图中的20,24,28位置的值,还要继续追吗?其实改改入参的话会发现这个结果始终是0x53,为了严谨点还是跟过去看看吧.
只需要3个字节就可了

在这里插入图片描述
0x49138
在这里插入图片描述
这个函数是sub_4908C,来自a2,a5,a7
a2和a5都是指针
在这里插入图片描述
长度是07,是build,在xy-common-params里面有
在这里插入图片描述
长度0x24,是deviceid
在这里插入图片描述
长度0x10,这个很重要,是后续魔改md5的最终结果,这个非常重要
除了这几个还有app_id,后续我不再这样跟了,出现的时候我说一声

在这里插入图片描述
0x497dc,都是赋值的位置,我们要找最初生成的位置,和上面一样的跟踪,这里不重复上面的内容了,只需定位一次就可定位到,在0x5126c
在这里插入图片描述
在这里插入图片描述
一轮8个字节,一共10轮,再加后序添加的3个字节,入参是a3,先看一眼入参
在这里插入图片描述
也是0x53个字节,后16字节是前面说到的md5的结果,ECFAAF01是app_id,在xy-common-params中有,
和上面跟踪0x53一样的思路,01,02是固定在so里的,07,24,10就是我们上面跟踪的,中间的结果由build+deviceid拼接成.所以最终的目标就是后16个字节,魔改md5的结果.
等等,还有眼前的这83个字节转换呢!
这个是标准的RC4,先介绍下RC4
1 RC4是一种流加密算法,密钥长度可变。它加解密使用相同的密钥,因此也属于对称加密算法。
2 通过算法生成一个256字节的S-box。再通过算法每次取出S-box中的某一字节K.将K与明文做异或得到密文。
下面是我学RC4时的笔记可以参考一下,或者网上找篇文章学习下

我刚开始弄的时候没看出来RC4,虽然他一直在swap我也没想到,遇到的少,就是纯看汇编和c代码还原的,以及前面trace的文本,还原出来是查表法实现的

这里就不带大家扣了,仔细点对着汇编来还原应该都可以,我们来看看这个256字节的表是怎么来的,能否还原最初始的秘钥
在使用RC4加密时,不应使用弱密钥。当密钥长度超过128位时,至今也没有有效的破解手段,所以还是看看能不能在so里找到秘钥,硬破解不一定能弄.
sub_511E0的入参就是256字节的表,不过中间隔了3个00
在这里插入图片描述
0xbffff090这块内存是栈内存

在这里插入图片描述
pc和lr寄存器指向的都是libc,这样不太好跟,看下调用栈,断下按bt
图片不展示,敏感信息
0x04956c
在这里插入图片描述
v21来自51698,这个函数传了13个字节,会不会是秘钥?
在这里插入图片描述

扩展完和上面256字节的表对的上,这样秘钥也找到了,拿去加密验证下
在这里插入图片描述
验证了,是标准RC4,所有最终需要的参数就是后16字节,魔改MD5的结果

还是上面说的跟踪方法,这里不跟了,地址是0x539DC,这个函数一共跑5次,对应md5的updata函数

输出内容太多了,就展示部分,这其实是个hmac md5,hamc方法是标准的,需要结合上面hook的日志来看,我这里直接说结果了,你们自己验证下.hmac去之前ks那篇又讲或者网上找些资料.
第一次 update 64字节的0x36的扩展秘钥得到context1
第二次 update 64字节的0x5c的扩展秘钥得到context2
这两次的魔数(iv)都是
a0 = 0x10325476
b0 = 0x98badcfe
c0 = 0xefcdab89
d0 = 0x67452301
和标准的不一样
第三次 魔数用的context1,入参用的url(去掉协议和问号) + xy-common-params + xy-direction + xy-platform-info,也对应着update过程
第四次 就是第三次的填充结果的updata,不满512分组要填充,看到有0x80想到填充,得到第一个md5的结果
第五次 魔数用的context2,入参是第四次得到的md5的结果,最终加密成A9 EC EC C3 C7 90 C4 E7 78 41 37 63 F3 F5 3D AB
单看一二和三四五就发现是标准hmac

ida中c代码太长了,500行,我这里就截关键位置
1 k表
2 魔数
3 循环左移ida中的是循环右移,需要用32减去,同时循环左移的位数也改了,不是32减标准的,没规律
在这里插入图片描述
4 md5 64轮,每16轮算一小轮,第3小轮改了运算顺序
以下是一个标准的md5

魔改的md5

魔改点我上面说的也很清楚了,自己去还原一遍才知道md5的算法细节.

由上面的分析,最终需要逆向的参数就是hmac的秘钥
在这里插入图片描述
用扩展秘钥1异或0x36或秘钥2异或0x5c得到,扩展秘钥上面hook md5的代码可以得到,我没贴图.
追踪这64个字节,还是和上面一样的方法,最终生成的位置是0x52A5C
在这里插入图片描述
veorq_s8是两个16字节的数据逐字节异或,这里看汇编意思更清楚

在这里插入图片描述
异或的两个16进制来自X19和X24,hook看一下,注意这个hook需要在callinitialize这个native函数执行前调用,原因后面再说

在这里插入图片描述
一共6轮,看下他们异或的结果
在这里插入图片描述
中间0x40字节就是我们需要的,前16字节不知道是什么,最后16字节都是0x10,应该能猜出这是pkcs7填充过的吧.网上有文章说是魔改了AES,来看下findcrypt能不能找到
在这里插入图片描述
AES最原始的名称就是RijnDael,之前有文章介绍过这个RijnDael,这里不说了.
也就是说我们hmac的秘钥是来自中间的0x40字节,这有点奇怪啊,为什么会要填充前的0x40字节?答案就是这0x60数据是AES解密出来的,以nopadding解密出来后面会带填充的值.这样的话加密结果也是0x60字节,我们需要找到这0x60字节,这是入参
在这里插入图片描述
获取了
看下jni的日志,解密的上几行,获取了DeviceId和main_hmac的值,这个main_hmac值来源于文件访问,最终源头是服务器返回的,看着像是base64过的,前面有一个base64是标准的,这个应该也会是,转成16进制看看
在这里插入图片描述
16进制的结果是0x60个字节,这一个就是加密的数据了,可以发现前16字节和第二轮的hexString19===EE1755BFE9D97ECE5D3215AF401FA9E7对的上,后面的错开排列也对的上,这意思也很明显,上一组的密文异或一个东西得到解密的"明文",这个明文打个引号,那不就是CBC模式吗?解密出的"明文"是最初的明文异或iv得到的,最初的iv是0x3101323404020861667A666607176639,后续的iv用上一轮加密的结果,CBC模式可以防止替换某段密文来达到替换明文的效果.这里其实看汇编也能看懂
,X19是iv,X24是明文异或iv的结果,他两异或就是最原始明文
这个是AES解密,密文知道了,iv知道了,key是39923e3c-6c6e-3891-945a-fd03c4796eb3,36字节,但是AES没有36字节的key,只有16,24和32字节的,跟踪下这个key的使用,跟踪jni日志中的GetStringUtfChars,0x17d10,一路往下跟,来到秘钥编排的位置sub_51884,上面我们说hook需要在callinitialize这个native函数执行前调用,原因就是在callinitialize函数已经完成了解密,如果在最终的callintercept函数调用就好hook不上.
在这里插入图片描述
这里可以看到只使用了前16字节,填充方式也确认了,基本的都确认了,但是这个是魔改AES,魔改了秘钥扩展部分,改了Rcon,s盒,既然是魔改AES,听说魔改程度很大,不建议对着代码标准AES来改,因为这个AES采用的是八个大的合并表,所以直接扣代码会比跟简单些,主要就是下断,跟汇编,之前zh的x-96用的也魔改的表合并AES,那篇有讲过这么扣代码,这里我就不说了,最多花个几天时间肯定能搞定.

Java.perform(function (){
    let xxxHttpInterceptor = Java.use("com.xxxxx.shield.http.xxxHttpInterceptor");
    xxxHttpInterceptor["intercept"].overload('okhttp3.Interceptor$Chain', 'long').implementation = function (chain, j2) {
    var request = chain.request();
    console.log('1111111111111111111111:',j2)
    var url = request.url().toString()
    console.log('url:',url)
    var headerString = request.headers().toString();
    console.log('headers:',headerString)
    console.log('1111111111111111111111')
    let result = this["intercept"](chain, j2);
    return result;
};
})
Java.perform(function (){
    let xxxHttpInterceptor = Java.use("y62.b");
     xxxHttpInterceptor["intercept"].overload('okhttp3.Interceptor$Chain').implementation = function (chain) {
    console.log('22222222222222222222222')
    var request = chain.request();
    var url = request.url().toString()
    console.log('url:',url)
    var headerString = request.headers().toString();
    console.log('headers:',headerString)
    console.log('22222222222222222222222')
    let result = this["intercept"](chain);
    return result;
};
})
Java.perform(function (){
    let xxxHttpInterceptor = Java.use("com.xxxxx.shield.http.xxxHttpInterceptor");
    xxxHttpInterceptor["intercept"].overload('okhttp3.Interceptor$Chain', 'long').implementation = function (chain, j2) {
    var request = chain.request();
    console.log('1111111111111111111111:',j2)
    var url = request.url().toString()
    console.log('url:',url)
    var headerString = request.headers().toString();
    console.log('headers:',headerString)
    console.log('1111111111111111111111')
    let result = this["intercept"](chain, j2);
    return result;
};
})
Java.perform(function (){
    let xxxHttpInterceptor = Java.use("y62.b");
     xxxHttpInterceptor["intercept"].overload('okhttp3.Interceptor$Chain').implementation = function (chain) {
    console.log('22222222222222222222222')
    var request = chain.request();
    var url = request.url().toString()
    console.log('url:',url)
    var headerString = request.headers().toString();
    console.log('headers:',headerString)
    console.log('22222222222222222222222')
    let result = this["intercept"](chain);
    return result;
};
})
var addrRegisterNatives = null;
 
var symbols = Module.enumerateSymbolsSync("libart.so");
for (var i = 0; i < symbols.length; i++) {
    var symbol = symbols[i];
    if (symbol.name.indexOf("art") >= 0 &&
        symbol.name.indexOf("JNI") >= 0 &&
        symbol.name.indexOf("RegisterNatives") >= 0 &&
        symbol.name.indexOf("CheckJNI") < 0) {
 
        addrRegisterNatives = symbol.address;
        console.log("RegisterNatives is at ", symbol.address, symbol.name);
        break
    }
}
if (addrRegisterNatives) {
    Interceptor.attach(addrRegisterNatives, {
        onEnter: function (args) {
            var env = args[0];        // jni对象
            var java_class = args[1]; // 类
            var class_name = Java.vm.tryGetEnv().getClassName(java_class);
            var taget_class = "com.xxxxx.shield.http.xxxHttpInterceptor";   //111 某个类中动态注册的so
            if (class_name === taget_class) {
                //只找我们自己想要类中的动态注册关系
                console.log("\n[RegisterNatives] method_count:", args[3]);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    // Java中函数名字的
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    // 参数和返回值类型
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    // C中的函数内存地址
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    // 地址、偏移量、基地址
                    var offset = ptr(fnPtr_ptr).sub(find_module.base);
                    console.log("name:", name, "sig:", sig,'module_name:',find_module.name ,"offset:", offset);
 
                }
            }
        }
    });
}
// 动态注册函数地址
// frida -U -f com.xingin.xxx -l hook_so_register.js 
var addrRegisterNatives = null;
 
var symbols = Module.enumerateSymbolsSync("libart.so");
for (var i = 0; i < symbols.length; i++) {
    var symbol = symbols[i];
    if (symbol.name.indexOf("art") >= 0 &&
        symbol.name.indexOf("JNI") >= 0 &&
        symbol.name.indexOf("RegisterNatives") >= 0 &&
        symbol.name.indexOf("CheckJNI") < 0) {
 
        addrRegisterNatives = symbol.address;
        console.log("RegisterNatives is at ", symbol.address, symbol.name);
        break
    }
}
if (addrRegisterNatives) {
    Interceptor.attach(addrRegisterNatives, {
        onEnter: function (args) {
            var env = args[0];        // jni对象
            var java_class = args[1]; // 类
            var class_name = Java.vm.tryGetEnv().getClassName(java_class);
            var taget_class = "com.xxxxx.shield.http.xxxHttpInterceptor";   //111 某个类中动态注册的so
            if (class_name === taget_class) {
                //只找我们自己想要类中的动态注册关系
                console.log("\n[RegisterNatives] method_count:", args[3]);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    // Java中函数名字的
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    // 参数和返回值类型
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    // C中的函数内存地址
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var find_module = Process.findModuleByAddress(fnPtr_ptr);
                    // 地址、偏移量、基地址
                    var offset = ptr(fnPtr_ptr).sub(find_module.base);
                    console.log("name:", name, "sig:", sig,'module_name:',find_module.name ,"offset:", offset);
 
                }
            }
        }
    });
}
// 动态注册函数地址
// frida -U -f com.xingin.xxx -l hook_so_register.js 
name: initializeNative sig: ()V module_name: libxyass.so offset: 0xbd8bc
name: intercept sig: (Lokhttp3/Interceptor$Chain;J)Lokhttp3/Response; module_name: libxyass.so offset: 0xbc6b4
name: initialize sig: (Ljava/lang/String;)J module_name: libxyass.so offset: 0xbc3c8
name: destroy sig: (J)V module_name: libxyass.so offset: 0xbc380
name: initializeNative sig: ()V module_name: libxyass.so offset: 0xbd8bc
name: intercept sig: (Lokhttp3/Interceptor$Chain;J)Lokhttp3/Response; module_name: libxyass.so offset: 0xbc6b4
name: initialize sig: (Ljava/lang/String;)J module_name: libxyass.so offset: 0xbc3c8
name: destroy sig: (J)V module_name: libxyass.so offset: 0xbc380
代码不公开
代码不公开
String traceFile = "unidbg-android/src/test/java/com/xxx/trace/trace2.txt";
        PrintStream traceStream = null;
        try{
            traceStream = new PrintStream(new FileOutputStream(traceFile), true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
emulator.traceCode(0x40000000,0x40000000+1245184).setRedirect(traceStream);
String traceFile = "unidbg-android/src/test/java/com/xxx/trace/trace2.txt";
        PrintStream traceStream = null;
        try{
            traceStream = new PrintStream(new FileOutputStream(traceFile), true);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
emulator.traceCode(0x40000000,0x40000000+1245184).setRedirect(traceStream);
public void HookByConsoleDebugger() {
    Debugger debugger = emulator.attach();
    debugger.addBreakPoint(module.base+0xBCF2C);
    }
public void HookByConsoleDebugger() {
    Debugger debugger = emulator.attach();
    debugger.addBreakPoint(module.base+0xBCF2C);
    }
emulator.traceWrite(0x40461018,0x40461018+134);
emulator.traceWrite(0x40461018,0x40461018+134);
emulator.traceWrite(0x4058e024,0x4058e024+9);
emulator.traceWrite(0x4058e024,0x4058e024+9);
总结
通过算法生成一个256字节的S-box。
再通过算法每次取出S-box中的某一字节K.
将K与明文做异或得到密文。
 
重点是打乱的s盒
1、先初始化状态向量S(256个字节,用来作为密钥流生成的种子1)
    按照升序,给每个字节赋值0,1,2,3,4,5,6.....,254,255
2、初始密钥(由用户输入),长度任意
    如果输入长度小于256个字节,则进行轮转,直到填满
    例如输入密钥的是1,2,3,4,5   ,  那么填入的是1,2,3,4,5,1,2,3,4,5,1,2,3,4,5........
3、开始对状态向量S进行置换操作(用来打乱初始种子1)
    j = 0;
   for (i = 0 ; i < 256 ; i++){
    j = (j + S[i] + T[i]) % 256;
    swap(S[i] , S[j]);
   }
4、最后是秘钥流的生成与加密,很多人在这里不是特别理解
    假设我的明文字节数是datalength=1024个字节(当然可以是任意个字节)
    i=0;
    j=0;
    while(datalength--){//相当于执行1024次,这样生成的秘钥流也是1024个字节
         i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        swap(S[i] , S[j]);
        t = (S[i] + S[j]) % 256;
        k = S[t];这里的K就是当前生成的一个秘钥流中的一位
        //可以直接在这里进行加密,当然也可以将密钥流保存在数组中,最后进行异或就ok
        data[]=data[]^k; //进行加密,"^"是异或运算符
    }
    解密按照前面写的,异或两次就是原文,所以只要把密钥流重新拿过来异或一次就能得到原文了
    这样就完成了一次生成密钥流及加密的过程,这也是RC4的全部工作,是不是很简单呢?
总结
通过算法生成一个256字节的S-box。
再通过算法每次取出S-box中的某一字节K.
将K与明文做异或得到密文。
 
重点是打乱的s盒
1、先初始化状态向量S(256个字节,用来作为密钥流生成的种子1)
    按照升序,给每个字节赋值0,1,2,3,4,5,6.....,254,255
2、初始密钥(由用户输入),长度任意
    如果输入长度小于256个字节,则进行轮转,直到填满
    例如输入密钥的是1,2,3,4,5   ,  那么填入的是1,2,3,4,5,1,2,3,4,5,1,2,3,4,5........
3、开始对状态向量S进行置换操作(用来打乱初始种子1)
    j = 0;
   for (i = 0 ; i < 256 ; i++){
    j = (j + S[i] + T[i]) % 256;
    swap(S[i] , S[j]);
   }
4、最后是秘钥流的生成与加密,很多人在这里不是特别理解
    假设我的明文字节数是datalength=1024个字节(当然可以是任意个字节)
    i=0;
    j=0;
    while(datalength--){//相当于执行1024次,这样生成的秘钥流也是1024个字节
         i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        swap(S[i] , S[j]);
        t = (S[i] + S[j]) % 256;
        k = S[t];这里的K就是当前生成的一个秘钥流中的一位
        //可以直接在这里进行加密,当然也可以将密钥流保存在数组中,最后进行异或就ok
        data[]=data[]^k; //进行加密,"^"是异或运算符
    }
    解密按照前面写的,异或两次就是原文,所以只要把密钥流重新拿过来异或一次就能得到原文了
    这样就完成了一次生成密钥流及加密的过程,这也是RC4的全部工作,是不是很简单呢?
def swap(list, swap1, swap2):
    int1 = list.index(swap1)
    int2 = list.index(swap2)
    # 交换值
    list[int1], list[int2] = list[int2], list[int1]
    return list
def eightyRounds(i, result1, next, _str, a3):
    for j in range(8):
        mid = result1[i * 8 + j + 1]
        # print(hex(mid))
        _swap = result1[(mid + next) & 0xff]
        # print(hex(_swap))
        result1 = swap(result1, mid, _swap)
        res = a3[i * 8 + j] ^ result1[(_swap + mid) & 0xff]
        # print(f'结果{i+1}_{j+1}:', hex(res))
 
        _str += hex(res)[2:] if len(hex(res)[2:]) == 2 else '0' + hex(res)[2:]
        next = (mid + next) & 0xff
    return result1, next, _str
 
result1 = [0x7c, 0x2e, 0x0e, 0x3d, 0x09, 0x32, 0xe3, 0x1c, 0x87, 0xc9, 0xb3, 0x2d, 0xb1, 0x2b, 0xdd, 0x20, 0x0b, 0xc3
    , 0x28, 0x5c, 0x0f, 0x7b, 0x12, 0x53, 0x0c, 0x2f, 0x92, 0x23, 0x30, 0xce, 0x50, 0xd0, 0x40, 0x1f, 0x45, 0x33
    , 0x3b, 0x95, 0xb2, 0x15, 0xbc, 0x34, 0x89, 0xe6, 0xd5, 0x39, 0x68, 0x78, 0xf1, 0x80, 0x7f, 0xc1, 0xca, 0x3e
    , 0x71, 0x52, 0x7a, 0x5e, 0xe7, 0xa4, 0x37, 0xa5, 0x69, 0xd1, 0x4c, 0x73, 0xb6, 0x5b, 0xfe, 0x16, 0xcf, 0x55
    , 0xcb, 0xc0, 0xf9, 0x24, 0x10, 0xa8, 0x6b, 0xe0, 0x62, 0x8c, 0x63, 0xd9, 0xbd, 0xda, 0x31, 0xf3, 0x96, 0xe4
    , 0x75, 0x5d, 0xf7, 0x79, 0x22, 0x57, 0xde, 0xbf, 0x91, 0x86, 0xea, 0x85, 0x97, 0x4d, 0x83, 0x00, 0xa9, 0x84
    , 0x14, 0x27, 0x1a, 0x17, 0xed, 0xe1, 0x1b, 0x6f, 0x41, 0x59, 0xa2, 0x99, 0xfc, 0x4b, 0x1e, 0xa7, 0x74, 0xab
    , 0x7d, 0x88, 0x5a, 0x51, 0xe2, 0xbe, 0xa6, 0x77, 0x67, 0x58, 0x11, 0x0d, 0xa0, 0x8f, 0x08, 0x2a, 0x72, 0xd2
    , 0xc2, 0x9b, 0x36, 0xd7, 0xf2, 0x70, 0x35, 0x8e, 0xd3, 0x03, 0xb7, 0xb9, 0xc8, 0x2c, 0x93, 0xb0, 0xee, 0x8d
    , 0x07, 0xf8, 0x47, 0x8b, 0xe9, 0x90, 0x04, 0x46, 0x94, 0xd4, 0x49, 0x4e, 0xb5, 0xd6, 0xa1, 0xac, 0x4f, 0xbb
    , 0xdc, 0xff, 0x18, 0xba, 0xaf, 0xc4, 0x26, 0x6e, 0x9e, 0x6d, 0x3a, 0x5f, 0xc7, 0x82, 0x44, 0xae, 0xcd, 0x8a
    , 0xad, 0x3c, 0x38, 0x01, 0xa3, 0xdf, 0xc5, 0x05, 0x02, 0xf4, 0xf0, 0x06, 0x13, 0x3f, 0xef, 0x29, 0x64, 0xfd
    , 0x66, 0x25, 0x60, 0xeb, 0xe8, 0x61, 0x7e, 0xfa, 0x54, 0x9a, 0xfb, 0xd8, 0xf5, 0xb4, 0x48, 0x76, 0x43, 0x65
    , 0x6a, 0xec, 0x9c, 0x1d, 0xf6, 0x0a, 0xe5, 0x9f, 0x42, 0x6c, 0xc6, 0xb8, 0xcc, 0x9d, 0xaa, 0xdb, 0x98, 0x21
    , 0x81, 0x56, 0x4a, 0x19]
next = 0
_str = ''
a3 = [0x00, 0x00, 0x00, 0x01, 0xec, 0xfa, 0xaf, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00
    , 0x24, 0x00, 0x00, 0x00, 0x10, 0x38, 0x33, 0x32, 0x30, 0x36, 0x38, 0x39, 0x33, 0x39, 0x39, 0x32, 0x33, 0x65, 0x33
    , 0x63, 0x2d, 0x36, 0x63, 0x36, 0x65, 0x2d, 0x33, 0x38, 0x39, 0x31, 0x2d, 0x39, 0x34, 0x35, 0x61, 0x2d, 0x66, 0x64
    , 0x30, 0x33, 0x63, 0x34, 0x37, 0x39, 0x36, 0x65, 0x62, 0x33, 0xa9, 0xec, 0xec, 0xc3, 0xc7, 0x90, 0xc4, 0xe7, 0x78
    , 0x41, 0x37, 0x63, 0xf3, 0xf5, 0x3d, 0xab]
for i in range(10):
    result1, next, _str = eightyRounds(i, result1, next, _str, a3)
 
# 第81个字节
byte1 = a3[80] ^ result1[(result1[0x51] + result1[(result1[0x51] + next) & 0xff]) & 0xff]
 
next = result1[0x51] + next
_str += hex(byte1)[2:] if len(hex(byte1)[2:]) == 2 else '0' + hex(byte1)[2:]
result1 = swap(result1, result1[0x51], result1[(result1[0x51] + next) & 0xff])
 
# 第82个字节
byte2 = a3[81] ^ result1[(result1[0x52] + result1[(result1[0x52] + next) & 0xff]) & 0xff]
 
next = result1[0x52] + next
_str += hex(byte2)[2:] if len(hex(byte2)[2:]) == 2 else '0' + hex(byte2)[2:]
result1 = swap(result1, result1[0x52], result1[(result1[0x52] + next) & 0xff])
 
# 第83个字节
byte2 = a3[82] ^ result1[(result1[0x53] + result1[(result1[0x53] + next) & 0xff]) & 0xff]
 
_str += hex(byte2)[2:] if len(hex(byte2)[2:]) == 2 else '0' + hex(byte2)[2:]
print(_str)
def swap(list, swap1, swap2):
    int1 = list.index(swap1)
    int2 = list.index(swap2)
    # 交换值
    list[int1], list[int2] = list[int2], list[int1]
    return list
def eightyRounds(i, result1, next, _str, a3):
    for j in range(8):
        mid = result1[i * 8 + j + 1]
        # print(hex(mid))
        _swap = result1[(mid + next) & 0xff]
        # print(hex(_swap))
        result1 = swap(result1, mid, _swap)
        res = a3[i * 8 + j] ^ result1[(_swap + mid) & 0xff]
        # print(f'结果{i+1}_{j+1}:', hex(res))
 
        _str += hex(res)[2:] if len(hex(res)[2:]) == 2 else '0' + hex(res)[2:]
        next = (mid + next) & 0xff
    return result1, next, _str
 
result1 = [0x7c, 0x2e, 0x0e, 0x3d, 0x09, 0x32, 0xe3, 0x1c, 0x87, 0xc9, 0xb3, 0x2d, 0xb1, 0x2b, 0xdd, 0x20, 0x0b, 0xc3
    , 0x28, 0x5c, 0x0f, 0x7b, 0x12, 0x53, 0x0c, 0x2f, 0x92, 0x23, 0x30, 0xce, 0x50, 0xd0, 0x40, 0x1f, 0x45, 0x33
    , 0x3b, 0x95, 0xb2, 0x15, 0xbc, 0x34, 0x89, 0xe6, 0xd5, 0x39, 0x68, 0x78, 0xf1, 0x80, 0x7f, 0xc1, 0xca, 0x3e
    , 0x71, 0x52, 0x7a, 0x5e, 0xe7, 0xa4, 0x37, 0xa5, 0x69, 0xd1, 0x4c, 0x73, 0xb6, 0x5b, 0xfe, 0x16, 0xcf, 0x55
    , 0xcb, 0xc0, 0xf9, 0x24, 0x10, 0xa8, 0x6b, 0xe0, 0x62, 0x8c, 0x63, 0xd9, 0xbd, 0xda, 0x31, 0xf3, 0x96, 0xe4
    , 0x75, 0x5d, 0xf7, 0x79, 0x22, 0x57, 0xde, 0xbf, 0x91, 0x86, 0xea, 0x85, 0x97, 0x4d, 0x83, 0x00, 0xa9, 0x84
    , 0x14, 0x27, 0x1a, 0x17, 0xed, 0xe1, 0x1b, 0x6f, 0x41, 0x59, 0xa2, 0x99, 0xfc, 0x4b, 0x1e, 0xa7, 0x74, 0xab
    , 0x7d, 0x88, 0x5a, 0x51, 0xe2, 0xbe, 0xa6, 0x77, 0x67, 0x58, 0x11, 0x0d, 0xa0, 0x8f, 0x08, 0x2a, 0x72, 0xd2
    , 0xc2, 0x9b, 0x36, 0xd7, 0xf2, 0x70, 0x35, 0x8e, 0xd3, 0x03, 0xb7, 0xb9, 0xc8, 0x2c, 0x93, 0xb0, 0xee, 0x8d
    , 0x07, 0xf8, 0x47, 0x8b, 0xe9, 0x90, 0x04, 0x46, 0x94, 0xd4, 0x49, 0x4e, 0xb5, 0xd6, 0xa1, 0xac, 0x4f, 0xbb
    , 0xdc, 0xff, 0x18, 0xba, 0xaf, 0xc4, 0x26, 0x6e, 0x9e, 0x6d, 0x3a, 0x5f, 0xc7, 0x82, 0x44, 0xae, 0xcd, 0x8a
    , 0xad, 0x3c, 0x38, 0x01, 0xa3, 0xdf, 0xc5, 0x05, 0x02, 0xf4, 0xf0, 0x06, 0x13, 0x3f, 0xef, 0x29, 0x64, 0xfd
    , 0x66, 0x25, 0x60, 0xeb, 0xe8, 0x61, 0x7e, 0xfa, 0x54, 0x9a, 0xfb, 0xd8, 0xf5, 0xb4, 0x48, 0x76, 0x43, 0x65
    , 0x6a, 0xec, 0x9c, 0x1d, 0xf6, 0x0a, 0xe5, 0x9f, 0x42, 0x6c, 0xc6, 0xb8, 0xcc, 0x9d, 0xaa, 0xdb, 0x98, 0x21
    , 0x81, 0x56, 0x4a, 0x19]
next = 0
_str = ''
a3 = [0x00, 0x00, 0x00, 0x01, 0xec, 0xfa, 0xaf, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00
    , 0x24, 0x00, 0x00, 0x00, 0x10, 0x38, 0x33, 0x32, 0x30, 0x36, 0x38, 0x39, 0x33, 0x39, 0x39, 0x32, 0x33, 0x65, 0x33
    , 0x63, 0x2d, 0x36, 0x63, 0x36, 0x65, 0x2d, 0x33, 0x38, 0x39, 0x31, 0x2d, 0x39, 0x34, 0x35, 0x61, 0x2d, 0x66, 0x64
    , 0x30, 0x33, 0x63, 0x34, 0x37, 0x39, 0x36, 0x65, 0x62, 0x33, 0xa9, 0xec, 0xec, 0xc3, 0xc7, 0x90, 0xc4, 0xe7, 0x78
    , 0x41, 0x37, 0x63, 0xf3, 0xf5, 0x3d, 0xab]
for i in range(10):
    result1, next, _str = eightyRounds(i, result1, next, _str, a3)
 
# 第81个字节
byte1 = a3[80] ^ result1[(result1[0x51] + result1[(result1[0x51] + next) & 0xff]) & 0xff]
 
next = result1[0x51] + next
_str += hex(byte1)[2:] if len(hex(byte1)[2:]) == 2 else '0' + hex(byte1)[2:]
result1 = swap(result1, result1[0x51], result1[(result1[0x51] + next) & 0xff])
 
# 第82个字节
byte2 = a3[81] ^ result1[(result1[0x52] + result1[(result1[0x52] + next) & 0xff]) & 0xff]
 
next = result1[0x52] + next
_str += hex(byte2)[2:] if len(hex(byte2)[2:]) == 2 else '0' + hex(byte2)[2:]
result1 = swap(result1, result1[0x52], result1[(result1[0x52] + next) & 0xff])
 
# 第83个字节
byte2 = a3[82] ^ result1[(result1[0x53] + result1[(result1[0x53] + next) & 0xff]) & 0xff]
 
_str += hex(byte2)[2:] if len(hex(byte2)[2:]) == 2 else '0' + hex(byte2)[2:]
print(_str)

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

最后于 2024-5-25 09:54 被杨如画编辑 ,原因: 更正
收藏
免费 13
支持
分享
最新回复 (15)
雪    币: 5531
活跃值: (31866)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-5-6 10:41
1
雪    币: 1297
活跃值: (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
tql
2024-5-6 11:01
0
雪    币: 122
活跃值: (561)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
达到这个水平要多久,需要学啥
2024-5-9 14:48
1
雪    币: 22
活跃值: (440)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
北冥鱼丶 达到这个水平要多久[em_4],需要学啥
需要学逆向
2024-5-9 15:14
0
雪    币: 122
活跃值: (561)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
chaego 需要学逆向
so你的逆向学习路径太陡峭了,我看这个文章中用到的技术:frida使用、IDA操作、010editer操作、winHex操作、so文件格式的魔改、unidbg的使用、md5 rc4等加密或编码算法的精通,然后将之灵活组合……艹  这学东西太多了
2024-5-9 17:06
0
雪    币: 3531
活跃值: (2939)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
北冥鱼丶 达到这个水平要多久[em_4],需要学啥

1

最后于 2024-12-9 19:25 被杨如画编辑 ,原因:
2024-5-9 19:57
0
雪    币: 1833
活跃值: (2528)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
膜拜,足够详细。
2024-5-9 23:49
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
大佬,tql吧。非科班的就只能看看java层的加密,一碰到so就抓瞎
2024-5-11 18:46
0
雪    币: 1503
活跃值: (3694)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2024-7-23 14:39
0
雪    币: 200
活跃值: (525)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
感谢分享
2024-7-31 18:47
0
雪    币: 31
活跃值: (491)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
请问,so dump工具能发一下吗或者加个星球
2024-8-13 23:17
0
雪    币: 3531
活跃值: (2939)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
13
阳光宝贝 请问,so dump工具能发一下吗或者加个星球
加我
2024-8-23 09:12
0
雪    币: 93
活跃值: (150)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
你好,有些问题想请教,方便加下联系方式吗?我的联系方式发你私信了
2024-8-30 09:37
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
怎么加入你的星球, 我等级太低了, 无法给你发私信
2024-9-30 00:44
0
雪    币: 3531
活跃值: (2939)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
16
mb_hfjpmxnn 怎么加入你的星球, 我等级太低了, 无法给你发私信
加我 yruhua0
2024-9-30 11:06
0
游客
登录 | 注册 方可回帖
返回