ROP without returns on Intel X86

論文:Return-Oriented Programming without Returns

X86上的ULB

舉例:
pop %eax;
jump *%eax
其中钢拧,X是任何通用寄存器澈驼。由于x86 ISA的臨時(shí)特性有送,pop-jump序列作為非預(yù)期的指令以某種頻率出現(xiàn)行拢。

  • 用另一個(gè)通用寄存器代替eax(esp除外)
  • 使用雙重間接跳轉(zhuǎn)而不是單獨(dú)間接跳轉(zhuǎn)
  • 使用具有立即偏移的雙重間接跳轉(zhuǎn)旭愧,通過(guò)添加或減去序列地址來(lái)抵消立即偏移的影響
  • X86提供了兩種雙重間接跳轉(zhuǎn):near jump(在當(dāng)前段中采用32位地址)和far jump(采用32位地址和16位段選擇器)

其他可能的ULB指令序列:

  • 基于調(diào)用X的序列辽狈,會(huì)在每次使用時(shí)減少esp
  • 使用與esp不同的寄存器
    例如
    add 0x4,%eax;
    jmp(%eax);
  • 使用SIB尋址,可使用寄存器組合秀鞭,索引寄存器在每次引用后加4
  • 內(nèi)存位置作為可變狀態(tài)而非寄存器

由于存在多種多樣的可能構(gòu)造ROP攻擊的指令序列趋观,進(jìn)行不全面檢測(cè)的防御方式效果明顯有限。攻擊者很可能切換到不同的返回類序列從而逃避檢測(cè)锋边。

選擇Gadgets

來(lái)源:
Debian的libc本身
Mozilla的libxul
PHP的libphp5

編譯器是否可以修改,從而避免發(fā)出pop-jump序列编曼?
不能豆巨。

使用以jmp y結(jié)尾的指令序列,其中y是指向pop x的指針掐场。pop x;jump x不在libc中往扔,就必須存在于目標(biāo)程序或它的一個(gè)庫(kù)中。我們稱其為BYOPJ范例(bring your own pop-jump)熊户。

libc在每個(gè)Linux程序中都會(huì)被加載萍膛,我們可以使用它作為gadgets的語(yǔ)料庫(kù)。但很大程度地使用libc中的指令序列而不使用libxul中的pop-jump序列不是一個(gè)真正的攻擊者會(huì)使用的攻擊方法嚷堡。

大多可用的指令序列以近間接跳轉(zhuǎn)或遠(yuǎn)間接跳轉(zhuǎn)結(jié)束蝗罗,跳轉(zhuǎn)的地址在內(nèi)存中被存儲(chǔ)在存儲(chǔ)器edx中。即很多可用的指令序列是以 jmp *(%edx) 或 ljmp *(%edx) 結(jié)束蝌戒。為了將這些gadgets鏈接起來(lái)串塑,我們要確保每個(gè)gadget的結(jié)尾都能夠保存 pop *X; jmp *X 序列目錄條目的地址。大多情況下北苟,我們只需要修改函數(shù)調(diào)用gadget即可桩匪。

本文的設(shè)計(jì):
三地址碼的內(nèi)存-內(nèi)存gadget集。格式為:

x<—y op z
其中x友鼻,y和z處于內(nèi)存中隨意位置傻昙,包含了操作數(shù)和目標(biāo)地址。

我們?cè)O(shè)計(jì)了一個(gè)三地址代碼內(nèi)存 - 內(nèi)存小工具集:我們的小工具的形式是x yopz彩扔,其中x妆档,y和z是內(nèi)存中包含操作數(shù)和目標(biāo)的文字位置。 我們使用寄存器edx來(lái)鏈接我們的指令序列; 對(duì)于我們BYOPJ范例中的pop xjmp x序列借杰,我們使用寄存器ebx过吻。 這意味著我們不能在寄存器ebx中存儲(chǔ)任何狀態(tài),但我們不必?fù)?dān)心在指令序列過(guò)程中更改其內(nèi)容,因?yàn)樗鼘⒃趐op%ebx期間被覆蓋纤虽。 這給我們留下了五個(gè)寄存器乳绕,eax,ecx逼纸,ebp洋措,esi和edi,可以隨意使用杰刽。

指令序列

