typora保护机制与注册逆向分析

、起因

一直比较喜欢Typora的简洁与美观(尝试过用 vscode 搭配插件编辑 markdown 文件,体验还是要差一些的),突然发现自己windows机器上很久前安装的typora不让用了,提示:
 

版本过期


幸好原始安装文件还在,对比一下版本,原始安装文件是 0.9.93 版本,而不让用的是 0.10.11。
在我的 ubuntu22.04 机器上安装有 1.7.4 版本,win10上安装了 1.7.6版本。貌似最新的版本开始收费了,还分国内/国外两个版本。(typora.io 这个官网貌似被墙了,真不知道为了个啥。)
本着一个cracker无知者无畏的折腾到底的精神,开始瞎整!

二、摸底、知识和工具准备

逆向分析的第一步就是要了解目标软件是用啥开发的,架构是啥。
一看 typora.exe 好家伙,100多个M,这么大的可执行程序还分析个啥?IDA分析一下都不知道要多久。
由于目前主用 ubuntu22.04 系统,想着顺便从 0 开始学习使用 ghidra 进行 Linux 平台的逆向分析工作。于是从一个小目标开始,需要瞎搞的知识点有了第一次扩充。

  • ubuntu平台,用ghidra分析 1.7.4 版本;
  • win7平台,用IDA6.8+x64DBG分析0.10.11版本;
  • win10平台,用IDA6.8+x64DBG分析 1.7.6版本。(1.7.6 在我的win7下会出现不能在kernel32里面定位 discardVirtualMemory的错误)

悲催的是用 ghidra 静态分析 1.7.4 版本的 typora 有160多M,分析了10个小时也没结束。这个弯路绕太远了,毫无意义。直接放弃。

正确的打开方式:直接网上搜索
有大神直接用 IDA 分析 typora.exe,提示了一些有用的东东。结合两位成功破解的大神的文章,可以确定 typora 是使用 electron + nodejs 框架,用 javascript 开发的跨平台的桌面程序。(和vscode类似)
所以,啥是 electron/nodejs/javascript?0 基础的暴走,奇怪的知识点需要进行第二次扩充。
这几个每一个都是大部头,越底层的难度越大。被V8引擎绕进去花了不少时间(虽然了解一下好处很多,带着VM的思路去了解,能有不少收获),但就typora的破解来说,只需要涉猎以下内容:

  1. electron app 的目录结构。
  2. asar 打包/解压工具 (npm i -g asar)
  3. node.js: 模块/API/运行环境构建/npm工具;node 命令行环境下运行 js 代码(类似python命令行环境)
  4. V8 引擎:几乎用不到,因为用到了 Addon N-API 接口库。当然了解V8的好处还是很多的。真实的托管类数据结构,都是在V8引擎里面定义的,有详细的参考手册。
  5. node.js Addon:通过 C++ 编写的扩展模块,用于扩展 Node.js 的功能。这个是重点,需要知道怎么写扩展模块的基本逻辑。(至少了解 V8 和 C++ 相互调用变量/函数的一两个例子)
  6. N-API(Node-API)是一组稳定的 C 语言 API,用于编写跨平台的 Node.js Addon。N-API 提供了一套抽象层,使得开发者可以更轻松地编写可移植的扩展模块,而无需担心不同版本的 Node.js 或不同平台之间的兼容性问题。
    这个是需要重点把握的,熟练查阅 nods.js 中 N-API 的文档。
    N-API 实际上是 V8 接口的封装,进行了抽象,但对逆向增加了困难。因为其参数都是一系列 void** 之类的指针,具体在 V8 层进行实际的数据类型转换。所以逆向时,对于托管对象的指针,就别想着查看其数据了。重点应该放在 C++ 的本地数据结构上。

三、聚焦 main.node

