APP逆向 day21大姨妈逆向

一.前言

今天来和大家说一款app名叫DYM,我们选择版本v8.6.0,今天通过这个可以学到的知识点有绕过root检测,通过frida-rpc和自己编写一款小的app来调用so文件,然后再来破解登录接口

二.绕过root检测

我们进入app后发现,弹出一个检测到了root了

那绕过之前,我们得先了解一下root检测原理

root检测原来是
    -使用java代码或者so代码---》检测root的特征
    -一个手机一旦root了,在某些目录下,就会有对应的一些文件(su)
    -app做root检测,就是在所有可能的目录下,检索有没有这些特征,如果有,app就不允许运行了
    
   
手机root了,会在那些目录下有特征呢?--》特征一般都是  [su] 
    "/system/bin/su", # 不同root软件,生成的位置不同,面具一般在这个目录下
     "/data/local/bin/su",
    "/data/local/su",
    "/data/local/xbin/su",
    "/sbin/su",
    "/system/app/Superuser.apk",
    "/system/bin/failsafe/su",
    "/su/bin/su",
    "/system/sd/xbin/su",
    "/system/xbin/busybox",
    "/system/xbin/daemonsu",
    "/system/xbin/su",
    "/system/sbin/su",
    
我们手机已经root了,会不会有这些特征
    -/system/bin/su 路径下有su 可执行文件
    -查看手机是否被root
        adb shell  # 进入手机
        su         # 只要有执行,其实手机就是被root了
        cd /system/bin/ # 进入到/system/bin/ 目录下
        ls |grep su  #过滤当前目录下有没有su 可执行文件,发现有,就是被root了

我们手机已经root了,我们要不让app检测到我们root了
    -1 反编译apk,找到检测root的代码--》hook--》让它不执行
    -2 直接修改特征文件-->把这些关键位置的 su  --》改个名字
        -app搜索不到相关特征,他就认为我们没有root
                
改名字,一劳永逸
    - 把su --》su1--》以后获取root权限,就不是执行su ,而是执行 su1

当然改名字是最捞的方法可对,我们就要整其他的绕过检测方法

2.1 通用hook脚本绕过

github上有人开源了绕过的通用脚本,这里给出地址

GitHub - AshenOneYe/FridaAntiRootDetection: A frida script for bypass common root detection,the collection of detection methods is still improving!A frida script for bypass common root detection,the collection of detection methods is still improving! - AshenOneYe/FridaAntiRootDetectionicon-default.png?t=N7T8https://github.com/AshenOneYe/FridaAntiRootDetection 

这里我也贴出代码,方便大家使用