我們使用了34個(gè)不同的以jmp x結(jié)尾的指令序列菠发,來(lái)構(gòu)造19個(gè)通用的gadgets:
load immediate,
move
load
store
add
add immediate
subtract
negate
and
and immediate
or
or immediate
xor
xor immediate
complement
branch uncon-ditional
branch conditional
set less than
function call
這些指令序列大多包含四個(gè)或更少的指令。這些序列是從libc的潛在指令序列集合(由Shacham的算法提供)中選出的贺嫂。

數(shù)據(jù)的移動(dòng)

gadgets需要能夠在存儲(chǔ)器和寄存器之間以及多個(gè)寄存器之間移動(dòng)數(shù)據(jù)滓鸠。方法如下:

  1. 將數(shù)據(jù)從存儲(chǔ)器移入寄存器:mov n(x),y??
  2. 將數(shù)據(jù)從寄存器移入存儲(chǔ)器:mov y第喳,n(x)
  3. 寄存器之間的移動(dòng)有兩種方法:
    a) 使用xchg指令交換兩個(gè)寄存器的內(nèi)容
    b) 將目的地址寄存器設(shè)置為0x00000000或0xffffffff糜俗,可以對(duì)源地址寄存器和目的地址寄存器進(jìn)行or或and運(yùn)算,從而實(shí)現(xiàn)移動(dòng)

將固定地址處的值加載到內(nèi)存中:
加載具有立即值的esi和具有固定地址的eax加上0xb來(lái)輕松實(shí)現(xiàn)曲饱。這需要兩個(gè)pop操作悠抹。然后我們使用mov%esi,-0xb(%eax)將立即值寫入內(nèi)存扩淀。

Move Gadget:
將源地址加載到eax楔敌,將目標(biāo)地址加載到ebp,從eax加載到edi驻谆,最后將edi存儲(chǔ)到ebp中的地址的內(nèi)存中卵凑。

Load Gadget:
不將內(nèi)存中源地址中的字存儲(chǔ)在目標(biāo)地址處,而是用作指向存儲(chǔ)器中另一個(gè)字的指針旺韭,該字加載到另一個(gè)寄存器中氛谜,然后存儲(chǔ)在目標(biāo)地址中。偽代碼:

eax <— source
edi <— (eax)
esi <— (edi)
eax <— destination
(eax) <— esi

Store Gadget:
執(zhí)行操作(A)B区端,其中A是目的地址處存儲(chǔ)的字值漫,B是源地址處存儲(chǔ)的字。實(shí)際上织盼,我們可以執(zhí)行操作(A + n)B杨何,其中n是字面值。這使常量數(shù)組容易索引到在內(nèi)存中位置不固定的值沥邻,其中A是數(shù)組基數(shù)危虱,n是偏移量。

算術(shù)運(yùn)算

add唐全,add immediate和subtract的gadget是比較容易實(shí)現(xiàn)的埃跷。它們將源操作數(shù)加載到寄存器中蕊玷,執(zhí)行適當(dāng)?shù)牟僮鳎缓髮⒔Y(jié)果存回內(nèi)存弥雹。 x86 ISA允許其中一個(gè)操作數(shù)成為內(nèi)存中的一個(gè)位置垃帅,這樣就不需要加載其中的一個(gè)操作數(shù)。這簡(jiǎn)化了gadgets剪勿。

Negate gadget從源地址加載數(shù)據(jù)贸诚,取雙字的補(bǔ)碼,并將其存儲(chǔ)回內(nèi)存厕吉。執(zhí)行寄存器的二進(jìn)制補(bǔ)碼的neg指令不會(huì)出現(xiàn)在jmp x指令附近;我們使用xor%esi酱固,%esi使esi為零,然后使用序列:subl -0x7D(%ebp头朱,%ecx)运悲,%esi; jmp(%ecx),從零中減去該值髓窜。 subl指令執(zhí)行操作esi esi(ebp + ecx 0x7D)扇苞。由于我們的jmp x使用ecx,我們必須用指向pop x; jmp x序列的指針地址加載它寄纵。這意味著ebp必須具有源地址加上0x7D再減去pop x; jmp x指針的地址的值

邏輯運(yùn)算

and脖苏,and immediate程拭,or,or immediate 的gadgets構(gòu)造方式與add gadget類似棍潘。即恃鞋,將操作數(shù)加載到寄存器中,執(zhí)行操作亦歉,并將結(jié)果存回內(nèi)存恤浪。唯一棘手的部分是如上所述的寄存器之間的數(shù)據(jù)移動(dòng)。