node.js Addon 可以在 js 层直接 require 一个 dll/so。electron 入口可能也是 require 引入 package.json 及里面定义的入口 js 文件的。
0.10.11 版本:PEid 查看算法常数,发现 sbox/rsbox,基本可以确定使用了AES算法。只是windows版本去符号,要从 rbox 逆推找到 AES KEY 和取定其 mode 难度还是比较大的。网上也有人分析到这一步放弃的。当然大神可以直接找到 mode CBC iv 和 AES KEY。
1.7.4 linux 版本:神奇的是没有去除符号,但与0.10.11版本不同,KEY 和 mode 的地方进行了混淆处理,不知道把 CBC 的 iv 怎么藏起来了。懒得去反混淆分析了(主要是能力不够)
有大神直接给出了 main.node 保护electron app的概念验证项目:https://github.com/toyobayashi/electron-asar-encrypt-demo
有了第二部分提到的知识准备后,就可以尝试看一下这个项目。当然像我一样 0 基础的,也可以边看边学,借助 N-API 文档和chatGPT的帮助。
简单总结一下这个项目的思路:

  1. 由于 node.js 在 js 层可以 hook api,任意隐藏加解密的手段都会失效,所以要想办法将加解密放到 C++ 层去做。(这里可以只将 license 模块放到 addon 里面实现,估计是太过于明显直接暴露攻击目标,所以选择隐藏和加密入口)
  2. 通过重载 V8::_compile() 函数,实现针对性的解密。具体实现有 global 和 this module 的发现/遍历/重建require函数/require原始入口等问题,不是专家,也没搞懂,就破解来说,也不需要搞懂,根据关键字定位 main.node 里的关键函数就行。

结合V8执行js的基本逻辑来理解上面的思路:

1

2

3

4

5

6

7

// 定义一个 JavaScript 代码字符串

  V8::Local<V8::String> source_code =

      V8::String::NewFromUtf8(isolate, "'Hello, ' + 'World!'").ToLocalChecked();

  // 编译和运行 JavaScript 代码

  V8::Local< V8::Script> script = Script::Compile(context, source_code).ToLocalChecked();

   V8::Local< V8::Value> result = script->Run(context).ToLocalChecked();

V8执行js代码有两个过程,Compile 和 Run。Compile 只接受 V8 的托管字符串(js代码),而 C++ 字符串需要在 isolate(V8的一个独立的运行时虚拟机容器)内创建一个托管的 V8::String,用 N-API 库的话,相应函数是 napi_create_string_utf8(...)。
所以,既然把入口 js 的代码放到 C++ 层加解密,那么要执行它,比然经过上面的三步。重载 _compile 函数就是为了在这一步,给传入的 C++ string(js code)进行解密并创建对应的托管String,以便传给原来的 compile 函数。
理解了这个过程,那么最快、最容易的获取解密后的入口 js code,就在这个过程中。有两个思路,后面介绍。

四、分析 main.node

1、找入口:

  • 0.10.11 windows版本,用IDA查看 exports 导出表,然后一路跟踪下来:

    exports

     

    _register_main

     

    main_module_register

     

    _napi_main

     

    _init


    addon 开发最后一个宏 NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) 实现的导出绑定,实际上就是调用 napi_module_register 注册了一个module,这个module结构包含一些源代码开发环境的一些信息,和一个__napi_main,在__napi_main中传递了 init 函数的地址。
  • 1.7.6 win10版本,导出表里面只有一个 DllEntryPoint。需要手动在 imports 里面找 napi_module_register,剩下的流程和上面一样。可以定位到 sub_18000492B 就是 init 入口。

    1.7.6 init

  • 1.7.4 linux版本,用ghidra查看发现 exports 几乎导出了所有的符号名。不过作者还是遵循了 _init 这个函数名。

实际上找入口这一步是多余的,有了源代码只要结合关键字符串 _compile 和相应的函数调用,就能很快定位到 init 函数。只是作为一个cracker对流程跟踪的执念,瞎整。

