一、效果预览typecho默认禁用插件会情况所有配置信息,如果配置信息比较多的不小心点错了就很麻烦。二、实现方法1.修改config2.修改deactivate修改插件Plugin.php找到public static function deactivate()，修改如下public static function deactivate() { //防呆设计 $uninstallConfig = Helper::options()->plugin('你的插件名称')->uninstall; if (!$uninstallConfig) { throw new Exception('为了防止误操作，请先设置 “允许卸载插件” 后再试。'); return; } } 第二步，找到config个人配置里面{hide}public static function config(Typecho_Widget_Helper_Form $form) { //……你的代码 // 防呆设计 $uninstall = new Typecho_Widget_Helper_Form_Element_Radio('uninstall', array(1 => _t('允许卸载'), 0 => _t('禁止卸载')), '0', _t('是否允许卸载插件'), _t('卸载插件会导致所有的屏蔽清空，且无法恢复，请慎重选择。')); $form->addInput($uninstall);{/hide}
一、介绍插件支持IPV6和IPV4归属地获取并且 为了避免卡顿或者获取失败 数据库都是本地 不调用任何第三方API接口二、使用说明解压后修改文件夹名为 XQLocation，将插件上传至网站目录的 /usr/plugins 下在 Typecho 后台「插件管理」处启用插件在需要显示的地方插入以下代码：<?php XQLocation_Plugin::render($comments->ip); ?>(此项也适用于后台)如果是系统自带的评论框架找到var/Widget/Comments/Archive.php大概为108行，插入<?php XQLocation_Plugin::render($this->ip); ?>三、下载地址{cloud title="" type="lz" url="https://suxiaoq.lanzoul.com/iCQx62fgkieb" password=""/}
一、前言360极速浏览器是chrome内核里面更符合国人习惯的浏览器，一直使用360极速浏览器X，但是划词搜索等一直是so.com，不太喜欢，研究了下可以使用16进制编辑器进行修改。二、教程1.下载ultraedit十六进制编辑器2.找到360极速浏览器内chrome.dll，目录为Application\22.3.1010.64下（后面纯数字为版本号，各版本不一样，可以使用全局搜索工具进行定位）3.chrome.dll拖入ultraedit进行数据修改，使用ctrl+r搜索替换，记得勾选查找ASCII三、修改数据代码说白了就是代码平替，要注意长度要统一。每段数据查完完需要回到文件头重新查找，修改完后保存文件，再替换到原来目录就可以了！记得做好备份！https://www.so.com/s?ie={inputEncoding}&q={searchTerms}替换为https://www.baidu.com/s?wd={searchTerms}&ie=utf-8 (这里有几个空格，保持长度一致){dotted startColor="#ff6c6c" endColor="#1989fa"/}https://www.so.com/s?q=%s&src=360csex_zoned替换为http://www.baidu.com/baidu?word=%s&ie=utf-8{dotted startColor="#ff6c6c" endColor="#1989fa"/}https://www.so.com/s?ie=utf-8&src=hao_360so&q=替换为https://www.baidu.com/s?&ie=utf-8&ie=utf-8&wd={dotted startColor="#ff6c6c" endColor="#1989fa"/}https://www.so.com/s?ie=utf-8&src=360csex_addr_sug&q=替换为https://www.baidu.com/s?&ie=utf-8&src=360chrome_z&wd={dotted startColor="#ff6c6c" endColor="#1989fa"/}https://www.so.com/s?ie=utf-8&src=360se7_addr&q={searchTerms}替换为https://www.baidu.com/s?&f=8&src=360chrome_z&wd={searchTerms}其它的请自行搜索。赶快打开浏览器去测试吧！四、百度搜索参数代码{hide}http://www.baidu.com/baidu?word=%s https://www.baidu.com/s?&ie=utf-8&ie=utf-8&wd= https://www.baidu.com/s?&f=8&src=360chrome_z&wd= https://www.baidu.com/s?wd={searchTerms}&ie={inputEncoding} https://www.baidu.com/s?&f=8&src=360chrome_z&wd={searchTerms}{/hide}
一、插件的基本结构在 Typecho 中，插件通常是一个独立的 PHP 文件，默认放置在usr/plugins/目录下。插件文件名即为插件名，插件类名也应与文件名相同。插件必须实现 Typecho_Plugin_Interface 接口。基本结构：<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; /** * 插件的描述信息 * * @package PluginName */ class PluginName_Plugin implements Typecho_Plugin_Interface { // 插件激活方法 public static function activate() {} // 插件停用方法 public static function deactivate() {} // 插件配置面板 public static function config(Typecho_Widget_Helper_Form $form) {} // 个人用户的配置面板 public static function personalConfig(Typecho_Widget_Helper_Form $form) {} // 实现的插件功能方法 // ... }说明Typecho_Plugin_Interface：插件类必须实现该接口，包含 activate、deactivate、config、personalConfig 四个方法。activate：插件激活时调用，用于注册插件方法、初始化数据等。deactivate：插件停用时调用，用于清理数据、注销插件方法等。config：插件的配置界面，用于在后台展示插件的设置选项。personalConfig：针对个人用户的配置界面。二、激活插件在 activate 方法中，可以进行以下操作：注册插件方法 ：使用 Typecho_Plugin::factory 注册插件的方法到指定的 Hook Point。创建数据库表 ：如果插件需要额外的数据表，可以在激活时创建。初始化数据 ：初始化插件所需的数据或配置。public static function activate() { // 注册插件方法到 Hook Point Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('PluginName_Plugin', 'beforeRender'); // 返回激活成功的信息 return _t('插件已激活'); }三.停用插件在deactivate方法中，可以进行以下操作：注销插件方法：清理注册的插件方法（Typecho 会自动处理）。删除数据库表：如果插件创建了额外的数据表，可以选择在停用时删除。清理数据：清理插件的配置信息或缓存。四、定义插件的配置界面在 config 方法中定义插件的配置选项。public static function config(Typecho_Widget_Helper_Form $form) { // 添加一个文本框 $text = new Typecho_Widget_Helper_Form_Element_Text('text', NULL, '默认值', _t('文本框标签'), _t('文本框提示')); $form->addInput($text); // 添加一个单选框 $radio = new Typecho_Widget_Helper_Form_Element_Radio('radio', array('1' => '选项1', '2' => '选项2'), '1', _t('单选框标签')); $form->addInput($radio); // 添加一个复选框 $checkbox = new Typecho_Widget_Helper_Form_Element_Checkbox('checkbox', array('1' => '选项1', '2' => '选项2'), NULL, _t('复选框标签')); $form->addInput($checkbox); }说明：使用 Typecho_Widget_Helper_Form_Element_* 类来创建不同类型的表单元素。调用 $form->addInput() 方法将元素添加到配置界面。五、处理插件的用户配置在插件的方法中，可以通过Helper::options()->plugin('PluginName')获取用户的配置信息。public static function someMethod() { $options = Helper::options()->plugin('PluginName'); $textValue = $options->text; // 获取文本框的值 // ... }说明：Helper::options()：获取全局的配置对象。plugin('PluginName')：获取指定插件的配置信息。六、注册插件方法（Hook Point）在 activate 方法中，使用 Typecho_Plugin::factory 注册插件的方法到指定的 Hook Point。public static function activate() { Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('PluginName_Plugin', 'beforeRender'); }说明： 具体插入点可以参考文章后半段，有专门介绍各插入点的作用及位置Typecho_Plugin::factory('类名')->方法名：指定要拦截的类和方法（Hook Point）。array('PluginName_Plugin', '方法名')：插件的方法回调。七、定义自定义路由如果插件需要自定义 URL 路由，可以在activate 方法中定义。public static function activate() { // 添加自定义路由 Helper::addRoute('plugin_route', '/plugin/route/', 'PluginName_Action', 'actionMethod'); } public static function deactivate() { // 删除自定义路由 Helper::removeRoute('plugin_route'); }Helper::addRoute：添加自定义路由。第一个参数：路由名称。第二个参数：路由规则（URL 模式）。第三个参数：处理该路由的类名。第四个参数：处理方法。Helper::removeRoute：删除自定义路由。八、加载插件的类和方法如果插件包含多个类或文件，可以通过require_once或自动加载的方式引入。// 在插件的主文件中 require_once 'libs/Helper.php'; // 使用命名空间和自动加载 spl_autoload_register(function ($class) { $prefix = 'PluginName\\'; $base_dir = __DIR__ . '/src/'; $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } $relative_class = substr($class, $len); $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; if (file_exists($file)) { require $file; } });require_once：直接引入文件。spl_autoload_register：注册自动加载函数，按照命名空间和目录结构自动加载类。九、文件载入与资源管理插件可能需要加载 CSS、JS 文件或其他资源，可以通过以下方式实现。在模板中加载资源：// 在插件的方法中 public static function header() { $cssUrl = Helper::options()->pluginUrl . '/PluginName/assets/style.css'; echo ' '; } public static function footer() { $jsUrl = Helper::options()->pluginUrl . '/PluginName/assets/script.js'; echo ' '; }注册到 Hook Point：public static function activate() { Typecho_Plugin::factory('Widget_Archive')->header = array('PluginName_Plugin', 'header'); Typecho_Plugin::factory('Widget_Archive')->footer = array('PluginName_Plugin', 'footer'); }Helper::options()->pluginUrl：获取插件目录的 URL。header、footer：在页面的头部和尾部输出内容。十、模板的使用与扩展插件可以自定义模板，或者扩展主题的模板功能。在插件目录中创建模板文件：创建themes目录 ：在插件目录下创建themes目录，用于存放模板文件。编写模板文件 ：例如，创建themes/plugin-template.php在插件中调用模板：public static function renderTemplate() { $template = __DIR__ . '/themes/plugin-template.php'; if (file_exists($template)) { include $template; } else { echo '模板文件不存在'; } }在路由处理方法中调用：public function actionMethod() { self::renderTemplate(); }十一、完整的插件示例usr/ └── plugins/ └── HelloWorld/ ├── HelloWorld_Plugin.php ├── Action.php ├── assets/ │ ├── style.css │ └── script.js └── themes/ └── hello.php十二、钩子(Hook)插入点在 Typecho 插件开发中，可以通过 Typecho_Plugin::factory('类名')->方法名的形式来拦截和扩展 Typecho 的核心功能。以下是所有可用的插入位置（Hook Points），按照类名分类，并注明调用的位置及意义。十三、插入位置的定义方式插入位置通常是通过以下几种方式定义的：方法（Method）：在 Typecho 的核心类中，某些方法被定义为插入位置。例如，Widget_Contents_Post_Edit类中的insert方法就是一个插入位置。插件可以通过Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert来拦截这个方法，并在其中执行自定义代码。 属性（Property）：在 Typecho 的核心类中，某些属性被定义为插入位置。例如，Widget_Contents_Post_Edit 类中的 insert 属性就是一个插入位置。插件可以通过 Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert 来拦截这个属性，并在其中执行自定义代码。13.1示例假设我们有一个Widget_Contents_Post_Edit类，其中定义了一个insert方法：class Widget_Contents_Post_Edit extends Typecho_Widget { public function insert() { // 插入文章的逻辑 } }在插件中，我们可以通过以下方式拦截这个insert方法：class XXX_Plugin implements Typecho_Plugin_Interface { public static function activate() { Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert = array('XXX_Plugin', 'publish'); } public static function publish($post) { // 自定义的插入逻辑 } }在这个例子中，XXX_Plugin 插件在 Widget_Contents_Post_Edit 类的 insert 方法执行时，会调用 XXX_Plugin::publish 方法来执行自定义的插入逻辑十四、常见Hooks整理1.Widget_Contents_Post_Editinsert：在插入文章时触发。update：在更新文章时触发。delete：在删除文章时触发。finishPublish：在文章发布完成后触发。2.Widget_Contents_Page_Editinsert：在插入页面时触发。update：在更新页面时触发。delete：在删除页面时触发。finishPublish：在页面发布完成后触发。3.Widget_Comments_Editinsert：在插入评论时触发。update：在更新评论时触发。delete：在删除评论时触发。finishComment：在评论操作完成后触发。finishDelete：在评论删除后触发。4.Widget_Users_Editinsert：在创建用户时触发。update：在更新用户信息时触发。delete：在删除用户时触发。finishCreate：在用户创建完成后触发。finishDelete：在用户删除后触发。5.Widget_Metas_Category_Editinsert：在创建分类时触发。update：在更新分类信息时触发。delete：在删除分类时触发。finishCreate：在分类创建完成后触发。6.Widget_Metas_Tag_Editinsert：在创建标签时触发。update：在更新标签信息时触发。delete：在删除标签时触发。finishCreate：在标签创建完成后触发。7.Widget_Feedbackcomment：在用户提交评论时触发。trackback：在接收Trackback时触发。8.Widget_Registerregister：在用户注册时触发。9.Widget_LoginloginSucceed：在用户登录成功后触发。loginFail：在用户登录失败后触发。10.Widget_ArchivebeforeRender：在渲染文章内容之前触发。afterRender：在渲染文章内容之后触发。beforeOutput：在输出文章内容之前触发。afterOutput：在输出文章内容之后触发。handleInit：在 Archive Widget 初始化时触发。select：在构建内容查询时触发。11.Widget_CommentsbeforeRender：在渲染评论列表之前触发。afterRender：在渲染评论列表之后触发。12.Widget_Abstract_ContentscontentEx：在获取文章完整内容时触发。excerptEx：在获取文章摘要内容时触发。filter：在获取内容列表时触发。13.Widget_Abstract_CommentscontentEx：在获取评论内容时触发。excerptEx：在获取评论摘要时触发。filter：在获取评论列表时触发。14.Widget_Abstract_Metasfilter：在获取分类或标签列表时触发。15.Widget_Abstract_Usersfilter：在获取用户列表时触发。16.Widget_Abstract_Optionsfilter：在获取系统选项时触发。17.Widget_AbstractbeforeRender：在任何 Widget 渲染之前触发。afterRender：在任何 Widget 渲染之后触发。___construct：在 Widget 初始化时触发。___init：在 Widget 初始化完成后触发。___execute：在执行 Widget 主逻辑时触发。18.Typecho_Widgetwidget：在创建 Widget 实例时触发。widgetStart：在 Widget 开始执行时触发。widgetEnd：在 Widget 执行结束时触发。19.Typecho_Widget_Helper_Formrender：在渲染表单之前触发。20.Typecho_Widget_Helper_Form_Elementrender：在渲染表单元素之前触发。21.Typecho_Dbquery：在执行数据库查询时触发。fetchAll：在获取查询结果时触发。22.Typecho_Routerrouter：在路由解析时触发。dispatch：在路由调度时触发。23.Typecho_Responseredirect：在执行页面重定向时触发。24.Typecho_Cookieset：在设置 Cookie时触发。get：在获取 Cookie时触发。25.Typecho_Feedoutput：在输出RSS Feed时触发。26.Typechoinit：在系统初始化时触发。header：在输出页面头部时触发。footer：在输出页面尾部时触发。使用方法示例：Typecho_Plugin::factory('Widget_Contents_Post_Edit')->insert = array('YourPlugin_Class', 'yourMethod');说明：插入位置（Hook Point）：即可拦截的方法名。触发时机：在对应的方法执行时触发，允许插件在该时机执行自定义逻辑。使用场景：插件可以通过这些 Hook Points 实现对 Typecho 功能的扩展和定制，例如在文章发布后发送通知，在用户登录失败后记录日志等。 注意事项：确保方法存在：请确保您定义的插件类和方法存在且可被调用，避免出现错误。性能考虑：插件开发时应注意性能，避免阻塞核心功能的执行。充分测试：某些 Hook Points 可能仅在特定情况下触发，开发时需充分测试。通过以上整理，您可以清晰地了解 Typecho 插件开发中可用的所有插入位置，以及它们的调用位置和意义，方便在插件中实现所需的功能。
官方介绍比特币（Bitcoin）是一种去中心化的数字货币，由一个化名为“中本聪”（Satoshi Nakamoto）的人或团队在2009年创立。比特币的设计理念是基于区块链技术，这种技术使得比特币的发行、交易和管理无需依赖中央权威机构，从而具有以下特点：特点：去中心化：比特币的发行、交易和记录都通过全球的计算机网络（节点）共同维护，不存在中心化的管理机构。匿名性：比特币交易可以匿名进行，但并非完全匿名，因为交易记录是公开的。稀缺性：比特币总量有限，最多只能发行2100万个。可分割性：比特币可以分割到小数点后8位，即0.00000001比特币。可追溯性：比特币的交易记录是公开的，可以追溯到每一笔交易的来源和去向。应用：支付：比特币可以作为支付手段，用于购买商品或服务。投资：许多投资者将比特币视为一种投资工具，期待其价值上涨。融资：一些初创公司选择接受比特币作为投资，以降低融资成本。问题：波动性：比特币价格波动较大，可能导致投资者损失。安全性：虽然比特币具有较高的安全性，但仍存在被黑客攻击的风险。监管：不同国家和地区对比特币的监管政策不同，可能导致投资者面临法律风险。总之，比特币作为一种新兴的数字货币，具有去中心化、匿名性等特点，但也存在波动性、安全性等问题。投资者在参与比特币交易时，应充分了解其风险。{dotted startColor="#ff6c6c" endColor="#1989fa"/}最近在整理 Crypto 的资料，搜索到张洋的这篇文章，可能是写的最好的比特币原理及运作机制讲解故事，故转载至此进行分享。这篇文章的定位会比较科普，尽量用类比的方法将比特币的基本原理讲出来，不会涉及算法和协议中细节的部分。文中会给出一个虚拟的村庄叫「比特村」，整体以讲故事的方式，逐步告诉大家比特币提出的动机、解决了什么问题以及一些关键组件的目标和设计方案。以物易物的比特村话说在这个世界上，有一个叫比特村的小村庄，村庄共有几百户人家。这个村庄几乎与世隔绝，过着自给自足的生活。由于没有大规模贸易，比特村村民一直过着以物易物的生活，也就是说村民之间并没有使用统一的货币，互相间的贸易基本上就是老张家拿一袋面粉换老李家一只羊，王大嫂拿一筐野果换刘大婶两尺布。村民们一直就这么纯朴的生活着。实物货币终于有一天，村民觉得一直这样以物易物实在太不方便了，于是村子全员开会，讨论如何解决这个问题。有人提议，以便于分割且稀有的东西，例如黄金，作为一般等价物，把其它物品和黄金的对应关系编成一张表格，例如一克黄金对应一只羊，一克黄金对应一袋面粉等等，此时老张再也不用扛着一袋面粉气喘吁吁的去老李家换羊了，他只要从家里摸出一克金子，就可以去老李家牵回一只羊，而老李拿着这一克黄金可以从任何愿意出让面粉的人那里换回一袋面粉，当然也可以换取任何和一克黄金等值的物品。此时比特村进入了实物货币时代。符号货币好景不长，过了一段时间，实物货币的弊端也出现了。因为比特村附近金矿并不多，开采和冶炼金子太费时费力了。而随着使用，金子总是不断会因为磨损、丢失或有人故意囤积而发生损耗。全村人又一次坐在了一起，开始商讨对策。此时有人说，其实大家也不必一定要真的用黄金啊，随便找张纸，写上“一克黄金”，只要全村人都认同这张纸就等于一克黄金，问题不就解决了。其他人纷纷表示认同，但同时也有了新的问题：真实的黄金是需要开采和冶炼的，金矿有限，开采和冶炼也需要成本，所以没有人可以短期凭空制造大量的黄金，可写字就不同了，只要我纸够笔够，随便像写多少写多少，那这就变成拼谁家里纸多了，搞不好到时一万张纸才能换一只羊（实际上这就发生了经济学上的通货膨胀）。大家一想也是啊。不过此时又有人提出了解决方案：这个纸不是谁写都有效，我们只认村里德高望重的老村长写得，大家都认识老村长的字。老村长写一些纸，同时按照各家黄金存量发给大家等量的纸，例如老张家有二百克黄金，老村长就发给老张二百张写着“一克黄金”的纸，同时将老张家的黄金拿走作为抵押。就这样，老村长将村里所有黄金收归到自己的家里，并按各家上交的黄金数量发给等值的写有字的纸。此时村民就可以拿着这些纸当黄金进行贸易了，而且大家都认得老村长的字，其他人伪造不出来。另外，如果谁的纸磨损太严重，也可拿到老村长那里兑换新的等值的纸，另外老村长承诺任何人如果想要换成真黄金，只要拿纸回来，老村长就会把等值的黄金还给那人。因为老村长写得纸的黄金量和真实放在家里的黄金量是一样的，所以只要严格按照销毁多少纸新写多少纸的原则，每一张有效的纸总能换回相应的真黄金。此时，比特村进入了符号货币（纸币）时代。而老村长就承担了政府和银行的角色。中央系统虚拟货币又过了几年，老村长由于每天都要核对大量的旧纸币，写新的纸币，还要把各种账目仔细做好记录。一来二去，老村长操劳过度不幸驾鹤西去了。比特村再次召开全体大会，讨论应该怎么办。此时老村长的儿子二狗子自告奋勇接过了父亲的笔，承担起货币发行的责任。这个年轻的村长二狗子很聪明，他做了几天，发现好像也不用真的写那么多纸。完全可以这样：村民把纸币都交上来，销毁，但是二狗子会记录下每户上交的纸币数量。以后如果要进行付钱，例如老张要拿一克金子向老李换一只羊，就一起给二狗子打个电话，说明要将老张名下的一克金子划归老李名下，二狗子拿出账本，看看老张名下是否有一克金子，如果有就在老张的名下减掉一克，在老李的名下加上一克，这样就完成了支付，此时老李在电话中听到二狗子确认转账完成，就可以放心让老张把羊牵走了。此时比特村进入了中央系统虚拟货币时代。每个村民都不需要用实物支付，支付过程变成了二狗子那边维护的账本上数字的变更。分布式虚拟货币这新上任的二狗子是聪明，不过这人有时候是聪明反被聪明误。有一天二狗子盯着这账本，心想这全村各户谁有多少钱就是我说的算，那我岂不是……。于是他头脑一热，私自从老张帐下划了十克金子到自己名下。本以为天衣无缝，但没想到老张也有记账的习惯，有一天他正要付钱却被二狗子告知账户没钱了。老张核对了一下自己的账本，明明还有十克啊，于是拿着账本去找二狗子理论，这一核对发现了那笔未经老张同意的转账。东窗事发！比特村炸开锅了。二狗子被弹劾是不可避免了，不过通过这件事，大家发现了账本集中在一个人手里的弊端：这个体系完全依赖于账本持有人的个人信用，如果这个人不守规矩，随意篡改账本，那么整个货币系统就会崩溃如果这个人家里失火或者账本失窃，同样也会为整个体系带来毁灭性的打击正当人们不知所措时，村里一个叫中本聪的宅男科学家走上了台，告诉大家他已经设计了一套不依赖任何中央处理人的叫比特币的虚拟货币系统，可以解决上述问题。然后他缓缓讲述了自己的方案。下面我们就来看看中本聪同学是如何设计这套系统的。基础设施搭建中本聪首先说明，要对现有账簿进行如下改造：账簿上不再记载每户村民的余额，而只记载每一笔交易。即记载每一笔交易的付款人、收款人和付款金额。只要账簿的初始状态确定，每一笔交易记录可靠并有时序，当前每个人持有多少钱是可以推算出来的。账簿由私有改为公开，只要任何村民需要，都可以获得当前完整的账簿，账簿上记录了从账簿创建开始到当前所有的交易记录。 此言一出，下面立刻炸锅了。第一条还无所谓，但是第二条简直无法接受，因为账簿可是记录了所有村民的交易，这样大家的隐私不全暴露了吗。中本聪倒是不慌不忙，拿出了一对奇怪的东西。身份与签名机制（公钥加密系统）中本聪说，大家不要慌。在他的这套机制下，任何人都不使用真实身份交易，而是使用一个唯一的代号交易。他展示了手里神奇的东西，说这两件东西分别叫保密印章和印章扫描器。后面他会给村里每一户发一个保密印章和一个印章扫描器。两者的作用如下：保密印章可以在纸上盖一个章，每个印章盖出的章都隐含了一个全村唯一的一串字符，但是凭肉眼是看不出来的。也无法通过观察来制造出相应的印章。印章扫描器可以扫描某个已经盖好的章，读出隐含的信息，并在液晶屏上显示出一串字符。 有了这两个神奇的东西，大家就可以在不暴露真实身份的情况下进行交易了，而印章隐含的那一串字符就是这户人家的代号。具体如何巧妙利用保密印章和印章扫描器进行交易，会在下文详述。成立虚拟矿工组织（挖矿群体） 下一步，中本聪面向全村招募虚拟矿工，招募要求如下：矿工以组为单位，一组可以是单独的一户，也可以是几户联合为一组成为矿工不影响正常使用货币矿工每天要花费一定时间从事比特币“挖矿”活动，但是不同于挖金矿，虚拟矿工不需要拿着工具去野外作业，在家里就可以完成工作矿工有一定可能性获得报酬，在挖矿活动中付出的努力越多，获得报酬的可能性越大矿工可以随时退出，也可以随时有新的矿工加进来 很快，大约有五分之一的村民加入比特币矿工组织，共分成了7个组。建立初始账簿（创世块）下面，中本聪宣布，先根据二狗子手里的账簿，把抵押的所有黄金按账簿记录的余额退还给每位村民，然后彻底销毁这本账簿。然后，中本聪拿出一本新账簿，在账簿的第一页上记录了一些交易记录，特别的是，这些记录的付款人一栏全都是「系统」，而收款人分别是每个印章对应的隐含字符，代表初始时刻，系统为每一户默认分配了一定数量比特币，但是数量非常少，都只有几枚，甚至有些不幸的村户没有获得比特币。接着中本聪说，由于目前市面上比特币非常少，大家可以先回到用黄金做货币的时代，由于我不是村长，我也没有权利强迫大家一定要承认比特币，大家可以自行决定要不要接受比特币。不过随着比特币的流动和矿工的活动，比特币会慢慢多起来。支付与交易做了这么多铺垫，终于说到重点了，下面说一下在这样一个体系下如何完成支付。以老张付给老李10个比特币为例。付款人签署交易单为了支付 10 个比特币，老张首先要询问老李的标识字符串，例如是「ABCDEFG」，同时老张也有一个标识字符串例如是「HIJKLMN」，然后老张写一张单子，内容为「HILKLMN 支付 10 比特币给 ABCDEFG」，然后用自己的保密印章改一个章，将这张单子交给老李。另外为了便于追溯这笔钱的来源，还要在单子里注明这笔钱的来源记在哪一页，例如这个单子里，老张的10比特币来自建立账簿时系统的赠送，记录在账簿第一页。收款人确认单据签署人老李拿到这个单子后，需要确认这个单子确实是来自「HIJKLMN」这个人（也就是老张）签署的，这个并不困难。因为单子上必须有保密章，老李拿出印章扫描器，扫一下章，如果液晶屏显示出的字符和付款人字符是一致的（这里是「HIJKLMN」），就可以确认单子确实是付款人签署的。这是因为根据保密印章的机制，没有其他人可以伪造印章，任何一个人只要扫描一下印章，都可以确认单子的付款人和盖章人是否一致。收款人确认付款人余额这个系统到目前还是很有问题。通过保密印章，收款人虽然可以确认付款人确实签署了这份单子，但是无法自行确认付款人是否有足够的余额支付。之前的中央虚拟货币系统中，二狗子负责检查付款人的余额，并通知收款人交易是否有效，现在把二狗子开了，谁来负责记账和确认每笔交易的有效性呢？之前说过，中本聪设计的这个系统是分布式货币系统，不依赖任何中央人物，所以不会有一个或少数几个人负责这件事，最终承担这份工作的是之前所提到的矿工组织。老张、老李和全村其他任何使用比特币进行交易的村民都依赖矿工组织的工作才能完成交易。矿工的工作矿工的工作是整个系统的核心，也是最复杂性最高的地方。下面逐步介绍矿工的工作内容和目的。矿工的工具俗话说，工欲善其事，必先利其器。比特币矿工虽然不用铁撅、铁锨和探照灯等工具，不过也要有一些必备的东西。初始账簿。每个组首先自己复制一份初始账簿，初始账簿只有一页，记录了系统的第一次赠送空账簿纸。每个小组有若干账簿纸，每一页纸上仅有账簿结构，没有填内容，具体内容的书写规则后面讲述。下面是一张空账簿纸的样子，各个字段的意义后面会说到编码生成器（哈希函数）。中本聪又向矿工组织的每个组分发了若干编码生成器，这个东西很神奇，将一页账簿填好内容的账簿纸放入这个机器，机器会在账簿纸的“本账单编号”一栏自动打印一串由“0”和“1”组成的编号，共256个。最神奇的是，编号生成器有如下功能：生成的编号仅与账簿纸上填入的内容有关，与填写人、字体、填写时间等因素均无关内容相同的账簿纸生成的编号总是相同，但是如果内容哪怕只改一个字符，编号就会面目全非编码生成器在打印编码时还需要将所有填入账簿纸的交易单放入，机器会扫描交易单和填入交易单的一致性，尤其是保密印章，如果发现保密印章和付款人不一致，会拒绝打印编码将一张已打印的账簿纸放入，机器会判定编号是否是有效的机器打印，并且判定编号和内容是否一致，这个编号无法伪造交易单收件箱。每个矿工小组需要在门口挂一个箱子用于收集交易单。公告板。每个矿工小组同样需要一个公告板公示一些信息。有了上面的工具，矿工组织就可以开工了。收集交易单中本聪规定，每笔交易的发起人，不但要将交易单给到收款人，还要同时复制若干份一模一样的交易单投递到每个矿工小组的收件箱里。矿工小组的人定期到自己的收件箱里把收集到的交易单一并取出来。填写账簿此时小组的人拿出一张空的账簿纸，把这些交易填写到“交易清单”一栏，同时找到当前账簿最后一页，将最后一页的编号抄写到“上一张账单编号一栏”。 注意还有个“幸运数字”，可以随便填上一个数字，如12345。然后，将这样账簿纸放入编号生成器，打印好编号，一张账簿就算完成了。如果你以为矿工的工作就这么简单，那就大错特错了，中本聪有个变态的规定：只有编号的前10个数均为0，这页账簿纸才算有效。根据之前对编号生成器的描述，要修改编号，只能修改账簿纸的内容，而“交易清单”和“上一张账簿纸编号”是不能随便改的，那么只能改幸运数字了。于是为了生成有效的账簿纸，小组里的矿工就不断抄写账簿纸，但每张纸的幸运数字都不同，然后不断的重复将纸放入编码器，如果生成的编号不符合规定，这张纸就算废了，重复这个过程直到生成一串有效的编号。我们知道，如果编号的每一个数字都是随机的，那么平均写1000多张幸运数字不同的纸才能获得一个有效的编号。这就奇怪了，这些矿工为什么要拼命干这看似无意义的事情呢？还记得之前说过矿工有报酬吧，这就是矿工的动力了。中本聪规定：每一张账簿纸的交易清单第一条交易为“系统给这个小组支付50个比特币”。也就是说，如果你生成了一张有意义的账簿纸，并且被所有挖矿小组接受了，那么就意味着这条交易也被接受了，你的挖矿小组获得了50个比特币。这就是矿工被叫做矿工的原因，也是为什么之前说随着交易和矿工的活动，比特币的数量会不断增多。例如下面是一个挖矿过程，这个小组的公共比特币帐号为「UVWXYZ」。在幸运数字尝试到「533」时，系统生成了一页有效账簿确认账簿当某挖矿小组幸运的生成了一张有意义的账簿，为了得到奖励，必须立刻请其它小组确认自己的工作。前面说过，当前村里有7个挖矿组，所以这个小组必须将有效账簿纸誊抄6份快马加鞭送到其他6个小组请求确认。中本聪规定，当某个小组接到其他小组送来的账簿纸时，必须立即停下手里的挖矿工作进行账簿确认。需要确认的信息有三个：账簿的编号有效账簿的前一页账簿有效交易清单有效首先看第一个，这个确认比较简单。只要将送来的账簿纸放入编码生成器进行验证，如果验证通过，则编号有效。第二部分需要将账簿页上的「上一页账簿纸编号」和这个小组目前保存的有效账簿最后一页编号比对，如果相同则确认，如果不同，需要顺着已有账簿向前比对，直到找到这个编号的页。如果没有找到指定的“上一页账簿纸编号”对应的页，这个小组会将此页丢掉。不予确认。注意，由上面的机制可以保证，如果各个小组手里的账簿纸是相同的，那么他们都能按同样的顺序装订成相同的账簿。因为后面一张纸的编号总是依赖前面的纸的编号，编码生成器的机制保证了所有合法账簿纸的相对先后顺序在每个小组那里都是相同的（可能会有分支，但不会出现环，后面细讲）。最后是如何确认交易清单有效，其实也就是要确认当前每笔交易的付款人有足够的余额支付这笔钱。由于交易信息里包含这笔钱是如何来的，还包含了记录来源交易的账单编号。例如，HIJKLMN要给ABCDEFG10个比特币，并注明了这10个比特币来自之前OPQRST支付给HIJKLMN的一笔交易，确认时首先要确认之前这笔交易是否存在，同时还要检查HIJKLMN在这之前没有将这10个比特币支付给别人。这一切确认后，这笔交易有效性就被确认了。其中第一笔是系统奖励给生成这页账簿的小组的50个，这笔交易大家都默认承认，后面的只要按照上述方法追溯，就可以确认HIJKLMN是否当前真有10个比特币支付给ABCDEFG。如果完成了所有了上述验证并全部通过，这个小组就认可了上述账簿纸有效，然后将这张账簿纸并入小组的主账簿，舍弃目前正在进行的工作，后面的挖矿工作会基于这本更新后的主账本进行。账簿确认反馈对于挖矿小组来说，当账簿纸送出去后，如果后面有收到其他小组送来的账簿纸，其「上一页账簿纸编号」为自己之前送出去的账簿纸，那么就表示他们的工作成功被其他小组认可了，因为已经有小组基于他们的账簿纸继续工作了。此时，可以粗略的说可以认为已经得到了50个比特币。另外，任何一个小组当新生成有效账簿纸或确认了别的小组的账簿纸时，就将最新被这个小组承认的交易写到公告牌上，那么收款人只要发现相关交易被各个小组认可了，基本就可以认为这笔钱已经到了自己的账上，后面他就可以在付款时将钱的来源指向这笔交易了。以上就是整个比特币的支付体系。下面我们来分析一下，这个体系为什么可以工作下去，以及这个体系可能面临的风险。工作机制分析虽然上面阐述了比特币的基本运作规则，但是村民们还是有不少疑问。所以中本聪同学专门开了个答疑会，解答常见问题。下面总结一下村民最集中关心的问题。核心问题答疑如果同时收到两份合法的账簿页怎么办？注意在上面的运行机制中，各个挖矿小组是并行工作的，因此完全可能出现这样的情况：某小组收到两份不一样的账簿页，它们都基于当前这个小组的主账簿的最后一页，并且内容也都完全合法，怎么办？关于这个问题，中本聪同学说，小组不应该以线性方式组织账簿，而应该以树状组织账簿，任何时刻，都以当前最长分支作为主账簿，但是保留其它分支。举个例子，某小组同时收到A、B两份账簿页，经核算都是合法的，此时小组应该将两页以分叉的形式组织起来，如下图所示：黑色表示当前账簿主干。此时，可以随便选择一个页作为当前主分支，例如选择A：此时如果有一个新的账簿页是基于A的，那么这个主干就延续下去：如果这个主干一直这么延续下去，表示大家基本都以 A 为主干，B 就会被遗忘。但是也有可能忽然 B 变成更长了：那么我们就需要将 B 分支作为当前主干，基于这个分支进行后续工作。从局部来看，虽然在某一时刻各个小组的账簿主干可能存在不一致，但大方向是一致的，那些偶尔由于不同步产生的小分支，会很快被淹没在历史中。如果挖矿小组有人伪造账簿怎么办关于这个问题，中本聪同学说，只要挖矿组织中大多数人是诚实的，这个系统就可靠，具体分几个方面给予答复。首先，基于保密印章机制，没有人能伪造他人身份进行付款，因为编码生成器在打印编码时会核对所有交易单的保密印章，印章和付款人不一致会拒绝打印。而且诚实的矿工也不会承认不合法的交易（如某笔交易付款方余额不够）。所以只有一种可能的攻击行为，即在收款人确认收款后，从另一条分支上建立另外的交易单，取消之前的付款，而将同一笔钱再次付款给另一个人（即所谓的 double-spending 问题）。下面同样用一个例子说明这个问题。先假设有一个攻击者拥有 10 个比特币，他准备将这笔钱同时支付给两名受害者 A 和 B，并都得到承认。第一步，攻击者准备从受害者 A 手里买 10 比特币的黄金，他签署交易单给受害者 A，转 10 个比特币给受害者 A。第二步，这笔交易在最新的账簿页中被确认，并被各个挖矿小组公告出来。受害人 A看到公告，确认比特币到账，给了攻击者10个比特币等值的黄金。第三步，攻击者找到账簿，从包含刚才交易的账簿页的前一页做出一个分支，生成更多的账单页，超过刚才的分支。由于此时刚才攻击者制造的分支变成了主干分支，而包含受害者A得到钱的分支变成了旁支，因此挖矿组织不再承认刚才的转账，受害者A得到的10比特币被取消了。第四步，攻击者可以再次签署交易单，将同一笔钱支付给受害者B。受害者B确认钱到账后，支付给攻击者等值黄金。 至此，攻击者将10个比特币花了两次，从两名受害者那里各购得等值黄金。攻击者还可以如法炮制，取消与受害者B的转账，将同一笔钱再支付给其他人……关于这种攻击，中本聪给出的解决方案是，建议收款人不要在公告挂出时立即确认交易完成，而是应该再看一段时间，等待各个挖矿小组再挂出6张确认账簿，并且之前的账簿没有被取消，才确认钱已到账。中本聪解释道，之前设定变态的编号规则，正是为了防御这一点。根据前面所述，生成有效账簿页不是那么简单的，要花费大量的人力反复试不同的幸运数字，而且过程完全是碰运气。如果某账簿页包含你收到钱的确认，并且在后面又延续了6个，那么攻击者想要在落后6页的情况下从另一个分支赶超当前主分支是非常困难的，除非攻击者拥有非常多的人力，超过其他所有诚实矿工的人力之和。而且，如果攻击者有如此多人力，与其花这么大力气搞这种攻击，还不如做良民挖矿来的收益大。这就从动机上杜绝了攻击的形成。比特币会一直增加下去，岂不是会严重通货膨胀中本聪说，这一点我也想到了。前面忘了说了，我给矿工组织的操作细则手册会说明，刚开始我们协议每生成一页账簿，奖励小组50个比特币，后面，每当账簿增加21,000页，奖励就减半，例如当达到210,000页后，每生成一页账簿奖励25个比特币，420,000页后，每生成一页奖励12.5个，依次类推，等账簿达到6,930,000页后，新生成账簿页就没有奖励了。此时比特币全量约为21,000,000个，这就是比特币的总量，所以不会无限增加下去。没有奖励后，就没人做矿工了，岂不是没人帮忙确认交易了到时，矿工的收益会由挖矿所得变为收取手续费。例如，你在转账时可以指定其中 1% 作为手续费支付给生成账簿页的小组，各个小组会挑选手续费高的交易单优先确认。矿工如果越来越多，比特币生成速度会变快吗不会。中本聪解释，虽然可以任意加入和退出矿工组织，导致矿工人数变化，每个矿工也会拿到一个编码生成器，不过我已经在编码生成器中加入了调控机制，当前工作的编码生成器越多，每个机器的效率就越低，保证新账簿页生成速率不变。虽然每个人的代号是匿名的，但如果泄露了某个人的代号，账簿又是公开的，岂不是他的所有账目都查出来了确实是这样的。例如你要和某人交易，必然要要到他的代号才能填写交易单。因为收款人一栏要填入那人的代号。不过中本聪说可以提供无限制的保密印章，建议每一次交易用不同的保密印章，这样查账簿就追查不到同一个人的所有账目了。答疑完毕。说明本文用通俗比喻的方式讲解了比特币的运行机制。有几点需要说明：为了便于理解，我做了很多简化，因此有些机制细节和实际的比特币可能不完全相同。但总体思想和关键原理是一致的。 由于很多计算机世界的东西（如公钥体系、网络传输）在现实世界中并没有特别好的对等物，所以故事里难免有一些生硬和不合常理的细节。 本文描述的是比特币网络本身的技术原理和运作机制，当在如 Mtgox 这种买卖市场中进行比特币交易时，市场做了中间代理，并不遵从上述机制参考Bitcoin 白皮书（英）Bitcoin 白皮书（中）Bitcoin Wiki（英）云风的 BLOG: 《Bitcoin 的基本原理》
【WEB攻防】保姆级教程---前端加密的对抗（附带靶场）
一、前言​ JS加解密是目前黑盒渗透测试中非常大的痛点, 为了满足等保需求, 越来越多行业的内部系统使用了加密技术对HTTP的请求体加密, 增加签名, 时间戳, RequestId等防止进行数据包的抓包改包. 这篇文章就把网络上一些流行的JS逆向后进行数据包的抓包改包方式来个大汇总, 希望能够帮助到大家入门， 提高黑盒渗透的水平。​ 值得注意的一点是, 本篇不太适合完全不懂代码的同学看, 因为介绍的工具和方法均需要对JS和python等代码有一定的了解, 关于JS加密这点没法一蹴而就, 只能慢慢的学习。二、环境1.环境靶场http://39.98.108.20:8085/ ​ 各位高抬贵手， 写了几个逻辑漏洞，可以自己试试，但别把服务器搞崩了2.项目地址0ctDay/encrypt-decrypt-vuls: 加解密逻辑漏洞靶场 (github.com) ​ 可以根据需求自行修改​ 说到环境本人也是下了很大的心血，毕竟项目上遇到的存在前端加解密的系统都是保密性质的， 不太好当作公开文章复现学习所使用。 在网上找了很久也没现成的，索性自己搞了一套加解密网关和一个比较基础的图书管理系统，这里的前端使用VUE进行开发， 没有网上许多简单通过Jquery实现的一套加解密逻辑漏洞的靶场那么简单。 但也没那么难， 仅仅使用了固定密钥进行AES加密的形式对请求体进行加密。​ 关于加解密也引出许多思考，第一是实现HTTP加解密是一对一实现的吗？第二是如果把数据包加密了，那安全设备是不是也失效了，这里带着几个疑问简单了解一下加解密的实现流程。这里以请求为例​ 用户输入明文消息在被JS提取后， 通过JS中定义的加密方法进行加密并制作请求包进行请求。 发送到后端后，一般来说由加解密网关进行解密后， 发送到真正提供服务的服务器上。​ 加解密网关本质其实就是一种反向代理，除了本身转发数据包的功能以外，还对数据包进行解密。​ 按照这种部署模式，有两个优势：1.如果新增分布式系统， 无需重复实现加解密功能， 直接使用作为该网关的下游服务即可。2.在springcloud gateway和 分布式系统之间部署安全设备， 可以检测明文流量识别攻击行为。三、改包的防范先汇总一下目前流行的防止改包方式主要是这么几个方面1. 请求参数和路径的加密（靶场未实现）如果原始请求是GET请求，或防止访问者获取请求路径，通常会将用户实际的请求路径和GET请求参数封装都封装为POST请求的请求体，通过加解密网关再还原为原始GET请求传入后端分布式服务上。 在APP中比较常见。表现的形式通常为： 抓包后发现访问任何功能都是同一路径，并且请求全为密文2. 请求体的加密这类在纯web中最常见， 通常仅仅加密接口请求的请求体内容，但有以下几类加密问题。①使用固定密钥 --- 顾名思义， 这种情况一般JS中会存储密钥， 属于最简单的一种②使用动态密钥 --- JS中不存储，一般用户第一次请求后将密钥加密写入COOKIE或本地存储中， 这类加密追踪难度较大。③对称加密 --- 加解密数据包内容同一套密钥④非对称加密 --- 加密一套解密一套⑤算法 --- 算法就不是特别固定了， 常见的诸如AES RSA等， 也遇到过使用国密算法或一些冷门算法。3. 签名签名的应用也十分广泛，app，小程序和现在许多web中均存在，签名的构成主要是以下几点①RequestId --- 为了防止重放攻击, 客户端生成随机RequestId 服务端接收后保存至Redis中, 如果再次接收到此RequestID, 则视为非法请求②时间戳 --- 添加时间戳的超时时间, 一旦超时, 原始数据包失效③签名本身 --- 通过 requestId + 原始请求体或请求参数 + 时间戳 + 盐值合并生成哈希值 从而保证以上参数的有效性和唯一性四、JS逆向​ JS逆向老生常谈的问题了， 需要懂得基础的JS知识，和调试方法， 这里主要就不讲解如何详细的逆向了，我们直接切入正题： 如何 解决数据包的修改 问题。​ 关于如何找到JS中的加解密的方法，很多新手会认为一定得找到 加解密函数 。其实不然, 老手都会告诉你无论是APP还是JS中可以先找到 明文点 。​ 什么是明文点？​ 在前端进行复杂的请求操作时，肯定会经过一系列从A函数-->B函数-->C函数-->D函数-->E函数之类的流程， 那么在这个流程中，假设D函数是加密函数，那么ABC函数中原始请求参数均是明文的，这就是明文点，找到明文点后再一步步调试， 其实就能顺腾摸瓜找到加密函数了。v_jstools cilame/v_jstools: 模仿着写一个 chrome 插件，用来快速调试前端 js 代码。 (github.com) 一款比较好用的工具，能够一键监测JS中指定函数的调用这里我们就以靶场举例看看实际的操作1.首先安装好v_jstools进行配置， 选择需要挂钩的函数即可，然后点击挂钩总开关。具体挂钩什么函数，需要根据具体需求， 千万不要全部都勾上，那样信息流就太多了不利于我们的筛选2. 打开挂钩功能， 打开浏览器控制台， 刷新页面3. 发送数据包，查看提示这里就可以看到， 针对请求包， 我们找到了请求的明文点4. 跟进对应的JS文件​ 在该代码处打上断点， 跟踪后续的处理​ 重新发送数据包， 并点击步过， 我们就可以看到 名字为 n 的变量就是我们的提交内容​ 此时n还是明文， 那么我们继续步过， 经过 t.data = l(n)后，data内容为密文，​ 经过实际请求包的比较， 发现t.data 即为 加密后的内容， 那么l() 函数即为加密函数​ 所以我们跟进l() 函数中看看，发现l函数即为加密函数，也顺便发现了解密函数就在下面，其中t参数为原始的内容，f参数为密钥，h为密码。​ 再回到刚刚的起始点， 也可以发现requestID算法，时间戳算法 和 签名算法5. 问题​ 当然这算是个比较简单的例子，算法和对象的跟踪的都比较直接1. 为什么不用搜索的方式​ 如果能搜索到当然好，比如这个环境可以直接通过搜索AES的方式找到上下文。​ 但现在大部分webpack项目都自带混淆，在生产环境中许多变量和字符无法直接搜索，即使搜索到了阅读上下文也看不懂代码到底是个什么意思。2. 明文点​ 上文提到明文点，在这个过程中我们跟踪的两个函数都是明文点，一个是v_jstools 提示的函数位置u.interceptors.request.use((function(t){}，还有一个是l()函数。生产情况下，我们可能无法直接分析出该函数的作用，即便看不懂，也是大有用处，这点我们后面娓娓道来。五、修改数据包找到明文点后，我们开始着手数据包的修改了，那么我们来了解一下主流的修改方式。分类1、 修改当前的数据包浏览器发包后，代理到burp上或通过其他的形式修改这个数据包的内容，主要针对当前数据包的修改场景： 分析请求参数、添加额外参数、绕过前端校验等等2、主动发包的加密与解密脱离浏览器， 主动发包并加密， 对响应的数据包解密场景 ： 自动化工具插入漏洞payload、暴力破解、重放测试等第一大类: 修改当前数据包1.青铜 -- 直接在明文点处修改这里有个账号: test 密码:123首先我们在表单处输入 test 密码 1234，很明显我们是无法登录的。这时我们进入到调试中，走到加密前的一步，直接在作用域中修改然后继续运行，即可登录成功了这个方式需要在加密方式和一些签名的步骤之前，否则可能修改无效2.白银 -- JS-forward个人认为仅仅改包的话，是一个比较万金油的办法​ 首先我们了解一下JS-forward运行原理，简单来说就是在明文点处插入一段JS代码，这段代码先通过AJAX请求将现有的请求内容发送给burpsuite，burpsuite拦截并修改内容后，返回到原始变量中，优点是操作比较统一，如果明文点正确，后续所有的改包操作都可以在burpsuite中进行​ 大概就是这样一个流程​ 所以我们来实现， 步骤有一丢丢麻烦，请耐心看完a. 找到明文点确认明文变量名，然后启动JS-forward这里我们需要将变量名向前看一点，确认明文变量就是t.data启动JS-forward输入变量名: t.data输入数据类型： json (JS原始对象也可设置为JSON)输入请求标识： REQUEST这里的请求标识仅仅作为标识使用， 没有任何意义， 主要是为了在burpsuite中区分请求包和响应包输入$end 结束后， 会监听2个端口 分别是38080， 28080， 还会生成一段JS代码我们留作后续使用。b. 插入JS代码JS-forward 的使用尽量在明文点的函数第一行插入相关代码，因为不知道后续代码会做什么样的操作。具体的插入方式b1： 找到F12--源代码--替换（覆盖）--点击选择文件夹--选择我们硬盘中一个空文件夹如果浏览器有提示点击允许b2: 在 网页--明文点JS文件处--右键--替换内容（因为只有这样才能修改JS中的代码）b3: 将JS-forward中生成的代码，复制到函数第一行，Ctrl+S保存c. 打开burpsuite关闭调试功能或关闭F12，刷新页面，再次发包时即可接收到明文信息d. 注意1.能够理解原理的话， 尽量自己思考思考。此功能与浏览器--burpsuite代理无关，浏览器的代理可不设置为burpsuite。2.另外在实际测试过程中谷歌浏览器会报CORS错误，edge正常，具体原因不明，以后有机会再分析第二大类: 主动发包的加密与解密以上方法只适合修改浏览器的提交操作后的数据包修改优点：是简单易上手，就算是复杂的加密环境，只要找到明文点，后续工作不太复杂。缺点：是无法应对主动发包的情况，比如要使用被动扫描工具，暴力破解，重放测试等需求的时候，无法自动化完成。所以我们介绍第二类的解决方案，为什么不直接介绍这个方式呢？主要还是因为主动发包的加密和解密更加复杂，需要读懂目标JS代码环境中防范改包的一些业务逻辑，如果目标的JS代码混淆和加密并不是特别厉害，还是可以一试的。​ 在这之前再来了解一款工具JS-RPC jxhczhl/JsRpc: 远程调用(rpc)浏览器方法，免去抠代码补环境 (github.com)​ 所谓RPC，翻译过来是远程调用的意思，简单来说就是搭建一个桥梁让两个不同的应用系统之间一方能主动调用另外一方的api或函数。​ 我们知道浏览器中的加解密都是通过JS实现的，但如果想脱离浏览器在本地运行JS代码最大一个问题就是 如何调用浏览器的api 。举个例子: 比如我们想在python中执行JS中的解密函数，我们通常是两个方法：1.读懂JS加密函数的内容，在python中通过python代码使用同样的逻辑来实现。2.通过execJs selenium等框架执行指定的JS代码，理想状态是好的。但是，如果目标环境的加密很复杂，又伴随着一些复杂的对象操作，需要解析各种变量以及补环境来满足函数调用，意味着可能我们还没开始渗透就已经脱了几层皮了。​ 所以我们可以使用这款工具提高渗透测试前期的效率。​ JS-RPC这款工具的工作原理就是在控制台中执行一段代码，通过websocket与本地的python服务端相连。这样一来如果python中想要执行代码，只需要通过RPC即可调用控制台中的函数了，不需要再本地还原。​ 我们来看看实现步骤1. 先打开客户端，然后打开控制台，将JSrpc的注入代码输入先输入函数内容function Hlclient(wsURL) { this.wsURL = wsURL; this.handlers = { _execjs: function (resolve, param) { var res = eval(param) if (!res) { resolve("没有返回值") } else { resolve(res) } } }; this.socket = undefined; if (!wsURL) { throw new Error('wsURL can not be empty!!') } this.connect() } Hlclient.prototype.connect = function () { console.log('begin of connect to wsURL: ' + this.wsURL); var _this = this; try { this.socket = new WebSocket(this.wsURL); this.socket.onmessage = function (e) { _this.handlerRequest(e.data) } } catch (e) { console.log("connection failed,reconnect after 10s"); setTimeout(function () { _this.connect() }, 10000) } this.socket.onclose = function () { console.log('rpc已关闭'); setTimeout(function () { _this.connect() }, 10000) } this.socket.addEventListener('open', (event) => { console.log("rpc连接成功"); }); this.socket.addEventListener('error', (event) => { console.error('rpc连接出错,请检查是否打开服务端:', event.error); }); }; Hlclient.prototype.send = function (msg) { this.socket.send(msg) } Hlclient.prototype.regAction = function (func_name, func) { if (typeof func_name !== 'string') { throw new Error("an func_name must be string"); } if (typeof func !== 'function') { throw new Error("must be function"); } console.log("register func_name: " + func_name); this.handlers[func_name] = func; return true } //收到消息后这里处理， Hlclient.prototype.handlerRequest = function (requestJson) { var _this = this; try { var result = JSON.parse(requestJson) } catch (error) { console.log("catch error", requestJson); result = transjson(requestJson) } //console.log(result) if (!result['action']) { this.sendResult('', 'need request param {action}'); return } var action = result["action"] var theHandler = this.handlers[action]; if (!theHandler) { this.sendResult(action, 'action not found'); return } try { if (!result["param"]) { theHandler(function (response) { _this.sendResult(action, response); }) return } var param = result["param"] try { param = JSON.parse(param) } catch (e) {} theHandler(function (response) { _this.sendResult(action, response); }, param) } catch (e) { console.log("error: " + e); _this.sendResult(action, e); } } Hlclient.prototype.sendResult = function (action, e) { if (typeof e === 'object' && e !== null) { try { e = JSON.stringify(e) } catch (v) { console.log(v)//不是json无需操作 } } this.send(action + atob("aGxeX14") + e); } function transjson(formdata) { var regex = /"action":(?<actionName>.*?),/g var actionName = regex.exec(formdata).groups.actionName stringfystring = formdata.match(/{..data..:.*..\w+..:\s...*?..}/g).pop() stringfystring = stringfystring.replace(/\\"/g, '"') paramstring = JSON.parse(stringfystring) tens = `{"action":` + actionName + `,"param":{}}` tjson = JSON.parse(tens) tjson.param = paramstring return tjson }然后再输入var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");其中变量名demo, 和group的值可以自己定2. 记录加密函数首先还是调试到加密那一步这里我们就知道了, 加密函数为l()在控制台中输入window.enc = l, 控制台会显示当前函数信息, 并保存非形参的参数, 注册成功后我们可以主动调用enc()函数, 查看是否有效window.enc() = l #测试 enc("123")3. 向JsRPC中注册这些函数#有参 demo.regAction("enc", function (resolve, param) { var res = enc(String(param)); resolve(res); })4. 测试调用发送get请求, 或post请求到本机的12080端口, 即可获取调用结果http://127.0.0.1:12080/go?group=zzz&action=enc&param=123懂得原理后, 我们可以继续进行操作了黄金 -- JS-RPC + MITM​ 目前比较流行的一个解决方案, 通过 mitm 将原始请求发送到JS-RPC中进行加密后修改原始数据包内容, 再进行发包mitmproxy 为一款代理工具, 你可以把他理解为python版的burpsuite, 可以进行拦截,改包等操作, 所以我们的思路是这样:接下来就到实际应用的阶段了：​ 针对目前的靶场, 我们需要分析一下JS的代码。几个关键的变量和函数：r： 很明显就是时间戳。n： 原始的表单数据请求经过v() 函数处理后， 再进行JSON编码。i： 使用p函数生成的requestId。s： 使用MD5()函数生成的哈希值， 生成的方式为n+i+r的字符串拼接。加密： 对变量n使用l()函数进行加密。针对实际请求包的修改：我们需要在请求头中 添加 timestamp，requestId, sign 等字段 。然后 修改明文请求体进行加密 。接下来就是实现：1. 启动JS-rpc, 并注入代码2. 打上断点并调试, 记录函数, 并注册记录//时间戳 window.time = Date.parse //requestId window.id = p //v函数 window.v1 = v //签名 window.m = a.a.MD5 //加密 window.enc = l注册//md5函数 demo.regAction("req", function (resolve,param) { //请求头 let timestamp = time(new Date()); let requestid = id(); let v_data = JSON.stringify(v1(param)); let sign = m(v_data + requestid + timestamp).toString(); //加密请求体 let encstr = enc(v_data); let res = { "timestamp":timestamp, "requestid":requestid, "encstr":encstr, "sign":sign }; resolve(res); })测试这样我们就可以一次性获取所有请求的需求了3. 构建MITM​ 之前介绍过Mitmproxy , 就是python版的burpsuite, 所以我们只需要知道核心的代码逻辑: 即提取原始请求体后, 向请求头中 添加requestId, timestamp, sign字段 并且 替换原始请求体为加密后的内容 就OK了, 直接Chatgpt生成​ 代码:import json import time import hashlib import uuid from mitmproxy import http import requests import requests def request(flow: http.HTTPFlow) -> None: if flow.request.pretty_url.startswith("http://39.98.108.20:8085/api/"): # 提取原始请求体 original_body = flow.request.content.decode('utf-8') data = {"group": "zzz", "action": "req", "param": original_body} res = requests.post("http://127.0.0.1:12080/go",data=data) res_json = json.loads(res.text)["data"] data_json = json.loads(res_json) print(data_json) # 对请求体进行加密处理（这里假设加密方法是简单的哈希） encrypted_body = data_json["encstr"] # 替换请求体 flow.request.text = encrypted_body # 生成 requestId，sign 和 timestamp request_id = data_json["requestid"] timestamp = data_json["timestamp"] sign = data_json["sign"] # 添加或替换请求头 flow.request.headers["requestId"] = request_id flow.request.headers["timestamp"] = str(timestamp) flow.request.headers["sign"] = sign # 运行 mitmproxy 时加载这个脚本：mitmproxy -s your_script.py 例: mitmproxy -p 8083 -s mitm.py将代码运行起来后, burpsuite 的upstream 设为 mitm的监听端口4. 测试在burpsuite中发送明文数据包, 在经过mitm处理后, 自动加密, 此时服务端再不会报错了钻石 -- JS-RPC + YAKIT 热加载​ 在刚刚的例子里面, 我们虽然可以实现加解密, 但是毕竟数据包拐了山路十八弯, 难免优点麻烦。 有没有少拐点弯的方法呢？ 当然有啦， yakit作为国内优秀的渗透一体化工具，现在的在渗透中的使用率越来越高，相信随着国产化的普及，以后会更加流行。 还不会使用yakit的同学真的可以好好学习一下， 有的功能挺好用的。 在yakit中有一个模块叫做web fuzzer，有点像burpsuite中 repeater 和 intruder的结合体， 提供了数据包的重放和fuzz功能。热加载​ 通过web fuzzer自带热加载功能， 通过官方对热加载的描述， 我们可以构建一段代码，在发送后自动加密， 这样就省去mitm的使用了。yak官网对这个热加载功能解释并不是特别详细，仅仅提到了热加载中自带了两个魔术方法， 分别对请求和响应自动做处理八、热加载 | Yak Program Language (yaklang.com) 1. 原理通过研究， 可以详细解释解释：请求包处理： 实现beforeRequest..方法即可， 其中行参”req“为一个字节数组， 保存了完整的请求内容字节。那么我们通过yak官方的poc库（实际就是HTTP库），提供的方法，可以做如下操作：//获取请求体 requestBody = poc.GetHTTPPacketBody(req) //修改请求包中指定的请求头 req = poc.ReplaceHTTPPacketHeader(req, "请求头名", "请求头值") //修改请求体 req = poc.ReplaceHTTPPacketBody(req, "修改后的值")2. 实现首先我们需要准备好, 解密后的请求体, 可以直接把之前提到的变量n的值拿过来完整热加载内容（JsRPC沿用上面的内容）// 定义加密函数 func getEnc(data){ rsp,rep,err = poc.Post("http://127.0.0.1:12080/go",poc.replaceBody("group=zzz&action=req&param="+data, false),poc.appendHeader("content-type", "application/x-www-form-urlencoded")) if(err){ return(err) } return json.loads(rsp.GetBody())["data"] } // beforeRequest 允许发送数据包前再做一次处理，定义为 func(origin []byte) []byte beforeRequest = func(req) { //获取请求体 req_body = poc.GetHTTPPacketBody(req) //加密 res = getEnc(string(req_body)) //获取其他的参数 res = json.loads(res) //修改其他的请求头 req = poc.ReplaceHTTPPacketHeader(req, "requestId", res["requestid"]) req = poc.ReplaceHTTPPacketHeader(req, "timestamp", res["timestamp"]) req = poc.ReplaceHTTPPacketHeader(req, "sign", res["sign"]) //修改请求体 req = poc.ReplaceHTTPPacketBody(req, res["encstr"]) return []byte(req) } // afterRequest 允许对每一个请求的响应做处理，定义为 func(origin []byte) []byte afterRequest = func(rsp) { return []byte(rsp) } // mirrorHTTPFlow 允许对每一个请求的响应做处理，定义为 func(req []byte, rsp []byte, params map[string]any) map[string]any // 返回值回作为下一个请求的参数，或者提取的数据，如果你需要解密响应内容，在这里操作是最合适的 mirrorHTTPFlow = func(req, rsp, params) { return params }最后通过fuzz功能测试暴力破解，爆破成功​ 当然在这个过程中也并不是一帆风顺， 最大的一个问题还是yakit官方文档对热加载功能和poc库的解释还不够详细，导致很多功能都是靠猜（小小的吐槽一下）。但是该有的功能其实都有，工具本身还是很强大的。yakit解密​ 关于解密，其实和加密一样，如果懂得前面的逻辑，完全可以实现自动化，这里就不具体介绍了。​ 但除此之外，我们还可以使用yak中新增插件的方式来手动解密数据。原理也非常简单，我们来看看：1. 还是在控制台中记录和注册解密函数//记录：这里解密函数就是d函数 window.dec = d //注册 demo.regAction("decrypt", function (resolve,param) { resolve(dec(param)) })2. 插件--本地--新建插件类型选择codec插件， 勾选右键执行3. 编辑内容--保存handle = func(origin /*string*/) { //JSrpc的group group = "zzz"; //jsrpc的action action = "decrypt"; rsp,rep = poc.Post("http://127.0.0.1:12080/go",poc.replaceBody("group="+group+"&action="+action+"&param="+codec.EncodeUrl(origin), false),poc.appendHeader("content-type", "application/x-www-form-urlencoded"))~ return json.loads(rsp.GetBody())["data"]; }可以做个测试4. 在web_fuzzer中使用当然肯定有人问， burpsuite怎么办呢？当然也有办法，比如autodecoder插件就可以完成，但实现比这个还是要麻烦得多。最强王者 -- JS原生​ 相信看到这里的，一定是努力学习的你，所以这一段也是结语啦。​ JS解密的最终形态是什么呢？爬虫高手会告诉你，当然是用JS解密JS啊，通过反混淆--分析代码逻辑--研究参数作用--添加补环境--最终通过本地运行JS来加解密。但是我们之前也讨论过，等这一套操作搞完，可能渗透测试项目都快结束了， 所以为了速度，我们只能用魔法打败魔法。​ 那么yakit近期的版本更新中也提到使用了更为强大的JS库GoJa，这是相关api的文章​ 上新！更强大的JS引擎：goja (qq.com) ​ 也就是说，如果条件允许，完全可以脱离JSrpc直接去运行加解密，签名等逻辑。​ 至于在这个靶场怎么实现呢？ 当作部置作业了，毕竟这个加解密逻辑已经相当简单了，相信你一定可以独立完成！​ 另外靶场 还存在几个逻辑漏洞 ，可以在完成加解密的基础上，把他们都找出来！
理论上安卓设备都能刷，只要能解锁 Bootloader大致思路：解锁 Bootloader > 修补 boot.img > fastboot 刷入修补文件小米手机/红米手机/HyperOS/MIUI通用教程{message type="warning" content="内核的安卓版本Android 13 以上例如内核版本 5.15.XX – 6.X.XX 型号提取init_boot.img"/}{message type="warning" content="内核的安卓版本Android 13以下型号提取boot.img"/}1.解锁Bootloader{anote icon="fa-external-link" href="https://magiskcn.com/mi-unlock.html" type="info" content="参考教程"/}2.提取init_boot.img/boot.img内核安卓13以上提取init_boot.img内核安卓13以下提取boot.imgPS:（一般官改包里都有，以下教程以init_boot.img为例） {anote icon="fa-external-link" href="https://magiskcn.com/payload-dumper-go-boot.html" type="info" content="参考教程"/}3.手机插电脑文件传输模式复制init_boot.img 进手机Download目录(目录都可以，)4.手机安装Magisk的APK4.1稳定版-Stable下载地址{cloud title="Magisk-Stable-28100" type="github" url="https://github.com/topjohnwu/Magisk/releases/tag/v28.1" password=""/}{cloud title="Magisk-Stable-28100" type="lz" url="https://mrzzoxo.lanzoue.com/iACgn2hgvfmj" password=""/}4.2金丝雀-Canary下载地址{cloud title="Magisk-Canary-28103.apk" type="github" url="https://github.com/topjohnwu/Magisk/releases/download/canary-28103/app-release.apk" password=""/}5.修补文件安装 – 选择init_boot.img – 开始修补文件 – 修补完成修补生成的文件magisk_patched-28100_xxxx.img在Download目录6.使用adb工具刷入修补后的文件6.1电脑下载工具adb-fastboot（解压出来）{cloud title="adb-fastboot" type="lz" url="https://mrzzoxo.lanzoue.com/b02plghuh" password=""/}6.2把修补后的init_boot.img复制到工具目录手机插电脑，打开传输文件模式，打开Download目录，把magisk_patched-28100_xxxx.img复制到电脑adb-fastboot目录6.3手机进入fastboot模式手机关机，电源键 + 音量下键，两个键长按直至重启后显示fastboot界面7.最终刷入init_boot7.1打开打开CMD命令行.bat 输入下面的刷入命令fastboot flash init_boot 面具文件(init_boot.img)面具文件为刚才复制的文件名字全称。7.2如果是boot.img执行下面命令，fastboot flash boot 面具文件(boot.img)7.3显示Finished后再重启手机再使用指令重启手机fastboot reboot开机有震动基本没问题了，耐心等开机。（显示 Magisk 的版本，就是刷好了的）8.常见问题8.1waiting for any device{anote icon="fa-external-link" href="https://magiskcn.com/waiting-for-any-device.html" type="info" content="waiting for any device"/}8.2不开机处理办法{message type="warning" content="注意：不要把boot.img和init_boot.img弄混了"/}如果刷入init_boot.img或者boot.img不能开机，可以把前面提取的init_boot.img或者boot.img通过 fastboot 刷回去，恢复原版 boot，一般都能正常开机！init_boot.img和boot.img保留一份在电脑，避免出问题了可以自救下！还原 boot和init_boot 指令。fastboot flash init_boot init_boot.img fastboot flash boot boot.img
前言由于换了个虚拟主机，nginx环境，不是Apache新面板没有操作入口。教程一、加密的php文件头部添加引入文件搜索了下deepseek，只需要给需要加密的php文件头部添加require ('auth.php');二、auth.php代码auth.php代码<?php //在需要加密的地方添加require ('auth.php'); 这是用来开启auth认证的文件 // 设置用户名和密码 $valid_username = 'admin'; // 替换为你的用户名 $valid_password = 'password'; // 替换为你的密码 // 检查是否已经通过认证 if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] !== $valid_username || $_SERVER['PHP_AUTH_PW'] !== $valid_password) { // 发送401未授权头，触发浏览器弹出认证对话框 header('WWW-Authenticate: Basic realm="Restricted Area"'); header('HTTP/1.0 401 Unauthorized'); // 如果用户取消认证，显示错误信息 echo 'Authentication required.'; exit; } // 如果认证成功，继续加载页面内容 //echo 'Welcome, ' . htmlspecialchars($_SERVER['PHP_AUTH_USER']) . '! You have successfully authenticated.'; ?>
自己是虚拟主机，国内面板没法添加cron任务。cpanel和directadmin面板是有cron功能的.利用deepseek改造了下AutoBackup插件一、配置AutoBackup插件{message type="info" content="注意插件目录下创建backupfiles文件夹"/}二、利用免费CronJob网站定时访问{abtn icon="fa-external-link-square" color="#ff6800" href="https://nobb.cc/item/LinkJump/go.php?url=https://uptimerobot.com/" radius="" content="uptimerobot"/} 网站最大时间间隔为24小时，建议插件的时间间隔长一点。三、大功告成四、插件下载地址{hide}{cloud title="AutoBackup插件" type="lz" url="https://wwrw.lanzoue.com/iNTmU2ml6b7a" password="nobbcc"/}{/hide}
【群辉nas】部署CloudSaver-多源资源搜索神器，让你秒变资源获取大师
一、CloudSaver功能特性? 多源资源搜索支持多个资源订阅源搜索支持关键词搜索与资源链接解析支持豆瓣热门榜单展示? 网盘资源转存支持 115 网盘，夸克网盘，天翼网盘，123云盘 一键转存 支持转存文件夹展示与选择? 多用户系统 支持用户注册登录 支持管理员与普通用户权限区分? 响应式设计 支持 PC 端与移动端自适应布局 针对不同设备优化的交互体验二、安装教程1.利用filestation创建3个目录/docker/CloudSaver /docker/CloudSaver/data /docker/CloudSaver/configsudo docker pull jiangrui1994/cloudsaver:test2.注册帐号{message type="info" content="管理员：230713普通用户：9527"/}3.添加网盘三、其他设置1.添加搜索yuan{hide}[{"id":"guaguale115","name":"115影视资源分享频道"},{"id":"hao115","name":"115网盘资源分享频道"},{"id":"yunpanshare","name":"网盘资源收藏（夸克）"},{"id":"alyp_1","name":"网盘(高品质)影视"},{"id":"shareAliyun","name":"阿里云盘发布频道"},{"id":"XiangxiuNB","name":"肯德基の4K影视综合电影云盘站"},{"id":"Quark_Movies","name":"夸克云盘综合资源"},{"id":"Aliyun_4K_ Movies","name":"阿里云盘4K影视"},{"id":"zaihuayun","name":"阿里云盘资源"},{"id":"tgsearchers","name":"资源宇宙"},{"id":"pan115_share","name":"爱影115资源分享频道"},{"id":"PanjClub","name":"盘酱酱Club"},{"id":"tianyirigeng","name":"天翼云盘资源频道"},{"id":"xx123pan","name":"123云盘资源频道"},{"id":"zyzhpd123","name":"123云盘综合频道"},{"id":"cloudtianyi","name":"天翼云盘资源发布频道"},{"id":"tyypzhpd","name":"天翼云盘综合频道"},{"id":"Oscar_4Kmovies","name":"奥斯卡4K蓝光（精品）影视磁力站"},{"id":"ydypzyfx","name":"移动云盘资源分享"},{"id":"bdwpzhpd","name":"百度网盘综合频道"},{"id":"baidudianshi","name":"可乐小屋"},{"id":"yunpanall","name":"综合频道"},{"id":"NewQuark","name":"夸克浏览器二三事"},{"id":"NewAliPan","name":"阿里云盘吧（新）"},{"id":"Q66share","name":"阿里云盘吧"},{"id":"BaiduCloudDisk","name":"百度网盘资源分享"},{"id":"yunpan139","name":"网盘资源收藏（移动云盘）"},{"id":"yunpanuc","name":"网盘资源收藏（UC网盘）"},{"id":"pan123pan","name":"网盘资源收藏（123云盘）"},{"id":"yunpanpan","name":"云盘盘"},{"id":"qixingzhenren","name":"云盘资源发布频道"},{"id":"pankuake_share","name":"爱影夸克频道"},{"id":"duanjucabian","name":"热门短剧/擦边短剧/精选短剧/在线预览"},{"id":"yoyokuakeduanju","name":"YOYO资源|夸克|短剧"},{"id":"Channel_Shares_115","name":"Shares_115_Channel"},{"id":"alyp_1","name":"网盘(高品质)影视"},{"id":"yeqingjie_GJG666","name":"爷青回动画分享"}]{/hide}2.官方项目地址{cloud title="CloudSaver" type="github" url="https://github.com/jiangrui1994/CloudSaver" password=""/}3.官方说明me16{hide}{cloud title="官方说明" type="default" url="https://www.yuque.com/xiaoruihenbangde/ggogn3/ga6gaaiy5fsyw62l" password="me16"/}{/hide}四、视频教程{dplayer src="https://bdcache1-f1.v3mh.com/video/25-05-03/68158ddc52a33.mp4"/}