x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密

上一篇某招聘软件的sig及sp参数被和谐掉了,所以懂得都懂啊!
在这里插入图片描述
因为web的api没有那么全,所以来看了下app的,ios的防护几乎没有,纸糊的一样,android端的有点复杂了,到最后我也没能完整的实现整个加密过程,我也只复现到DFA还原出了秘钥,iv也找到了,就是结果不对,也许是魔改AES的程度比较高,后续搞出来了的话再发下文吧.
网上找了下,都是分析web的,几乎没有分析app的,所以这篇应该算是首篇比较详细的文章了.

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

流程

抓包

抓包的话可以发现头部有个x-zse-96参数,和web是一样的名字.web的之前我也搞过,没什么难度说实话.
在这里插入图片描述

关键代码定位

正常来说是先查壳看看有没有加固,加固的脱壳,没加固的直接拖到jadx里反编译,因为我觉得这应该算是个大厂吧,凭着经验大厂很少加壳,所以我也没有查壳,直接扔到jadx里反编译了.
尝试搜索一下字符串x-zse-96
在这里插入图片描述
什么也没有找到,其实是字符串被加密了
这个时候就有很多方法可以选择了,比如hook java层的系统hashmap,x-zse-96像是base64过的,也可以hook base64,又像是1.0_拼接后面一串得来的,也可以hook StringBuilder的tostring方法,甚至如果你觉得它是so层的加密也可以直接hook NewStringUTF函数.
这里我根据习惯先hook了hashmap