2、找_compile的重载函数:跟着源代码中的关键字符串找,很容易定位

1

2

3

4

5

6

7

module_prototype.DefineProperty(

  Napi::PropertyDescriptor::Function(env,

    Napi::Object::New(env),

    "_compile",

    ModulePrototypeCompile,    // 就是这个函数

    napi_enumerable,            // 实际这个传递参数的地方做了一个wrapper。

    addon_data));

3、作者的 trick 一。

疑似的ModulePrototypeCompile这个函数往里面看的时候,和源代码完全不一致:
 

wrapper of ModulePrototypeCompile


当时也懵了,因为自己是在windows系统上用IDA完全对照源码静态分析。使用静态分析工具查看 refrence to 和 refrence from 都连不上。无奈之下启用 x64DBG 调试。(找到真实的ModulePrototypeCompile函数还是很容易的,因为根据源代码,这个函数里面有非常明显的字符串"app.asar",而且是一个对称的if ... else ...结构。只是想弄清楚开发者到底怎么魔改的。)最后发现真实的ModulePrototypeCompile地址被打包在了上面替换函数的参数里面。调用变成了 call qword ptr [r8],从而避免了静态分析时候被一撸到底。实际的trick是这样的:
 

ModulePrototypeCompile


直到分析 1.7.4 linux版本的时候,就看的很清楚了。
 

ModulePrototypeCompile in Linux


两者结合可以清楚看到多了一层 Wrapper 把真实函数地址隐藏在 Wrapper的参数里面。

4、AES解密

后面的部分对于 0.10.11 windows版本来说,和源代码几乎一模一样,就是AES解密的过程,没有源代码那么多函数层级,中间的函数调用估计被 inline 了。但基本结构是一样的。CBC 模式的 iv 被藏在了实际chunck的最前面16个字节。
对于 1.7.4 Linux 版本整体架构不变,但 getKey 和 getIV 做了混淆,IV 怎么藏的也没看明白。尝试用函数的返回值,参照老版本的格式进行解密,发现是错误的。对混淆没什么能力,还是绕路吧。毕竟最终目标是去 main.node 运行。

五、获取源代码/解密app.asar

1.     0.10.11 windows版本:

AES KEY


可以直接解密 app.asar(因为打包了几乎所有重要 js 文件,所以一个个调试获取源码也麻烦。)
其AES KEY就编码在指令中,无论是静态获取还是调试 getKey 处获取都可以。
IV 是放在chunck前的16个字节。根据这两个条件,可以直接编写代码对 app.asar 进行解密。

2.     1.7.4 linux 和 1.7.6 win10版本:

因为看不懂 key 和 iv 的混淆,只能骚操作——直接调试获取 atom.js 的源代码。(只加密了这一个文件,估计是要保证启动速度)
在第三部分的最后,卖了一个关子,说到有两种方式可以获取解密后的 atom.js 入口,这里介绍一下:
(1)其实这个main.node加密框架本身就提供了获取方法,既然能重载_compile,在调用原始_compile之前对js code 进行解密,那么这个框架本身,也可以被cracker用来再次重载_compile,等着后续的main.node将解密好的 js code 传过来,然后 console.log 就可以了。
我不是搞开发的,看这个框架要配置编译环境貌似挺复杂的,留给有electron开发经验的朋友吧。这个方法的好处是:一次投入,终身享受。只要还使用这个框架,点下鼠标就能获得源代码。(当然typora作者也是有备而来,后面会讲到)
(2)napi_create_string_utf8,这个函数接受 C++ 字符串,可以直接从 GDB 或者 x64DBG 中把参数的值 dump 出来。不过因为调用这个函数的地方很多,需要手动定位 ModulePrototypeCompile 函数解密分支里面的那个,下断点。Decrypt函数直接返回了托管字符串,所以还需要深入进去找到正确的返回前最后一个调用 napi_create_string_utf8 的地方。