xor和xor immediate 的gadgets類似肴楷,它不是將兩個(gè)寄存器的值進(jìn)行xor然后將結(jié)果存儲(chǔ)回內(nèi)存水由,而是將第一個(gè)源字寫入目標(biāo)地址,然后使用第二個(gè)源字xor該地址赛蔫。

Complement gadget 計(jì)算源值的一個(gè)補(bǔ)碼并將其存儲(chǔ)到目標(biāo)地址中砂客。類似于negate gadget的情況,有一個(gè)不執(zhí)行補(bǔ)碼運(yùn)算的x86指令沒(méi)有出現(xiàn)在libc中的有用的指令序列中呵恢。我們完全按照nagate gadget進(jìn)行操作鞠值,除了不使用零加載esi而使用0xffffffff = -1加載它。這是因?yàn)?1-x =非x渗钉。

分支Branching

  • 絕對(duì)地址實(shí)現(xiàn)分支
  • 相對(duì)地址實(shí)現(xiàn)分支

在正常程序中彤恶,分支可以是絕對(duì)地址,也可以是相對(duì)于當(dāng)前指令的地址。 在面向返回的編程中声离,通過(guò)更改堆棧指針esp而非指令指針eip來(lái)執(zhí)行分支芒炼。 我們可以通過(guò)將堆棧中的值彈出到esp中來(lái)實(shí)現(xiàn)絕對(duì)分支。 或者抵恋,可將小工具末端的負(fù)偏移彈出到edi中焕议,然后使用序列sub %edi, %esp; ljmp(%eax)從堆棧指針中減去edi。這允許堆棧指針實(shí)現(xiàn)相對(duì)分支弧关。 這是branch unconditional gadget的基礎(chǔ)盅安。

條件分支gadget:

為了獲得Turing-complete行為,我們必須有辦法執(zhí)行條件分支世囊。 我們需要一種方法通過(guò)存儲(chǔ)在已知地址的內(nèi)容來(lái)改變堆棧指針别瞭。 如果字的值為零,那么我們不會(huì)更改堆棧指針株憾。 如果字是0xffffffff蝙寨,那么像branch unconditional 的情況一樣,減去堆棧指針的偏移量嗤瞎。 實(shí)現(xiàn)的方法是將字的值加載到寄存器中并與偏移量相與墙歪,再?gòu)亩褩V羔樦袦p去該結(jié)果。 該實(shí)現(xiàn)是and gadget 和 branch unconditional gadget的組合贝奇,即branch conditional gadget虹菲。

比較的實(shí)現(xiàn):

如果第一個(gè)值小于第二個(gè)值,則將目標(biāo)地址處的字設(shè)置為0xffffffff掉瞳。


Set Less Than Gadget

圖中給出了實(shí)現(xiàn)小于判斷的gadget實(shí)現(xiàn)毕源。字符串比較指令cmpsl比較%ds:%esi和%es:%edi指向的兩個(gè)值,如果后者大于前者陕习,便設(shè)置進(jìn)位標(biāo)志霎褐。同時(shí),寄存器esi和edi的值將根據(jù)方向標(biāo)志來(lái)增加或減少该镣。然而冻璃,這并不重要,因?yàn)槲覀冎皇潜容^一個(gè)字值拌牲。 sbb指令從esi中減去esi加上進(jìn)位標(biāo)志的值俱饿。實(shí)質(zhì)上,如果第一個(gè)源值小于第二個(gè)源值塌忽,那么將設(shè)??置進(jìn)位標(biāo)志拍埠,并將esi設(shè)置為0xffffffff;否則土居,將不設(shè)置進(jìn)位標(biāo)志枣购,esi將設(shè)置為零嬉探,正如branch conditional gadget所需。必須要注意的一件事是:寄存器cl不能為零棉圈,否則會(huì)發(fā)生除零異常涩堤。

利用Set Less Than Gadgets和Logical Gadgets,可以形成基于比較兩個(gè)值的六種關(guān)系(<分瘾,<=胎围,=,!=,>=和>)中的任何一種的條件分支德召。在這一點(diǎn)上白魂,我們的gadgets是圖靈完備的。

函數(shù)調(diào)用