Java.perform(function (){
    var hashMap = Java.use("java.util.LinkedHashMap");  //LinkedHashMap HashMap
hashMap.put.implementation = function (a, b) {
    if(a!=null && a.equals("X-Zse-96")){ //X-Zse-96 x-zse-96
        console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
        console.log("hashMap.put: ", a, b);
    }
    return this.put(a, b);
}}

什么也没有hook到,看来不是通过hashmap来添加的
接着我hook了StringBuilder的tostring方法,因为做过web的就知道,这个参数就是前面的1.0_拼接后面的得到的.事实上hook base64也是可以hook到的

var sb = Java.use("java.lang.StringBuilder");
sb.toString.implementation = function () {
var retval = this.toString();
if (retval.indexOf("1.0_") != -1) {
    console.log("StringBuilder.toString: ", retval);
    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
}
return retval;
}

在这里插入图片描述
从堆栈来看这个参数的生成是最后通过拦截器添加上去的,tostring的下面一行点进去看看
在这里插入图片描述
按堆栈的意思就是说这个sb.tostring就是结果了,hook了下b函数看看传进去的是不是x-zse-96,结果并不是,是一个32位的md5的结果,堆栈后面有一句native method,这里我也是比较困惑为什么按照堆栈里找的和反编译出来的结果不一样.
这里我以为是反编译出了什么问题,扔到pkid里查个壳看看
在这里插入图片描述
好家伙,不按常理出牌,正常来说大厂都不加壳的,看了一眼so的名字,dexhelper确实是梆梆企业版的
在这里插入图片描述
这个时候我拿出了我的px4定制的fart脱壳机,结果直接运行不起来,由于fart脱壳机太热门了,很多厂商对这个都有检测,我这个还是去过特征版的!
但是我记得这个梆梆加固主要是指令抽取,正常来说反编译的代码都是空的函数,为什么和jadx中的不一样,并且里面的dex都是有数据的.
在这里插入图片描述
用mt管理器看一下
在这里插入图片描述
发现是个伪加固,app虚晃一枪,要是正常人估计还在想办法怎么脱壳,这样的话也不用脱壳了,正好挺省事的.
在这里插入图片描述

接着上面的逻辑,b2不为空,所以走了下面的逻辑,并且有一个addHeader的方法,这个像是往请求头里添加键值对.并且可以看到是添加了两组,hook一下

Java.perform(function(){
let Builder = Java.use("okhttp3.Request$Builder");
Builder["addHeader"].implementation = function (str, str2) {
    let result = this["addHeader"](str, str2);
    if(str=='X-Zse-96' || str=='X-Zse-93'){
        console.log(`Builder.addHeader is called: str=${str}, str2=${str2}`);
        console.log(`Builder.addHeader result=${result}`);
    }
    return result;
};
})

有结果,并且入参是x-zse-96的值
在这里插入图片描述
这里的H.d(“G51CEEF09BA7DF27F”)其实就是字符串加密了,执行后结果就是x-zse-96,点进去是一个native方法.在so层加密了,现在很多app都弄成这样了,通过搜索几乎定位不到关键代码.
接着看
H.d(“G38CD8525”)就是1.0_了,hook H.d方法同样可以得到结果.真正的加密结果来自new String(this.c.a(a2)),a2是查询参数md5后的结果转为了字节数组.
接着一路跟下来到了native方法
在这里插入图片描述
看名字应该是一个aes.中间的str是一个很长的字符串,这个字符串每次都是一样的,但加密结果不一样,所以不用管这个,主要是1和3参数,分别是两个字节数组,1是params md5后的字节数组,第3个数组初步可以当成是aes的秘钥,如果是ecb模式的话(初步认为,其实是iv,一般人都会认为是秘钥的,因为标题说了白盒AES,秘钥内嵌在程序里)
在这里插入图片描述

so模块没有出现在代码里,前面我hook了jni方法NewStringUTF发现是libbangcle_crypto_tool.so,还可以hook libart.so来找动态注册以及libdl.so找静态注册.
接下来看libbangcle_crypto_tool.so,拖到ida里反汇编
在这里插入图片描述
可以看到都是seg段,没有text段
在这里插入图片描述
并且导出函数中也是有java中的静态注册函数
在这里插入图片描述
点过去也是这样,这里你应该感觉到不对劲了,似乎ida无法识别这个so,其实是so被加固了.

dump so

so加固的碰到的比较少,这里我能想到的办法就是dump内存中的so,再修复一下.

function dump_so(so_name) {
    Java.perform(function () {
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
        var libso = Process.getModuleByName(so_name);
        console.log("[name]:", libso.name);
        console.log("[base]:", libso.base);
        console.log("[size]:", ptr(libso.size));
        console.log("[path]:", libso.path);
        var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
        var file_handle = new File(file_path, "wb");
        if (file_handle && file_handle != null) {
            Memory.protect(ptr(libso.base), libso.size, 'rwx');
            var libso_buffer = ptr(libso.base).readByteArray(libso.size);
            file_handle.write(libso_buffer);
            file_handle.flush();
            file_handle.close();
            console.log("[dump]:", file_path);

        }
    });
}
dump_so("libbangcle_crypto_tool.so")

dump下来后拖到电脑上修复一下就好了,工具https://github.com/F8LEFT/SoFixer
不过这样dump下来的效果不是很好,unidbg压根跑不起来这个so,里面还是有些数据无法访问,偏偏这些就是白盒aes中的关键数据s盒.折腾了大半天,一路打patch,最后还是放弃了unidbg这条路.
还是老老实实看so里的内容把,修复后的so至少可以看到代码段了.
在这里插入图片描述
进来后先转jnienv对象,这样代码会好分析些.
这个函数稍微有点控制流混淆以及虚假指令
在这里插入图片描述
不过从后往前推就可以看出来走的那个函数了,v12是返回值,v12来自v10创建的字节数组,v10来着上面的8E74函数,这个函数就是核心的加密流程了,点进去瞧瞧
v9是a1赋值过来的,所以也是env对象在这里插入图片描述
进来后看左下角的流程图,用了好几个switch case来判断走哪个流程,还好我们知道我们的函数是laes,并且从提示来看知道是cbc模式,也就是说需要一个初始化iv,这个laes的意思应该是local aes,字面意思就是本地的aes.从这个函数进去看看.进去后再进入一个laes就到了下面这个图
在这里插入图片描述
前面那些内容函数最好hook看一下入参,其实我都hook过了,只是没写,写了篇幅太长了,后面还有白盒AES,这里有个a3>15,a3是明文的长度,aes分组长度都是16个字节,所以这里应该是判断明文的长度来决定加密几轮.这个a6是个函数
在这里插入图片描述
直接点进去是这样,不知道是不是加固的原因,这里只好看汇编了,鼠标放到a6那,按tab转汇编视图
在这里插入图片描述

blx R12,意思是跳到R12寄存器中的地址执行,这里直接看看不出跳到哪,可以用frida 的inlinehook看看

function inlineHook() {
    var nativePointer = Module.findBaseAddress("libbangcle_crypto_tool.so");
    var hookAddr = nativePointer.add(0x6140); // 6140	
    Interceptor.attach(hookAddr, {
        onEnter: function (args) {
            console.log(nativePointer)
            console.log("onEnter: ", this.context.r12);
        }, onLeave: function (retval) {

        }
    });
}
inlineHook()

这个so是32位的,正常情况下地址是要加1的,但是因为这个so是从内存中dump下来的,所以不需要加1了,这个需要注意下.结果是0x6420.这个app的好几年都是用的这个so,一点都没变,偏移也没变,属于是so加固了以为高枕无忧了.
在这里插入图片描述
进来后看到一个名字WB white box,白盒的意思,现在的安卓逆向java层的加密已经几乎没有了,so的逆向上来就是各种白盒,各种魔改算法以及自写算法,不如直接去搞ios逆向,现在的ios逆向很多都是调用的系统函数,可以直接hook整个系统函数,类似安卓的那个算法助手,而且ios逆向的人少,对抗少,系统也闭源,风控也比安卓小得多,现在从0开始搞安卓逆向难度很大,就算你有天赋至少也需要半年的时间(ios的可以看看沐阳老哥的课程,当然只是题外话,后面也会出几篇文章从0开始搞ios逆向.)
之前我发过一篇白盒aes的文章,很多js逆向的反馈压根没听过这个词,感觉很高级的样子,正常,js逆向你可以直接扣js啊,ida中的这个伪c代码扣下来很难跑通,对c的要求有点高.我是扣了一下,跑不起来,放弃了.
看下面的内容前你最好了解下什么是白盒AES,这是我从网上找的一篇文章https://blog.csdn.net/qq_37638441/article/details/128968233
接着正题,前面java层传入了一个参数3,16个字节,如果是白盒aes的话,那这个大概率就是iv了.

function callAES(){
    var base_addr = Module.findBaseAddress("libbangcle_crypto_tool.so");
    var real_addr = base_addr.add(0x6420);
    var wbaes_encrypt_ecb_func = new NativeFunction(real_addr, "void", ["pointer", "pointer", "pointer", "pointer","pointer"]);
    inputPtr = Memory.alloc(0x10);
    var inputArray = hexToBytes("b11812121a8886852e2e868786252e2e");
    Memory.writeByteArray(inputPtr, inputArray)

    var inputPtr3 = Memory.alloc(0x10);
    // var inputArray = hexToBytes("803d17b5b00000000400000080000000");
    var inputArray = hexToBytes("401948d4b00000000400000080000000");
    Memory.writeByteArray(inputPtr3, inputArray)

    var inputPtr4 = Memory.alloc(0x10);
    var inputArray = hexToBytes("00000000000000000000000000000000");
    Memory.writeByteArray(inputPtr4, inputArray)

    var inputPtr5 = Memory.alloc(0x10);
    var inputArray = hexToBytes("00000000000000000000000000000000");
    Memory.writeByteArray(inputPtr5, inputArray)

    wbaes_encrypt_ecb_func(inputPtr, inputPtr, inputPtr3, inputPtr4,inputPtr5);
    console.log(hexdump(inputPtr,{length: 0x10}));
}

so主动调用这个函数,其实它有5个参数,ida识别成了3个.前两个是同一个参数,iv和明文异或的结果,cbc模式下比ecb模式多了个iv,后两个参数作用不大,不用管,关键是第3个参数,有点奇怪,现在我也没搞清楚它的作用是什么,后面再说吧.

DFA攻击

调用后有结果,结果也是正确的.接下来寻找dfa攻击点,第8次列混淆到第9次列混淆之间,并且需要寻找到state块
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
按aes的算法应该是这样,最后面的第十轮少了列混淆,所以不在这个循环里面.
加下来寻找哪个是state块,初看选了result,每次核心运算都有他,一hook发现经过9轮他的值都不变,直到最后赋值结果的时候才变了.
后来仔细看了下v20也有可能,但是这个v20不太好hook他的变化,因为这里没有什么函数调用,所有没有入参什么的,要hook到这个v20的地址有些困难
在这里插入图片描述

这里我寻找九轮循环最开始的地方hook上,这个赋值写的也有点复杂.鼠标放到v20的地方,转汇编更方便看.
在这里插入图片描述
在这里插入图片描述
这里鼠标放到var_34按h可以转成立即数.STRB R3, [R11,#-52]
这条指令的意思就是将寄存器 R3 中的值存储到距离 R11 寄存器所指向的内存位置偏移为 -52 的地方。所以v20的地址就是R11的地址减去52的偏移的位置处.

function hookwb(){
    var count = 0;
    var base_addr = Module.findBaseAddress("libbangcle_crypto_tool.so");
    var real_addr = base_addr.add(0x6580)
    Interceptor.attach(real_addr, {
        onEnter: function (args) {
            count += 1;
            console.log("onEnter: ",count, hexdump(this.context.r11.sub(0x24),{length:0x10}));
            console.log("start:"+count);
        }
    });
}

结果也确实是9轮,接下来进行dfa攻击.

function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}
var inputPtr;
function callAES(){
    var base_addr = Module.findBaseAddress("libbangcle_crypto_tool.so");
    var real_addr = base_addr.add(0x6420);
    var wbaes_encrypt_ecb_func = new NativeFunction(real_addr, "void", ["pointer", "pointer", "pointer", "pointer","pointer"]);
    inputPtr = Memory.alloc(0x10);
    var inputArray = hexToBytes("b11812121a8886852e2e868786252e2e");
    Memory.writeByteArray(inputPtr, inputArray)

    var inputPtr3 = Memory.alloc(0x10);
    // var inputArray = hexToBytes("803d17b5b00000000400000080000000");
    var inputArray = hexToBytes("401948d4b00000000400000080000000");
    Memory.writeByteArray(inputPtr3, inputArray)

    var inputPtr4 = Memory.alloc(0x10);
    var inputArray = hexToBytes("00000000000000000000000000000000");
    Memory.writeByteArray(inputPtr4, inputArray)

    var inputPtr5 = Memory.alloc(0x10);
    var inputArray = hexToBytes("00000000000000000000000000000000");
    Memory.writeByteArray(inputPtr5, inputArray)

    wbaes_encrypt_ecb_func(inputPtr, inputPtr, inputPtr3, inputPtr4,inputPtr5);
    // var output = Memory.readByteArray(inputPtr, 0x10);
    // console.log(bufferToHex(output))
    console.log(hexdump(inputPtr,{length: 0x10}));
}
function bufferToHex (buffer) {
    return [...new Uint8Array (buffer)]
        .map (b => b.toString (16).padStart (2, "0"))
        .join ("");
}
function hookwb(){
    var count = 0;
    var base_addr = Module.findBaseAddress("libbangcle_crypto_tool.so");
    var real_addr = base_addr.add(0x6580)
    Interceptor.attach(real_addr, {
        onEnter: function (args) {
            count += 1;
            console.log("onEnter: ",count, hexdump(this.context.r11.sub(0x24),{length:0x10}));
            if(count===9){
                this.context.r11.sub(0x24).add(randomNum(0,15)).writeS8(randomNum(0, 0xff));
                console.log("onEnter: ", hexdump(this.context.r11.sub(0x24),{length:0x10}));
            }
            console.log("start:"+count);
        }
    });
}
function randomNum(minNum,maxNum){
    if (arguments.length === 1) {
        return parseInt(Math.random() * minNum + 1, 10);
    } else if (arguments.length === 2) {
        return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
    } else {
        return 0;
    }
}
function dfa(){
    for(var i=0;i<300;i++){
        hookwb()
        callAES()
    }
}

连续调用300次后取得故障密文再用phoenixAES拿到第10轮秘钥E48E8AA0E4449B580CF7ECC13CC17CD3,接着用aes_keyschedule还原出主密钥6BA6737912D31F3A1B53066645FABEA3
在这里插入图片描述
秘钥6BA6737912D31F3A1B53066645FABEA3,iv99303a3a32343a3992923a3b3a999292(java层的)都有了.事实上只有1轮的话,也可以直接用ecb模式,因为入参1就是明文和iv异或的结果,拿去加密一下发现结果不对…
前面我就说了这个参数3不太清楚是什么,因为这个入参这个时刻下主动调用是这个结果,重开app主动调用逻辑不变结果取变了.这里有个很大的问号?
这里先分析到这里吧,先埋个坑,后面有空再看看吧.

最后

微信公众号
在这里插入图片描述
知识星球
在这里插入图片描述
如果你觉得这篇文章对你有帮助,不妨请作者喝杯咖啡吧!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/474450.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySql实战--日志系统:一条SQL更新语句是如何执行的?】

前面我们系统了解了一个查询语句的执行流程&#xff0c;并介绍了执行过程中涉及的处理模块。相信你还记得&#xff0c;一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块&#xff0c;最后到达存储引擎。 那么&#xff0c;一条更新语句的执行流程又是怎…

亮数据代理IP轻松解决爬虫数据采集痛点

文章目录 一、爬虫数据采集痛点二、为什么使用代理IP可以解决&#xff1f;2.1 爬虫和代理IP的关系2.2 使用代理IP的好处 三、亮数据代理IP的优势3.1 IP种类丰富3.1.1 动态住宅代理IP3.1.2 静态住宅代理IP3.1.3 机房代理IP3.1.4 移动代理IP 3.2 高质量IP全球覆盖3.3 超级代理服务…

UE4 Json事件设置Asset值

通过Json事件来设置&#xff0c;比如骨骼网格体&#xff08;换皮&#xff09;等等

【漏洞复现】1. WebLogic 反序列化漏洞(CVE-2019-2890)复现与分析

文章目录 1. 基础知识2. 复现2.1 漏洞介绍漏洞影响版本&#xff1a; 2.2 漏洞原理分析2.3 漏洞复现2.3.1 环境搭建2.3.2 漏洞验证2.3.3 漏洞利用2.3.4 POC分析 2.4 漏洞修复 1. 基础知识 WebLogic是美国Oracle公司出品的一个application server&#xff0c;确切的说是一个基于J…

区域规划(Regional Planning)的学习笔记

目录 一、概念题 1.区域的概念、类型、特性 2.区域分析的概念、主要内容 3.自然环境、自然资源的概念 4.区域自然资源评价的内容 5.可持续发展理论定义 6.经济增长、经济结构定义 7.产业结构概念 8.人口增长分析的含义、指标 9.技术进步概念、类型 10.技术进步对区域…

『Python爬虫』极简入门

本文简介 点赞 收藏 关注 学会了 声明&#xff1a;请勿使用爬虫技术获取公民隐私数据、数据以及企业或个人不允许你获取的数据。 本文介绍如何使用 Python 写一只简单的爬虫&#xff0c;作为入门篇&#xff0c;这个程序不会很复杂&#xff0c;但至少可以讲明爬虫是个什么东…

Nacos 配置管理-快速入门

** Nacos 配置管理-快速入门 ** 2024-3-18 段子手168 1、首先在 nacos 发布配置&#xff08;确认安装启动了 nacos 服务&#xff09;。 浏览器访问 http://127.0.0.1:8848/nacos, 打开 nacos 控制台&#xff0c; 并点击菜单&#xff1a;新增 --》配置管理 --》配置列表 --》…

html5cssjs代码 034 自定义字体

html5&css&js代码 034 自定义字体 一、代码二、解释 这是一个带有自定义字体的网页&#xff0c;设置了页面背景颜色、文字颜色以及全局样式。它定义了三种自定义字体并通过font-face规则引入外部字体文件&#xff0c;并通过CSS类&#xff08;.f1, .f2, .f3&#xff09;…

2024上半年四川省属事业单位报名指南✅

⏰报考时间 1⃣报名时间&#xff1a;2024年3月21日10:00——3月25日18:00 2⃣缴费时间&#xff1a;截止2024年3月26日18:00 ⚠通过网络报名且上传相片质量合格的报考者&#xff0c;未按要求在规定时间内进行网上缴费的&#xff0c;视为自动放弃报考。 3⃣公共科目笔试时间&…

力扣---两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

QGIS开发笔记(一):QGIS介绍、软件下载和加载shp地图数据Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/136888334 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

【亲测有效】axureRP9授权密钥 Mac/Windows

哪里有axureRP9授权密钥&#xff1f;这里为大家带来了axureRP9中文授权版资源&#xff0c;内含axureRP9授权密钥&#xff0c;亲测支持apple Intel/M1/M2以及Windows系统&#xff01; 【永久可用】axureRP9授权密钥 Mac版 【永久可用】axureRP9授权密钥 Windows版

吴恩达机器学习-可选实验室:简单神经网络(Simple Neural Network)

在这个实验室里&#xff0c;我们将使用Numpy构建一个小型神经网络。它将与您在Tensorflow中实现的“咖啡烘焙”网络相同。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyle) import tensorflow as tf from lab_utils_common impor…

python网络爬虫实战教学——urllib的使用(3)

文章目录 专栏导读1、urlsplit2、urlunsplit3、urljoin4、urlencode 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对大学生、…

基于java+springboot+vue实现的学生管理系统(文末源码+Lw+ppt)23-486

摘 要 学生管理系统的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品&#xff0c;体验高科技时代带给人们的方便&#xff0c;同时也能让用户体会到与以往常规产品不同的体验风格。 与安卓&#xff0c;iOS相比较起来&#x…

高校校园点餐系统|基于JSP技术+ Mysql+Java+ B/S结构的高校校园点餐系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

百度智能云加速「低代码+大模型」融合,爱速搭位居 2023 年 IDC 低代码/无代码领导者象限

近期&#xff0c;IDC 发布了《IDC MarketScape: 中国低代码/无代码开发平台 2023 年厂商评估》研究报告。 IDC 预测&#xff0c;到 2027 年&#xff0c;低代码的市场规模将达到 106.3 亿人民币&#xff0c;未来 5 年市场年复合增长率为 32.3%。 凭借对企业级开发场景的深度支…

MD5源码(C语言描述)

本文介绍MD5源码&#xff08;C语言描述&#xff09;。 MD5(Message-Digest Algorithm 5)&#xff0c;即消息摘要算法5&#xff0c;是一种被广泛使用的消息散列算法。散列算法的基础原理是&#xff1a;将数据&#xff08;如一段文字&#xff09;经过运算转换为一段固定长度&…

自动推送个人站点到百度收录

自动推送个人站点到百度收录 准备 验证站点 访问百度收录官网注册帐号选择用户中心-站点管理 在“站点管理”里面点击“添加站点”&#xff0c;填写你的站点地址&#xff08;支持子域名&#xff09; 根据你的站点的内容、类型勾选站点属性 点击“验证站点”。 两种方式都可以…

ByteMD - 掘金社区 MarkDown 编辑器的免费开源的版本,可以在 Vue / React / Svelte 中使用

各位元宵节快乐&#xff0c;今天推荐一款字节跳动旗下掘金社区官方出品的 Markdown 编辑器 JS 开发库。 ByteMD 是一个用于 web 开发的 Markdown 编辑器 JavaScript 库&#xff0c;是字节跳动&#xff08;也就是掘金社区&#xff09;出品的 Markdown 格式的富文本编辑器&#…