1

iVar4 = napi_create_string_utf8(pnVar15,&DAT_00145b42,0,ppvVar14);

当然大神使用 frida 动态挂钩 napi_create_string_utf8 这种骚操作,咱小白也不懂。

六、去 main.node 运行

优点是可以保持最快的加载速度,相当于完全开源了。

1、0.10.11版本

根据0.9.93版本的目录架构,将解密后的文件丢到 typora/resources/app/ 目录下,其他保持不变,就可以直接运行了。

2、1.7.4 linux 和 1.7.6 win10版本

使用解密后的 atom.js 按照 0.10.11 版本那样直接运行 win10 会弹窗提示 scheme 变量没有定义;linux 下有 typora 进程,不提示错误,需要用 GDB 调试运行才能知道,错误是一样的。这又是闹哪一出?

  • 找不到暗桩怎么办?
    一开始不能确定导致变量没定义的原因,可能在 js 层,也可能在 main.node 层。js 层搜索了一圈没啥发现,感觉暗桩及有可能还是在 main.node 里面。想个办法验证一下:
    • patch 函数 ModulePrototypeCompile 里面对于加密/非加密 js code 的 if ... else... 逻辑,都转向非加密的逻辑。
    • 将解密出来的 atom.js 打包成 app.asar。
    • 可以成功运行
      其实完成这一步,也可以实现破解了,后面注册逻辑就都是 js 层面的事情了。不过对于一个喜欢瞎折腾的 cracker,做到这个程度,不是很满意,于是继续瞎整。。。
  • 作者的 trick 二
    在 js 层搜索一下 scheme 没有特殊的用法,对照 0.10.11版本,scheme 是定义一个自定义协议解析的字段,其值为 typora,可以使用 typora://xxx 这样的协议进行处理。
    在 main.node 里面全局搜索 scheme 啥也没发现,一时陷入僵局,作者显然是埋了一个深坑。因为是在 win10 下分析的去符号的版本,所以一时没有头绪。后来决定换个思路,atom.js 最后启动窗口肯定是要加载一个 html 的,这就是 typora 程序的入口。按照这个逻辑 发现了 “entry” 字符串/变量,其值应该是 window.html。现在有了两个键值对 scheme --> typora 和 entry --> 包含window.html。用这4个字符串去IDA搜索交叉引用,可以很快找到一个函数 sub_180012290。分析这个函数,实际一共埋了5个暗桩:addonPath,entry,scheme,shost,shostc。

这5个暗桩,有的是隐藏了键名,有的是隐藏了键值。

  • 对键名的隐藏:

    KEY NAME


    这是键值对 addonPath :app.asar。键名 addonPath 的ascii码有些字符做了 + 0x0a 处理。对键值 app.asar 没有处理,就是字符串引用。
  • 对键值的隐藏,1.7.6 win10版本似乎是花了功夫,进行了比较复杂的处理,似乎还加了混淆。(懒得去深入研究,主要是能力不够)还是看 1.7.4 linux版本的比较直观。

    KEY value


    这是键值对 entry :typora://app/typemark/window.html。键名没有加密,键值的处理方式很直观。当然 shost 的值藏的更复杂一些,主要是将单字节的 ascii 变成了4直接的 int。不一一列出来了。
  • 最后的问题,这些键值对加到哪里去了?
    安装函数linux版本名字是 _initGlobal。当然无论哪个版本,这个函数一开始的 napi_get_global 函数说明了一切。都加到 js 层的全局对象 global 里面了。由于我们要去 main.node 运行,5个暗桩实际上只有后面4个有用。

1

2

3

4

5

6

napi_get_global(env, global);