const commonPaths = ["/data/local/bin/su","/data/local/su","/data/local/xbin/su","/dev/com.koushikdutta.superuser.daemon/","/sbin/su","/system/app/Superuser.apk","/system/bin/failsafe/su","/system/bin/su","/su/bin/su","/system/etc/init.d/99SuperSUDaemon","/system/sd/xbin/su","/system/xbin/busybox","/system/xbin/daemonsu","/system/xbin/su","/system/sbin/su","/vendor/bin/su","/cache/su","/data/su","/dev/su","/system/bin/.ext/su","/system/usr/we-need-root/su","/system/app/Kinguser.apk","/data/adb/magisk","/sbin/.magisk","/cache/.disable_magisk","/dev/.magisk.unblock","/cache/magisk.log","/data/adb/magisk.img","/data/adb/magisk.db","/data/adb/magisk_simple","/init.magisk.rc","/system/xbin/ku.sud","/data/adb/ksu","/data/adb/ksud"
];const ROOTmanagementApp = ["com.noshufou.android.su","com.noshufou.android.su.elite","eu.chainfire.supersu","com.koushikdutta.superuser","com.thirdparty.superuser","com.yellowes.su","com.koushikdutta.rommanager","com.koushikdutta.rommanager.license","com.dimonvideo.luckypatcher","com.chelpus.lackypatch","com.ramdroid.appquarantine","com.ramdroid.appquarantinepro","com.topjohnwu.magisk","me.weishu.kernelsu"
];function stackTraceHere(isLog){var Exception = Java.use('java.lang.Exception');var Log = Java.use('android.util.Log');var stackinfo = Log.getStackTraceString(Exception.$new())if(isLog){console.log(stackinfo)}else{return stackinfo}
}function stackTraceNativeHere(isLog){var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t");console.log(backtrace)
}function bypassJavaFileCheck(){var UnixFileSystem = Java.use("java.io.UnixFileSystem")UnixFileSystem.checkAccess.implementation = function(file,access){var stack = stackTraceHere(false)const filename = file.getAbsolutePath();if (filename.indexOf("magisk") >= 0) {console.log("Anti Root Detect - check file: " + filename)return false;}if (commonPaths.indexOf(filename) >= 0) {console.log("Anti Root Detect - check file: " + filename)return false;}return this.checkAccess(file,access)}
}function bypassNativeFileCheck(){var fopen = Module.findExportByName("libc.so","fopen")Interceptor.attach(fopen,{onEnter:function(args){this.inputPath = args[0].readUtf8String()},onLeave:function(retval){if(retval.toInt32() != 0){if (commonPaths.indexOf(this.inputPath) >= 0) {console.log("Anti Root Detect - fopen : " + this.inputPath)retval.replace(ptr(0x0))}}}})var access = Module.findExportByName("libc.so","access")Interceptor.attach(access,{onEnter:function(args){this.inputPath = args[0].readUtf8String()},onLeave:function(retval){if(retval.toInt32()==0){if(commonPaths.indexOf(this.inputPath) >= 0){console.log("Anti Root Detect - access : " + this.inputPath)retval.replace(ptr(-1))}}}})
}function setProp(){var Build = Java.use("android.os.Build")var TAGS = Build.class.getDeclaredField("TAGS")TAGS.setAccessible(true)TAGS.set(null,"release-keys")var FINGERPRINT = Build.class.getDeclaredField("FINGERPRINT")FINGERPRINT.setAccessible(true)FINGERPRINT.set(null,"google/crosshatch/crosshatch:10/QQ3A.200805.001/6578210:user/release-keys")// Build.deriveFingerprint.inplementation = function(){//     var ret = this.deriveFingerprint() //该函数无法通过反射调用//     console.log(ret)//     return ret// }var system_property_get = Module.findExportByName("libc.so", "__system_property_get")Interceptor.attach(system_property_get,{onEnter(args){this.key = args[0].readCString()this.ret = args[1]},onLeave(ret){if(this.key == "ro.build.fingerprint"){var tmp = "google/crosshatch/crosshatch:10/QQ3A.200805.001/6578210:user/release-keys"var p = Memory.allocUtf8String(tmp)Memory.copy(this.ret,p,tmp.length+1)}}})}//android.app.PackageManager
function bypassRootAppCheck(){var ApplicationPackageManager = Java.use("android.app.ApplicationPackageManager")ApplicationPackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(str,i){// console.log(str)if (ROOTmanagementApp.indexOf(str) >= 0) {console.log("Anti Root Detect - check package : " + str)str = "ashen.one.ye.not.found"}return this.getPackageInfo(str,i)}//shell pm check
}function bypassShellCheck(){var String = Java.use('java.lang.String')var ProcessImpl = Java.use("java.lang.ProcessImpl")ProcessImpl.start.implementation = function(cmdarray,env,dir,redirects,redirectErrorStream){if(cmdarray[0] == "mount"){console.log("Anti Root Detect - Shell : " + cmdarray.toString())arguments[0] = Java.array('java.lang.String',[String.$new("")])return ProcessImpl.start.apply(this,arguments)}if(cmdarray[0] == "getprop"){console.log("Anti Root Detect - Shell : " + cmdarray.toString())const prop = ["ro.secure","ro.debuggable"];if(prop.indexOf(cmdarray[1]) >= 0){arguments[0] = Java.array('java.lang.String',[String.$new("")])return ProcessImpl.start.apply(this,arguments)}}if(cmdarray[0].indexOf("which") >= 0){const prop = ["su"];if(prop.indexOf(cmdarray[1]) >= 0){console.log("Anti Root Detect - Shell : " + cmdarray.toString())arguments[0] = Java.array('java.lang.String',[String.$new("")])return ProcessImpl.start.apply(this,arguments)}}return ProcessImpl.start.apply(this,arguments)}
}console.log("Attach")
bypassNativeFileCheck()
bypassJavaFileCheck()
setProp()
bypassRootAppCheck()
bypassShellCheck()

终端执行 frida -U -f com.yoloho.dayima -l 1-通用绕过root检测脚本.js 就ok了

执行好了就能顺利进入啦

2.2 Shamiko模块绕过

有的一些app会不光会检测是否root,还会检测是否安装了magisk,这个时候我们就需要借助shamiko模块进行隐藏magisk,这里给出网站

Release Shamiko v1.1 · LSPosed/LSPosed.github.io · GitHubicon-default.png?t=N7T8https://github.com/LSPosed/LSPosed.github.io/releases/tag/shamiko-344

我们安装好之后直接把下载好的zip推送到手机里(不要解压)

adb push 下载好的 /sdcard/Download/Shamiko.zip

放到这个目录比较好找,推送好之后,将zip刷入后重启

 进入之后选择你要绕过的app就好,那如何绕过magisk检测呢

我们点击上面的隐藏Magisk应用,改成你想要取的名字,这个时候桌面上就会生成那个app,此时那个才是真正的magisk,原,magisk就会失效

2.3 hook检测点

这个我不太想讲,但是因为学习,所以和大家说一下

我们反编译搜索关键字

找到后我们进入

 

发现这里就是判断是不是模拟器和是否root,我们hook这两个方法让他们返回false就好了

这里给出hook代码

import frida
import sysrdev = frida.get_remote_device()
pid = rdev.spawn(["com.yoloho.dayima"])
session = rdev.attach(pid)scr = """
Java.perform(function () {var MiscUtil = Java.use("com.yoloho.libcore.util.MiscUtil");MiscUtil.isRooted.implementation = function () {console.log('绕过')return false;}MiscUtil.isSimulator.implementation = function (ctx) {console.log('绕过')return false;}
});
"""script = session.create_script(scr)def on_message(message, data):print(message, data)script.on("message", on_message)
script.load()
rdev.resume(pid)
sys.stdin.read()

2.4 aosp刷机绕过 

这里我们讲不来,得等到后面才能和大家讲 (其实我也不会)

什么是aosp?

Android是开源的,AOSP(Android Open Source Project)为Android开源项目的缩写,自己编译Android系统

例如鸿蒙就是使用了aosp源码,再改底层

而小米之前的miui则是没有修改源码 修改的是ui界面

后面会和大家说aosp的知识

三.抓包分析 

 

抓包发现,url是 https://uicapi.yoloho.com/user/login

需要破解的参数有 device,password,sign

四.破解device

我们搜索device,发现太多了,于是我们就搜索请求头里的其他参数,然后看看附近有没有device

那我们搜索releasever看看

搜索发现五个,但是我们选择第四个,因为第四个是interceptor,拦截器里面的,而且每个请求都有device,那我们点进去,所以大概率就是拦截器生成的

 

发现就是在这里,发现外面是对他进行url编码,所以生成的是在里面那个函数,我们再次点进去

 

发现核心是setDeviceCode,我们点进去

发现主要加密逻辑就是在这里,大概意思就是str6是几个字符串拼接来的,然后再通过sha1加密,最后转成字符串,那我们是不是就可以通过hook messageDigest.update查看里面参数,然后打印参数的字符串形式,就知道str6是多少了,但是问题就是这个方法在很多地方都有打印,所以我们需要同时hook里面的大方法setDeviceCode,打印一下标记,在这个下面的不就是我们要的嘛,这里给出hook代码


Java.perform(function () {var PeriodAPIV2 = Java.use("com.yoloho.controller.api.PeriodAPIV2");var flag = false;PeriodAPIV2.setDeviceCode.implementation = function () {console.log("-------------------------setDeviceCode-------------------------")flag = true;return this.setDeviceCode();}var MessageDigest = Java.use("java.security.MessageDigest");var ByteString = Java.use("com.android.okhttp.okio.ByteString");// update是一个重载方法,所以要用overloadMessageDigest.update.overload("[B").implementation = function (data) {if (flag) {console.log(ByteString.of(data).utf8(), '\n' );}return this.update(data);}});
// frida -U -f  com.yoloho.dayima -l hook_str6.js

 

发现这个就是了,前面那个三个看就是包名,所以肯定是第三个了,而且发现,还没有登录就生成了,所以说肯定是在刚开始就生成,接下来每次请求都一样

null 设备id号

2d1fc41fe54b945 安卓id 可以随机生成

cbluelinegooglearm64-v8abluelineRQ3A.211001.001abfarm-01362RQ3A.211001.001GooglePixel 3bluelinerelease-keysuserandroid-build 设备信息

3A:38:93:AD:E6:12 wifi的mac地址 可以伪造

02:00:00:00:00:00 蓝牙的mac地址 可以伪造

然后对这个拼接今昔sha1加密,这种一看就是标准库,所以肯定是标准的,大家也可以去验证一下

五.破解password 

我们搜索 "password"

 

我们搜索到了四个,其中第二个是最像的,因为他的包名翻译就是登录,我们点进去

 小惊喜,我们不光看到了password还看到了sign,一看就知道sign是把这些加在一起进行加密的,那我们就先看password

可以看到password传入了两个参数,一个是账号,一个是密码明文,我们点击进去

可以看到这个加密逻辑不就是aes嘛

其中: 待加密字符串是密码,key是账号进行md5加密取前16位字符串,iv则是固定字符串"yoloho_dayima!%_"

再点进去,发现返回的是base64的,不正是我们抓包所抓到的嘛

大家可以去hook一下判断和抓包的是否一样,我这里就不带大家hook了,我这里去看看md5里有没有加盐

 

发现只update了一次,没有加盐,至此password破解完成

六.破解sign 

sign的位置刚才已经找到,这里我们退到之前的位置

 

sb拼接了:getDeviceCode 上节课破解的device
sb.append(PeriodAPIV2.getInstance().getDeviceCode());
sb拼接了 user/login
sb.append("user/login");
sb拼接了 手机号
sb.append(str);
sb拼接了 加密后的密码
sb.append(privateStrHandle);
把sb这个字符串--》调用encrypt_data 进行加密生成了 sign
arrayList.add(new BasicNameValuePair("sign", Crypt.encrypt_data(0L, sb.toString(), sb.length()))); 

第一个参数是0,第二个参数是sb,第三个参数是sb的长度 

我们点进去,看看

 

发现是个jni方法,在libCrypt.so里面加密(记住这个,后面都要用得到),这里进去非常难读,今天我们介绍个新的知识点,frida-rpc,就通过这个sign来教大家使用,因为这个是今天的重点,所以我单独开一页教大家

七.frida-rpc

什么是frida-rpc?

frida 提供了一种跨平台的 rpc (远程过程调用)机制,通过 frida rpc 可以在主机和目标设备之间进行通信,并在目标设备上执行代码
Frida-RPC 是一个基于 Frida 框架的扩展,用于在不同进程之间进行远程过程调用(RPC)。Frida 是一个功能强大的动态插桩工具,它允许开发人员在运行时监控、修改和控制应用程序的行为。通过使用 Frida-RPC,您可以在不同的进程之间建立通信通道,使得您能够从一个进程中调用另一个进程的函数或方法,就好像它们都在同一个进程中一样

这有一张图方便大家理解

 

使用流程

流程
1 手机端启动 frida-server  # 启动了
2 运行app                 # 手机端要运行app--》手机的内存中,存在 encrpyt_data函数
3 电脑端端口转发,运行脚本   # 端口转发
4 电脑端写脚本,远程调用 

import fridardev = frida.get_remote_device()
session = rdev.attach("大姨妈")scr = """
rpc.exports = {   //这里可以放很多键值,但是注意,名字任意 但不要带符号那些encryptdata:function(j2,str,j3){  //写传入的参数var res;Java.perform(function () {  //固定写法// 包.类var Crypt = Java.use("com.yoloho.libcore.util.Crypt"); //导入包和类// 类中的方法res = Crypt.encrypt_data(j2,str,j3); //执行类中的方法});return res;}
}
"""script = session.create_script(scr)
script.load()# python 调用
# frida低版本使用
# sign = script.exports_sync.aa(0, "bcae572b84d20c385d6d9d2d7d9e645da29da3c0user/login18953675221WA89qByLlDeaGjmVNzXm/w==", 85)# frida高版本使用
sign = script.exports.encryptdata(0, "bcae572b84d20c385d6d9d2d7d9e645da29da3c0user/login18953675221WA89qByLlDeaGjmVNzXm/w==", 85)
print(sign)

运行结果

 

八.编写app调用so

这里我截图教大家一步步做,首先我们进入AndroidStudio

新建一个这个项目,然后就下一步选择项目就行了,语言选择java

 看到这个页面就算好了,然后我们刚刚之前看到jni 是libCrypto.so,那我们就去找到这个so文件

把这两个包中的so文件删除到只剩 libCrypto.so,然后把这两个文件夹复制到项目的lib下面

然后再在 build.gradle里的android里面加上

sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }

 然后创建包 这个包名和你要调用的包名一致

 

然后创建一个类也要叫要调用地方的类名,然后再把这个代码c进去

再给主页面的的文字加上一个id 就叫sign 

 

 再在这个位置加上这些代码

TextView tv=findViewById(R.id.sign);String str="64e6176e45397c5989504e76f98ecf2e63b2679euser/login15131255555A4rE0CKaCsUMlKpHjtL0AQ==";String sign = Crypt.encrypt_data(0, str, str.length());Log.e("sign=", sign);tv.setText(sign);

大功告成,然后运行

 

大功告成

九.总结 

今天知识点有点多,主要是给大家说了一下绕过root检测,frida-rpc和安卓编写调用so,这个其实后期用的不多,主要是做个了解,后期遇到难读的so我们会用unidbg,还差两个知识点就能讲到了,讲完那个我们的基础课程也算是结束了,大家就到了app逆向入门了,好了,到时候再说吧,晚安!

补充

有需要源码的看我主页签名名字私信我,有求必应

 

 

 

 

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C++从入门到起飞之——初始化列表类型转换static成员 全方位剖析!

🌈个人主页:秋风起,再归来~🔥系列专栏:C从入门到起飞 🔖克心守己,律己则安 目录 1、初始化列表 2、 类型转换 3. static成员 4、完结散花 1、初始化列表 • 之前我们实现构造函数…

Qwen2模型Text2SQL微调​以及GGUF量化

Qwen2-1.5B微调 准备python环境 conda create --name llama_factory python=3.11 conda activate llama_factory部署llama-factory git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip3 install -e ".[torch,metrics]" # 如…

算法日记day 20(二叉搜索树)

一、验证二叉搜索树 题目: 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下: 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也…

IE11添加收藏、关闭窗口时弹出的对话框字体又大又粗很难看的解决办法

原因已查明,在win7 sp1 32位系统下,安装“2020-01 适用于基于 x86 的系统的 Windows 7 月度安全质量汇总(KB4534310)”这个更新会导致IE11的窗口字体变大变粗,把这个更新卸载了就可以了,无需重装IE11浏览器…

【四】jdk8基于m2芯片arm架构Ubuntu24虚拟机下载与安装

文章目录 1. 安装版本2. 开始安装3. 集群安装 1. 安装版本 如无特别说明,本文均在root权限下安装。进入oracle官网:https://www.oracle.com/java/technologies/downloads/找到最下面Java SE 看到java 8,下载使用 ARM64 Compressed Archive版…

探索 Electron:快捷键与剪切板操作

Electron是一个开源的桌面应用程序开发框架,它允许开发者使用Web技术(如 HTML、CSS 和 JavaScript)构建跨平台的桌面应用程序,它的出现极大地简化了桌面应用程序的开发流程,让更多的开发者能够利用已有的 Web 开发技能…

C++:类和对象2

1.类的默认成员函数 默认成员函数就是用户没有显示实现编译器会自动生成的成员函数称为默认成员函数。一个类,我们在不写的情况下编译器会默认生成6个默认成员函数,分别是构造函数,析构函数,拷贝构造函数,拷贝赋值运算…

GPT-4引领:AI新浪潮的转折点

OneFlow编译 **翻译|贾川、杨婷、徐佳渝 编辑|王金许** 一朝成名天下知。ChatGPT/GPT-4相关的新闻接二连三刷屏朋友圈,如今,这些模型背后的公司OpenAI的知名度不亚于任何科技巨头。 不过,就在ChatGPT问世前&#x…

Reaxys平台账号创建:简易注册流程

Reaxys数据库是Elsevier旗下的全球最大物质理化性质和事实反应数据库,包含了超过5亿条经过实验验证的物质信息,收录超过1.38亿种化合物,5,000万种单步和多步反应、6,000万条文摘记录。涵盖全球7大专利局和16,000种期刊16个学科中与化合物性质…

全网最详细Gradio教程系列5——Gradio Client: python

全网最详细Gradio教程系列5——Gradio Client: python 前言本篇摘要5. Gradio Client的三种使用方式5.1 使用Gradio Python Client5.1.1 安装gradio_client5.1.2 连接Gradio应用程序1. 通过URL连接2. 通过SpaceID连接3. 辅助:duplicate()和hf_token4. Colab Noteboo…

ajax学习1

<!-- 目标&#xff1a;使用axios库&#xff0c;获取省份列表数据&#xff0c;展示到页面上 1.引入axios库 --> <p class"my-p"></p> <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></ script> <sc…

Tomcat项目本地部署

今天来分享一下如何于本机上在不适用idea等辅助工具的前提下&#xff0c;部署多个tomcat的web项目 我这里以我最近写的SSM项目哈米音乐为例&#xff0c;简单介绍一下项目的大致组成&#xff1a; 首先&#xff0c;项目分为4个模块&#xff0c;如下图所示&#xff1a; 其中&…

力扣高频SQL 50题(基础版)第十八题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题1633. 各赛事的用户注册率题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题 1633. 各赛事的用户注册率 题目说明 用户表&#xff1a; Users --…

RPG素材Unity7月20闪促限时4折游戏开发资产兽人角色模型动画休闲放置模板物理交互流体水下焦散VR界面UI2D模板场景20240720

今天这个是RPG素材比较多&#xff0c;还有一些休闲放置模板、FPS场景素材、角色模型、动画、特效。 详细内容展示&#xff1a;www.bilibili.com/video/BV1Tx4y1s7vm 闪促限时4折&#xff1a;https://prf.hn/l/0eEOG1P 半价促销&#xff1a;https://prf.hn/l/RlDmDeQ 7月闪促…

小红书(社招二面)算法原题

萝卜快跑涨价 距离我们上次谈 萝卜快跑 不足半月&#xff0c;萝卜快跑迎来了不少"反转"。 先是被曝远程后台有人操控&#xff0c;真实日成本超 400&#xff1a; 最近还被不少网友吐槽&#xff1a;萝卜快跑涨价了&#xff0c;如今价格和网约车持平。 据不少博主实测&a…

17 Python常用内置函数——基本输入输出

input() 和 print() 是 Python 的基本输入输出函数&#xff0c;前者用来接收用户的键盘输入&#xff0c;后者用来把数据以指定的格式输出到标准控制台或指定的文件对象。无论用户输入什么内容&#xff0c;input() 一律作为字符串对待&#xff0c;必要时可以使用内置函数 int()、…

【SpringBoot教程:从入门到精通】掌握Springboot开发技巧和窍门(四)-Vue项目配置环境、导航栏

主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; 文章目录 前言 Vue项目配置环境 安装依赖 创建菜单 总结 前言 主要写前端页面&#xff0c;采用vue框架写页面的导航栏&#xff01;&#xff01;&#xff01; Vue项目配置环境 安装…

【算法】分布式共识Paxos

一、引言 在分布式系统中&#xff0c;一致性是至关重要的一个问题。Paxos算法是由莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在1990年提出的一种解决分布式系统中一致性问题的算法。 二、算法原理 Paxos算法的目标是让一个分布式系统中的多个节点就某个值达成一致。算…

2000-2023年上市公司全要素生产率数据LP法(含原始数据+计算代码+结果)

2000-2023年上市公司全要素生产率数据LP法&#xff08;含原始数据计算代码结果&#xff09; 1、时间&#xff1a;2000-2023年 2、指标&#xff1a;stkcd、year、证券代码、固定资产净额、资产总计、负债合计、支付给职工以及为职工支付的现金、购建固定资产无形资产和其他长期…

Monaco 使用 LinkedEditingRangeProvider

Monaco LinkEdit 功能是指同时修改同样的字符串&#xff0c;例如在编辑 Html 时&#xff0c;修改开始标签时会同时修改闭合标签。Monaco 支持自定义需要一起更新的字符串列表。最终效果如下&#xff1a; 首先&#xff0c;通过 registerLinkedEditingRangeProvider 注册 LinkEd…