添加一個(gè)執(zhí)行函數(shù)調(diào)用的gadget上岗,從而擴(kuò)展已擁有的gadgets集的功能福荸。
執(zhí)行函數(shù)調(diào)用的gadget能夠讓我們調(diào)用正常的面向返回的指令序列(即以return結(jié)束的指令序列),或允許我們調(diào)用合法的函數(shù)肴掷。其中敬锐,后者的操作更復(fù)雜。
這種方法可以避免兩種檢測(cè):

  • 依賴于函數(shù)調(diào)用堆棧的LIFO特性的ROP防御方法
  • 依賴于return指令頻率的防御方法

在進(jìn)行函數(shù)調(diào)用之前呆瞻,必須將堆棧指針指向新位置台夺,以防止覆蓋堆棧中的先前的gadgets。如果n是函數(shù)開(kāi)始執(zhí)行時(shí)堆棧指針?biāo)诘牡刂?- 即將存儲(chǔ)返回地址的位置 - 則k個(gè)參數(shù)應(yīng)存儲(chǔ)在地址n + 4痴脾,n + 8谒养,...,n + 4k種明郭。這可以使用load immediate gadget或move gadget來(lái)完成。接著丰泊,函數(shù)調(diào)用gadget將用于計(jì)算 A fun(arg1; arg2; ::::; argk)薯定,其中堆棧指針為n。

由于x86的Linux應(yīng)用程序二進(jìn)制接口(ABI)指定寄存器eax瞳购,ecx和edx被調(diào)用者保存话侄,因此如果被調(diào)用的函數(shù)覆蓋了這些寄存器,我們必須注意不要混淆面向返回的代碼学赛。 這種情況下年堆,一個(gè)棘手的問(wèn)題是:由于edx被調(diào)用者保存,一旦我們要執(zhí)行return盏浇,我們需要將edx恢復(fù)到指向pop x; jmp x的指針的地址变丧。 如果我們關(guān)心eax中的返回值,我們不能僅使用libc中的指令序列绢掰。 繼續(xù)我們的BYOPJ范例痒蓬,如果目標(biāo)程序有一個(gè)pop%edx; jmp(%edx)或pop%edx; jmp(%esi)童擎,那么我們就可以恢復(fù)edx而不覆蓋eax中的返回值。 Mozilla的libxul有這樣一個(gè)序列攻晒。 如果沒(méi)有這樣的序列顾复,函數(shù)調(diào)用的gadget必須針對(duì)每個(gè)應(yīng)用程序進(jìn)行定制,而不能通用鲁捏。