global['addonPath'= 'app.asar'

global['entry'= 'typora://app/typemark/window.html'

global['scheme']='typora'

global['shost'= 'https://store.typora.io'

global['shostc'= 'https://dian.typora.com.cn'

1.7 版本的 js 使用了 webpack 打包,main.node init 最后的入口调用有两步:

1

2

const a = require('./atom.js');        // require js 貌似一定要带路径,不然找不到模块,js 也不懂,唉

a();            // 从 webpack 打包的 atom.js 看,这是一个空函数。反正main.node 调用了,那就照葫芦画瓢吧。

有了上面这些信息,单独写一个 main.js 把4个全局变量加进去,按步骤调用入口就能实现去 main.node 运行了。

七、注册思路

这些属于 js 层面的东东,还是经过 webpack 打包和混淆过的。对于 javascript 小白来说,看这种代码和天书差不多。找了个debundle工具,能还原未加密的 dist/static/js/ 里面的文件,但还原 atom.js 报错。最后找了个 webcrack 工具,效果是比较好的格式化了 atom.js,比vscode js fommatter 之类的强。果断祭出大杀器——chatGPT。一开始心太黑,直接把整个 atom.js 复制给 chatGPT,想让它给格式化代码,并给出解释。结果 chatGPT 直接给了一个长长的菜谱,真的是用心良苦。

1、0.10.11版本

0.x版本应该都是作者发布的开发测试版本。而0.10版本很可能是作者第一个使用 main.node 进行加密的验证版本,因为 js 里面连个注册页面都没有,只有简单的验证逻辑和时间校验,超时不让用。
让 0.10.11版本重新跑起来也很间,直接将 License.js 里面表述注册与否的变量的初始值从 null 改成 true 就可以了。

1

hasLicense = true,  // null,

2、1.7.4 linux 和 1.7.6 win10版本。

这两个版本在 main.node 里面对 AES KEY 和 IV,以及暗桩的处理上,有一些差异,win10版本的更复杂和强化了一些。js 层上,关于注册的 445 类,对win32系统有着更加强化的校验。1.7.4linux版本,似乎是可以无限试用的,超时验证逻辑存在bug。
js 分析后,确定 445 类是处理所有注册/校验相关的。联网校验优先,涉及数据结构:

1

SLicense 的初步结构:base64(RSA.encrypt(json2str({license:xxx, email:xxx, type:xxx...})))#failCounts#lastRetryDate

注册信息会用服务端私钥加密,本地用公钥解密。再进行 email 和 license 的校验。本地注册也是这个解密验证过程。网络验证不成功,会清空 SLicense,回到未注册版本。
由于公钥解密的介入,想要完美本地注册已经没有意义。想持续使用的思路大致两个(没仔细研究,可能还有坑,看个大概齐吧):

(1)无限试用:

手动修改 IDate(install date) 时间。

  • win10 版本在注册表 HKCU\Software\Typora\IDate。
  • linux 版本在 ~/.config/typora/{hash_machine_id} 文件里面。文件格式只是将 json 文件的 ascii 每个字节编成 16 进制文本保存。用 Buffer.from(dataStr, 'hex').toString() 就可以解码。单独改这里貌似也会有问题的,因为会用 ~/.config/typora/profile.data 文件的创建时间恢复 IDate。不过由于时间校验对linux版本的bug存在,似乎不用改,也可以一直免费用。(有可能是我对 js 的理解不够全面,不保证对)
(2)patch js code:
  • 去掉RSA解密,其他保持不变,可以实现手动注册;然后将 shostc 改成和 shost 一样,利用GFW让网络注册失效。
  • 直接 patch 掉整个注册校验过程。反正要patch,不如彻底一些,一劳永逸。

八、加固建议

由于采用 main.node 这种框架把入口的解密放到 C++ 层,那么不可避免的,框架本身可以用来直接获取解密后的 js code,或者是绕不开字符串从 C++ 到 V8容器托管的转换过程。这是 electron 项目保护最大的难点。
作者也进行了很多努力:AES 关键的 key 和 iv 进行了隐藏和混淆;埋了一些暗桩并加密。但用前面的各种骚操作都可以绕过。
个人建议既然埋暗桩,与其花经历在几个全局变量的名字和值上进行各种花式操作,还不如把 445 注册校验类整体/部分放到 main.node 中。至少尽可能让不能脱离 main.node 运行。底层还能使用各种成熟的二进制保护技术,甚至可以引入虚拟机保护,增加逆向成本。

九、后记

typora风格的markdown编辑器,好像github上有开源的了,有道笔记的 markdown笔记似乎也改成typora这种所见即所得风格了(不过上图要付费,或者自己注册一个免费图床)。选择面变多了。不过typora依然是桌面最优雅的。
整个逆向分析过程学到了不少东西,毕竟几乎整个是从 0 开始学起的。
在 ubuntu 平台使用 ghidra 进行静态分析和动态调试;
对面向对象有了更深层次的理解;
托管与非托管的相互调用,从C#、V8 js、python,有点理解python作为胶水语言在这种场景下为什么强大;发现 js 后面的 AST 是一条不归路啊。
对框架的理解,在逆向中的作用越来越重要了。现在的程序似乎都依赖于某个框架。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NETWeb开发(C#版)-day1-C#基础+实操

目录 .NET实操&#xff1a;创建项目执行 C#基础语法数据类型变量实操001_变量如何在一个解决方案 中创建另一个项目实操002结构实操003-if else实操004-多分支多行注释按钮实操&#xff1a;循环 面向对象基础如何在同一个项目下创建新的.cs文件实操-类的定义与访问实操-练习实操…

55基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲(椒盐)噪声

基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲&#xff08;椒盐&#xff09;噪声五组噪声模型&#xff0c;程序已调通&#xff0c;可直接运行。 55高斯噪声、瑞利噪声 (xiaohongshu.com)

深度解剖Linux权限的概念

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;牢记Linux权限的概念。 > 毒鸡汤&#xff1a;你…

短视频矩阵seo系统源码搭建----技术定制化开发

一、需要遵循一下技术开发步骤&#xff1a; 1. 确定需求和功能&#xff1a;明确系统的主要目标和需要实现的功能&#xff0c;包括关键词研究、短视频制作、外链建设、数据分析、账号设置优化等方面。 2. 设计系统架构&#xff1a;根据需求和功能确定系统的架构&#xff0c;包…

windows上 Nexus 批量上传 maven依赖npm依赖

windows上 Nexus 批量上传 maven依赖/npm依赖 前言&#xff1a;windows系统上要有git环境&#xff0c;不然sh文件执行不了 1.批量上传maven依赖 设置脚本&#xff0c;把脚本放在依赖包的根目录执行&#xff0c;脚本名为upload.sh #!/bin/bash# 定义变量 while getopts &quo…

小程序中如何设置门店信息

小程序是商家转型升级的利器&#xff0c;小程序中门店信息的准确性和完整性对于用户的体验和信任度都有很大的影响。下面具体介绍门店信息怎么在小程序中进行设置。 在小程序管理员后台->门店设置处&#xff0c;可以门店设置相关。主要分为2个模块&#xff0c;一个是门店级…

智能一体化管网水位监测仪怎么样?

城市排水管网是城市正常运行的关键环节&#xff0c;这是地上和地下通道的连接点&#xff0c;一旦出现问题便会影响城市生命线建设的工程进展。在复杂的地下管道内想要了解水位数据&#xff0c;对于政府部门来讲是一个管理难题。如果可以采取智能产品在其中发挥作用&#xff0c;…

Java Web——前端HTML入门

目录 HTML&CSS3&JavaScript简述 1. HTML概念 2. 超文本 3. 标记语言 4. HTML基础结构 5. HTML基础词汇 6. HTML语法规则 7. VS Code 推荐使用的插件 8. 在线帮助文档 HTML&CSS3&JavaScript简述 HTML 主要用于网页主体结构的搭建&#xff0c;像一个毛坯…

低价寄快递寄件微信小程序 实际商用版 寄快递 低价寄快递小程序(源代码+截图)前后台源码

盈利模式 快递代下CPS就是用户通过线上的渠道&#xff08;快递小程序&#xff09;&#xff0c;线上下单寄快递来赚取差价&#xff0c;例如你的成本价是5元&#xff0c;你在后台比例设置里面设置 首重利润是1元&#xff0c;续重0.5元&#xff0c;用户下1kg的单页面显示的就是6元…

Go 14岁了

今天我们庆祝Go开源十四周年&#xff01;Go度过了美好的一年&#xff0c;发布了两个功能齐全的版本和其他重要的里程碑。 我们在2月份发布了Go 1.20&#xff0c;在8月份发布了Go 1.21&#xff0c;更多地关注实现改进而不是新的语言更改。 在Go 1.20中&#xff0c;我们预览了配置…

【论文精读】DMVSNet

今天读的是一篇发表在ICCV 2023上的文章&#xff0c;作者来自华中科技大学。 文章地址&#xff1a;点击前往 项目地址&#xff1a;Github 文章目录 Abstract1 Introduction2 Relative Work3 Motivation3.1 Estimated bias and interpolated bias3.2 One-sided V.S. Saddle-shap…

Android修行手册-POI操作Excel实现超链接并且变为蓝色

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…

黑洞路由的几种应用场景

第一种在内网中产生环路&#xff1a; 这种核心交换机上肯定写一条默认路由 0.0.0.0 0 10.0.0.1 出口路由要写一条192.168.0.0 16 10.0.0.2 如果出口路由访问一条不存在的内网网段&#xff0c;又或者访问的那台终端停机了&#xff0c;那就会产生三层环路&#xff0c;数据包在…

如何导出PPT画的图为高清图片?插入到world后不压缩图像的设置方法?

期刊投稿的时候&#xff0c;需要图片保持一定的清晰度数&#xff0c;那么我们怎么才能从PPT中导出符合要求的图片呢&#xff1f; 对于矢量图绘图软件所画的图&#xff0c;直接导出即可。 而PPT导出的图片清晰度在60pi&#xff0c;就很模糊。 整体思路&#xff1a; PPT绘图——…

初识-Servlet (第一个 Servlet 程序详解)

Servlet 是什么? Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序员的 API, 帮助程序员简单高效的开发一个 web app. 静态页面就只是单纯的 html 动态页面则是 html 数据 第一个 Servlet 程序 我们写一个 hello world 预期写一个 Servlet 程序, 部署到 Tomca…

基于SpringBoot+Vue的在线学习平台系统

基于SpringBootVue的在线学习平台系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 用户界面 登录界面 管理员界面 摘要 本文设计并实现了一套基于Spri…

计算机毕业设计选题推荐-校园交流平台微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

什么是代理IP池?如何判断IP池优劣?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…

ubuntu下tensorrt环境配置

文章目录 一、Ubuntu18.04环境配置1.1 安装工具链和opencv1.2 安装Nvidia相关库1.2.1 安装Nvidia显卡驱动1.2.2 安装 cuda11.31.2.3 安装 cudnn8.21.2.4 下载 tensorrt8.4.2.4 二、编写CMakeLists.txt三、TensorRT系列教程 一、Ubuntu18.04环境配置 教程同样适用与ubuntu22.04…

2023.11.13 hive数据仓库之分区表与分桶表操作,与复杂类型的运用

目录 0.hadoop hive的文档 1.一级分区表 2.一级分区表练习2 3.创建多级分区表 4.分区表操作 5.分桶表 6. 分桶表进行排序 7.分桶的原理 8.hive的复杂类型 9.array类型: 又叫数组类型,存储同类型的单数据的集合 10.struct类型: 又叫结构类型,可以存储不同类型单数据的集合…