函數(shù)調(diào)用gadget的具體實(shí)現(xiàn)如下:

  1. 它首先做的是加載寄存器esi芯砸,ebp和eax。
    寄存器esi加載了call-jump序列的序列目錄表的開(kāi)始地址给梅,ebp加載了leave-jump序列的實(shí)際地址假丧,eax加載了字值n(加上存儲(chǔ)序列的偏移量)。
  2. 接下來(lái)破喻,將call-jump的序列目錄表的開(kāi)始地址存儲(chǔ)在地址n虎谢。
  3. 然后,寄存器esi加載0x38曹质,并加上堆棧指針的值婴噩。
    此時(shí),esi保存著函數(shù)調(diào)用返回后堆棧指針的地址羽德。
  4. 將返回地址移動(dòng)到ebp中
    既然已經(jīng)知道函數(shù)返回后在堆棧上的位置几莽,我們需要將它移動(dòng)到ebp中。
    最簡(jiǎn)單的方法是將它存儲(chǔ)到內(nèi)存中(存儲(chǔ)在我們最終存儲(chǔ)函數(shù)的返回值的地方)宅静,再將其從內(nèi)存加載回edi章蚣,然后與ebp交換。
    交換之后姨夹,edi保存著leave-jump序列的地址纤垂,ebp保存著函數(shù)調(diào)用之后堆棧指針的值。
  5. 接下來(lái)磷账,將pop x; jmp x;的序列目錄表的開(kāi)始地址加載到esi峭沦,將存儲(chǔ)函數(shù)指針的地址加載到ecx(加上偏移量),并將值n加載到eax逃糟。交換寄存器esp和eax的值吼鱼,使堆棧指針為n。
  6. pop ebp實(shí)現(xiàn)鏈接
    回想一下绰咽,函數(shù)調(diào)用gadget所做的第一件事就是將call-jump序列目錄表的開(kāi)始地址存儲(chǔ)到n菇肃。此時(shí),函數(shù)的間接調(diào)用發(fā)生了取募。函數(shù)返回之后琐谤,當(dāng)eax保存返回值時(shí),我們不能依賴寄存器ecx或edx中的值矛辕。但是笑跛,edi保存了leave-jump序列的地址付魔,因此jmp %edi指令會(huì)導(dǎo)致執(zhí)行l(wèi)eave指令,將堆棧指針存入ebp - 但ebp還保存著使用第一個(gè)xchg指令時(shí)設(shè)置的地址 - 然后將堆棧頂部的值彈出到ebp中飞蹂。這導(dǎo)致pop x; jmp x序列目錄表的開(kāi)始地址(加上一個(gè)偏移量)被加載到ebp中几苍,使得后續(xù)的jmp -0x7d(%ebp)指令能夠鏈接下一個(gè)指令序列。
  7. 返回的兩種方法
    我們現(xiàn)在有兩種實(shí)現(xiàn)返回的方法陈哑。
    當(dāng)沒(méi)有pop%edx; jmp(%edx)序列時(shí)妻坝,我們使用popad; jmp(%edx),失去了返回值惊窖。這種情況下刽宪,函數(shù)調(diào)用gadget仍是完整的。
    如果我們有一個(gè)pop%edx; jmp(%edx)序列界酒,執(zhí)行它即可圣拄,然后將eax中的返回值存儲(chǔ)到內(nèi)存中。
    后一種形式的gadget如圖毁欣。


    function call gadget
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庇谆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子凭疮,更是在濱河造成了極大的恐慌饭耳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件执解,死亡現(xiàn)場(chǎng)離奇詭異寞肖,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)衰腌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門新蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人右蕊,你說(shuō)我怎么就攤上這事栅葡。” “怎么了尤泽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)规脸。 經(jīng)常有香客問(wèn)我坯约,道長(zhǎng),這世上最難降的妖魔是什么莫鸭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任闹丐,我火速辦了婚禮,結(jié)果婚禮上被因,老公的妹妹穿的比我還像新娘卿拴。我一直安慰自己衫仑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布堕花。 她就那樣靜靜地躺著文狱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缘挽。 梳的紋絲不亂的頭發(fā)上瞄崇,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音壕曼,去河邊找鬼苏研。 笑死,一個(gè)胖子當(dāng)著我的面吹牛腮郊,可吹牛的內(nèi)容都是我干的摹蘑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼轧飞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼衅鹿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起踪少,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤塘安,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后援奢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兼犯,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年集漾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了切黔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片期丰。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焕妙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛋铆,到底是詐尸還是另有隱情驱显,我是刑警寧澤诗芜,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站埃疫,受9級(jí)特大地震影響伏恐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜栓霜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一翠桦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胳蛮,春花似錦销凑、人聲如沸丛晌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澎蛛。三九已至,卻和暖如春孟岛,著一層夾襖步出監(jiān)牢的瞬間瓶竭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工渠羞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斤贰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓次询,卻偏偏與公主長(zhǎng)得像荧恍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屯吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 原文地址:C語(yǔ)言函數(shù)調(diào)用棧(一)C語(yǔ)言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過(guò)程可看作連續(xù)的函數(shù)調(diào)用送巡。當(dāng)一個(gè)函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,591評(píng)論 1 19
  • Return-Oriented-Programming(ROP FTW) Author: Saif El-Sher...
    RealSys閱讀 3,309評(píng)論 0 2
  • 計(jì)算機(jī)系統(tǒng)漫游 代碼從文本到可執(zhí)行文件的過(guò)程(c語(yǔ)言示例):預(yù)處理階段,處理 #inlcude 盒卸, #defin...
    willdimagine閱讀 3,570評(píng)論 0 5
  • 李大牛是喊著秦珍珍的名字咽下那最后一口氣的骗爆。 “珍珍,等我······” 李華華拉著他的手蔽介,半邊屁股挨著床沿摘投,像怕...
    卷餅兔閱讀 459評(píng)論 3 2
  • 接觸 Tower.im 是很偶然的機(jī)會(huì),試玩了一天聊下感受虹蓄。 Tower.im是一款基于云端的項(xiàng)目管理工具犀呼,便于團(tuán)...
    Nesier無(wú)恙閱讀 6,322評(píng)論 2 51