自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

實(shí)踐API鉤子攔截DLL庫(kù)調(diào)用

安全 應(yīng)用安全
在日常分析使用某個(gè)軟件的過(guò)程中,如果我們想要去挖掘軟件的漏洞、或者是通過(guò)打補(bǔ)丁的方式給軟件增添一些新的功能,抑或是為了記錄下軟件運(yùn)行過(guò)程中被調(diào)用的函數(shù)及其參數(shù),有時(shí)候我們需要劫持對(duì)某些DLL庫(kù)的調(diào)用過(guò)程。

[[177879]]

前言

在日常分析使用某個(gè)軟件的過(guò)程中,如果我們想要去挖掘軟件的漏洞、或者是通過(guò)打補(bǔ)丁的方式給軟件增添一些新的功能,抑或是為了記錄下軟件運(yùn)行過(guò)程中被調(diào)用的函數(shù)及其參數(shù),有時(shí)候我們需要劫持對(duì)某些DLL庫(kù)的調(diào)用過(guò)程。在一般情況下,如果我們是軟件的開(kāi)發(fā)者或者該軟件提供源碼下載,那么剛才提到的問(wèn)題只要對(duì)源碼進(jìn)行一定的修改就可以了,簡(jiǎn)直是小菜一碟。但是在更多情況下,我們無(wú)從獲取軟件或是庫(kù)的源碼,因?yàn)樗麄兏緵](méi)有采用源碼發(fā)行的方式。那這樣我們是否就一籌莫展了呢?通過(guò)閱讀這篇文章,我會(huì)告訴你最流行的“API鉤子”方法是什么,并且會(huì)以略微不同的方式展現(xiàn)給大家。

API鉤子

正如上文我們已經(jīng)提到的,劫持DLL最流行的方法被稱作“API鉤子”——一種將庫(kù)函數(shù)調(diào)用重定向到你的代碼的技術(shù)。最為流行的API鉤子庫(kù)非微軟的 Microsoft Detours (常用于游戲破解)莫屬,并且這個(gè)商業(yè)庫(kù)被打上的價(jià)值標(biāo)簽已經(jīng)高達(dá)9999.95美元(約68999元人民幣)。再舉一個(gè)例子,在Dephi語(yǔ)言中有一個(gè)庫(kù)叫做 madCodeHook,他的商業(yè)價(jià)值約為349歐元(約2564元人民幣)。

下面就讓我們來(lái)看一看API鉤子的具體實(shí)現(xiàn)原理。

對(duì)于已經(jīng)加載的DLL庫(kù)及對(duì)應(yīng)函數(shù),通過(guò)在想要鉤取的函數(shù)頭部首字節(jié)打上一個(gè)補(bǔ)丁(也叫重寫,個(gè)人認(rèn)為叫覆蓋最為貼切),補(bǔ)丁內(nèi)容為一個(gè)JMP指令,像是 JMP NEAR 這樣的形式,轉(zhuǎn)換成16進(jìn)制就是 E9 xx xx xx xx。如下圖所示:

圖1:被鉤取的函數(shù)前后內(nèi)容示意

當(dāng)控制權(quán)被傳遞到我們鉤取過(guò)的函數(shù)后,通常這時(shí)就可以執(zhí)行我們自己想要執(zhí)行的代碼了,執(zhí)行完畢后又會(huì)接著運(yùn)行原函數(shù)然后返回到之前從DLL庫(kù)中調(diào)用該函數(shù)的代碼位置。

API鉤子其實(shí)會(huì)導(dǎo)致一些問(wèn)題,而問(wèn)題的來(lái)源就在于編譯過(guò)的軟件結(jié)構(gòu)和它本身的代碼結(jié)構(gòu)。當(dāng)我們想要通過(guò)鉤子本身來(lái)調(diào)用原函數(shù)的時(shí)候(通常不加處理情況下會(huì)導(dǎo)致一個(gè)死循環(huán)),我們必須要?jiǎng)?chuàng)建一個(gè)特殊的代碼區(qū)塊來(lái)調(diào)用原函數(shù)代碼,這個(gè)代碼區(qū)塊有個(gè)別稱叫做“蹦床”(個(gè)人覺(jué)得在國(guó)內(nèi)更常被稱為跳板),這樣的話就不用管鉤子本身是否在要調(diào)用的函數(shù)體內(nèi)了。

另外需要說(shuō)明的是,API鉤子技術(shù)不是萬(wàn)能的,在受保護(hù)的DLL庫(kù)中幾乎不可能實(shí)現(xiàn)。說(shuō)得詳細(xì)一點(diǎn)就是,比如存在CRC校驗(yàn)保護(hù)的時(shí)候,無(wú)論是從硬盤上還是內(nèi)存中對(duì)庫(kù)DLL庫(kù)代碼的修改都是不可行的。

還有一點(diǎn)就是,經(jīng)典的API鉤子也不適用于DLL庫(kù)導(dǎo)出的“偽函數(shù)”,這里的偽函數(shù)是指導(dǎo)出的變量、類指針等等。因?yàn)樵谶@種類型的“函數(shù)”條件下我們根本不可能在原函數(shù)和我們的代碼之間建立一個(gè)經(jīng)典的代碼鉤子(事實(shí)上根本就沒(méi)有函數(shù)可鉤取)。那是不是就無(wú)可奈何了呢?上面我們提到的方法是改寫原函數(shù)代碼,而下面要介紹的第二種常見(jiàn)方法就是修改PE導(dǎo)出表。只不過(guò)這種方法的局限性很大,遠(yuǎn)不如前一種流行,而且只有很少的一部分鉤子庫(kù)支持它。

DLL轉(zhuǎn)發(fā)

一種更加有創(chuàng)意但是也更為麻煩的API鉤取方式叫做“DLL”轉(zhuǎn)發(fā),它通過(guò)Windows的內(nèi)部機(jī)制來(lái)實(shí)現(xiàn),基本原理就是轉(zhuǎn)發(fā)DLL調(diào)用至其他模塊。

DLL轉(zhuǎn)發(fā)技術(shù)基于“替換表“來(lái)實(shí)現(xiàn),所以也被稱為“DLL代理”,它可以導(dǎo)出所有的原始庫(kù)函數(shù),也可以傳遞所有對(duì)庫(kù)函數(shù)的調(diào)用——除了我們想要鉤取的那部分函數(shù)。而函數(shù)調(diào)用是被通過(guò)一些鮮為人知的Windows機(jī)制傳遞給原函數(shù)庫(kù)的,這樣我們就可以借此來(lái)調(diào)用其他庫(kù)函數(shù),裝作他們本來(lái)就是存儲(chǔ)在我們使用的API鉤子庫(kù)里一樣,但事實(shí)上這些代碼被存儲(chǔ)在其他的庫(kù)中。弄明白以上這些過(guò)程,我們也就不難得知為什么要叫做“DLL轉(zhuǎn)發(fā)”了。

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

函數(shù)調(diào)用慣例是一個(gè)低等級(jí)的用于傳遞函數(shù)參數(shù)和處理函數(shù)調(diào)用返回前的堆棧的方式。很大一部分情況下它取決于編譯時(shí)的設(shè)置,并且在大多數(shù)高級(jí)編程語(yǔ)言中可以任意選擇函數(shù)調(diào)用的方式,所以兩者任取其一均可。為了讓我們的API鉤子庫(kù)正常運(yùn)行,它的鉤取函數(shù)也必須使用和已經(jīng)被鉤取的函數(shù)相同的調(diào)用慣例。他們只有在二進(jìn)制情況下相互兼容才不會(huì)引發(fā)像堆棧破壞之類的異常。

表1. 函數(shù)調(diào)用慣例

調(diào)用慣例高度依賴于編譯器的默認(rèn)設(shè)置,比如Delphi默認(rèn)采用register調(diào)用慣例,C語(yǔ)言默認(rèn)采用cdecl調(diào)用慣例。

WinAPI函數(shù)(Windows系統(tǒng)函數(shù))默認(rèn)使用stdcall調(diào)用慣例,所以在調(diào)用之前,函數(shù)的參數(shù)都使用push指令存儲(chǔ)在棧中,然后call指令被執(zhí)行,執(zhí)行完畢后并沒(méi)有必要去修正棧指針ESP,因?yàn)樵趕tdcall調(diào)用慣例中,棧在函數(shù)返回前是自動(dòng)修正的。這里值得一提的是,一個(gè)很有趣的現(xiàn)象是WinAPI中的有些函數(shù)并不使用stdcall而是C語(yǔ)言的cdecl,cdecl并不將參數(shù)存儲(chǔ)于棧,但棧的修正會(huì)在調(diào)用完成后根據(jù)函數(shù)參數(shù)的數(shù)量被編譯器修正。舉一個(gè)例子,user32.dll中的一個(gè)函數(shù)wsprintfA()(它在C函數(shù)庫(kù)中的對(duì)應(yīng)是sprintf())就采用cdecl慣例,這種調(diào)用方式是備受推崇的,因?yàn)檫@樣除了編譯器之外沒(méi)有人知道究竟傳遞了多少個(gè)參數(shù)。

API鉤子實(shí)例

作為一個(gè)例子,我想讓它盡量簡(jiǎn)單易懂一點(diǎn),只會(huì)用到一個(gè)測(cè)試庫(kù)BlackBox.dll,它只導(dǎo)出兩個(gè)函數(shù)Sum()和Divide(),想必你已經(jīng)猜到了,第一個(gè)函數(shù)的作用是兩個(gè)數(shù)的求和,第二個(gè)函數(shù)是兩個(gè)數(shù)的除法。讓我們假設(shè)我們擁有一個(gè)完整的庫(kù)文檔,并且清楚地知道這兩個(gè)函數(shù)使用的調(diào)用慣例(假設(shè)我們有這個(gè)庫(kù)的頭文件),而且我們還知道它們各自都使用哪些參數(shù)。在其他情況下我們需要使用逆向工程來(lái)獲得這些底層信息。

代碼清單1:

  1. 6// 該函數(shù)將兩個(gè)數(shù)相加并將結(jié)果儲(chǔ)存于Result變量中 
  2. // 成功返回TRUE,失敗返回ERROR  
  3. BOOL __stdcall Sum(int Number1, int Number2, int * Result);  
  4. // 該函數(shù)將兩個(gè)數(shù)相除并將結(jié)果儲(chǔ)存于Result變量中  
  5. // 成功返回TRUE,失敗返回ERROR  
  6. BOOL __stdcall Divide(int Number1, int Number2, int * Result); 

在我們的樣例庫(kù)中,Divide()函數(shù)是有bug的,因?yàn)槿绻?就會(huì)導(dǎo)致程序崩潰(假設(shè)我們的程序并沒(méi)有做異常處理),現(xiàn)在我們的目標(biāo)就是來(lái)修補(bǔ)這個(gè)漏洞。

代理DLL

為了修補(bǔ)BlackBox.dll中的漏洞,我們接下來(lái)需要?jiǎng)?chuàng)建一個(gè)中間庫(kù),能夠使Divide()函數(shù)得以有效應(yīng)用而不出現(xiàn)除0異常。該應(yīng)用采用FASM編譯器(波蘭的mr Tomasza Grysztar 創(chuàng)建)的32位匯編器。在下面你會(huì)看到帶有精確注釋的樣例庫(kù)模板。

代碼清單2:樣例庫(kù)的開(kāi)頭

  1. -------------------------------------------------  
  2. ; DLL 輸出文件格式  
  3. ;-------------------------------------------------  
  4. format PE GUI 4.0 DLL  
  5. ; DLL 入口點(diǎn)函數(shù)名  
  6. entry DllEntryPoint  
  7. ; 導(dǎo)入的Windows函數(shù)和常數(shù)  
  8. include '%fasm%\include\win32a.inc' 

注意源代碼的開(kāi)頭,你可以在找到輸出文件的類型聲明,并且在頭文件、DLL庫(kù)的函數(shù)入口點(diǎn)也可以放置這些代碼。

代碼清單3:未初始化的數(shù)據(jù)段

  1. ;------------------------------------------------- 
  2. ; 未初始化的數(shù)據(jù)段 
  3. ;------------------------------------------------- 
  4. section '.bss' readable writeable 
  5. ; uchwyt HMODULE oryginalnej biblioteki 
  6. hLibOrgdd ? 

可執(zhí)行文件和DLL庫(kù)被分割為一個(gè)個(gè)獨(dú)立的部分,他們其中之一是未初始化的數(shù)據(jù)段,這部分并不占用硬盤的空間,僅僅擁作于記錄程序所使用的未初始化變量的整體大小信息。可執(zhí)行文件的段名稱并不重要(它被限制為最多只有8個(gè)字符),通常它會(huì)被賦以公司合同的名稱。在這個(gè)段的聲明中還會(huì)定義訪問(wèn)權(quán)限(如讀、寫、執(zhí)行),但是在FASM編譯器下.bss段的聲明還會(huì)為變量創(chuàng)建一個(gè)未初始化的段。

代碼清單4:數(shù)據(jù)段

  1. ;------------------------------------------------- 
  2. ; 初始化的數(shù)據(jù)段 
  3. ;------------------------------------------------- 
  4. section '.data' data readable writeable 
  5. ; 原始庫(kù)的名稱 
  6. szDllOrgdb 'BlackBox_org.dll',0 

因?yàn)樵紟?kù)已經(jīng)有了名稱了,所以這里我們重命名一個(gè)BlackBox_org.dll(它以ASCII形式存儲(chǔ)于源代碼中,以null結(jié)束),這個(gè)庫(kù)會(huì)在后面用到。

代碼清單5:帶有DLL入口點(diǎn)的代碼段

  1. ;------------------------------------------------- 
  2. ; 庫(kù)的代碼段 
  3. ;------------------------------------------------- 
  4. section '.text' code readable executable 
  5. ;------------------------------------------------- 
  6. ; DLL庫(kù)入口點(diǎn) (DllMain) 
  7. ;------------------------------------------------- 
  8. proc DllEntryPoint hinstDLL, fdwReason, lpvReserved 
  9. moveax,[fdwReason] 
  10. ; DLL library 加載完畢后立即傳遞事件 
  11. cmpeax,DLL_PROCESS_ATTACH 
  12. je_dll_attach 
  13. jmp_dll_exit 
  14. ; 庫(kù)已經(jīng)加載 
  15. _dll_attach: 
  16. ; 獲得原始 DLL 庫(kù)的句柄 
  17. ; 如果想要調(diào)用原始函數(shù)就會(huì)使用 
  18. pushszDllOrg 
  19. call[GetModuleHandleA] 
  20. mov[hLibOrg],eax 
  21. ; 返回 1 說(shuō)明庫(kù)初始化成功 
  22. moveax,1 
  23. _dll_exit: 
  24. ret 

代碼段包含所有庫(kù)函數(shù)和DLL入口點(diǎn)函數(shù)。這是一個(gè)特殊的函數(shù),它在庫(kù)加載以后被Windows系統(tǒng)函數(shù)調(diào)用。代碼段需要被標(biāo)記上可執(zhí)行的標(biāo)記,以此來(lái)告訴操作系統(tǒng)這段內(nèi)存區(qū)域包含可以執(zhí)行的代碼段。如果沒(méi)有這樣標(biāo)記,那么任何想從這塊內(nèi)存區(qū)域執(zhí)行代碼的行為都會(huì)以觸發(fā)CPU處理器的DEP(Data Execution Prevention)內(nèi)存保護(hù)機(jī)制而告終。在初始化函數(shù)內(nèi)部(DllMain),接收到 DLL_PROCESS_ATTACH 事件后我們將使用原始DLL庫(kù)名稱來(lái)獲得他的句柄,也就是 HMODULE (這樣之后就可以被調(diào)用了)。

代碼清單6:過(guò)度優(yōu)化保護(hù)

  1. ; 調(diào)用任何原始庫(kù) 
  2. ; BlackBox_org.dll 中的函數(shù), 沒(méi)有它FASM編譯器就會(huì) 
  3. ; 移除對(duì)庫(kù)的引用并且不會(huì)被自動(dòng)加載 
  4. calldummy 

我們自定義的庫(kù)會(huì)調(diào)用到原始庫(kù),但是如果我們一點(diǎn)引用也不放在源代碼中,F(xiàn)ASM編譯器會(huì)移除所有對(duì)它的引用(優(yōu)化)而且原始庫(kù)并不會(huì)被自動(dòng)加載,這就是為什么在ret指令后直接放了一個(gè)偽調(diào)用的緣故(這樣在任何時(shí)候都不會(huì)執(zhí)行)。

代碼清單7:有效的Divide()函數(shù)代碼

  1. ;------------------------------------------------- 
  2. ; 我們修改后能夠處理除0錯(cuò)誤的Divide() 函數(shù) 
  3. ;------------------------------------------------- 
  4. proc Divide Number1, Number2, Result 
  5. ; 檢查除數(shù)是否為0 
  6. ; 如果是的話返回ERROR代碼 
  7. movecx,[Number2] 
  8. testecx,ecx 
  9. jeDivisionError 
  10. ; 將第一個(gè)數(shù)字載入 EAX 處理器 
  11. moveax,[Number1] 
  12. ;擴(kuò)展 EDX 寄存器來(lái)處理有符號(hào)數(shù) 
  13. cdq 
  14. ; 現(xiàn)在 EDX:EAX 寄存器對(duì)可以處理64位數(shù)據(jù)了 
  15. ; EDX:EAX / ECX 除法的實(shí)現(xiàn), 除法在EDX:EAX寄存器對(duì) 
  16. ; 上實(shí)現(xiàn),就像對(duì)待64位數(shù)據(jù)一樣, 除法的結(jié)果保存在EAX 
  17. ; 寄存器中, 余數(shù)保存在EDX 寄存器中 
  18. idiv ecx 
  19. ; 檢查有效的指向結(jié)果的指針 
  20. ; 如果沒(méi)有檢測(cè)到則返回error 代碼 
  21. movedx,[Result] 
  22. testedx,edx 
  23. jeDivisionError 
  24. ; 在受保護(hù)的地址存儲(chǔ)除法的結(jié)果 
  25. mov[edx],eax 
  26. ; 以 exit code TRUE (1) 返回 
  27. moveax,1 
  28. jmpDivisionExit 
  29. ; 除法錯(cuò)誤,返回FALSE (0) 
  30. DivisionError: 
  31. sub eax,eax 
  32. DivisionExit: 
  33. ; 從除法函數(shù)中返回 
  34. ; 布爾型的exit 代碼被設(shè)置在 EAX 寄存器中 
  35. ret 
  36. endp 

修改后的Divide()函數(shù)的實(shí)現(xiàn)增添了對(duì)除0錯(cuò)誤的校驗(yàn),函數(shù)遇到錯(cuò)誤會(huì)返回錯(cuò)誤代碼FALSE,另外還額外做了對(duì)指向結(jié)果變量result的指針?lè)强諜z查,如果指針指向null也會(huì)報(bào)錯(cuò)。另外請(qǐng)注意,修改后的函數(shù)的調(diào)用慣例與原函數(shù)是完全一致的,并且在我們的這個(gè)例子中使用的是stdcall慣例,所以函數(shù)參數(shù)被傳遞到棧中,函數(shù)返回值儲(chǔ)存于EAX寄存器,棧指針也被FASM編譯器自動(dòng)修復(fù),方法是根據(jù)源代碼中的ret聲明生成ret (number_of_parameters * 4)指令。

代碼清單8:庫(kù)的導(dǎo)入表

  1. ;------------------------------------------------- 
  2. ; 我們的庫(kù)使用的函數(shù)段 
  3. ;------------------------------------------------- 
  4. section '.idata' import data readable writeable 
  5. ; 在代碼中用到的庫(kù)的列表 
  6. library kernel,'KERNEL32.DLL',\ 
  7. blackbox, 'BlackBox_org.dll' 
  8. ; KERNEL32.dll庫(kù)的函數(shù)列表 
  9. importkernel,\ 
  10. GetModuleHandleA, 'GetModuleHandleA' 
  11. ; 聲明了原始庫(kù)的用途 
  12. ; DLL 庫(kù)會(huì)被自動(dòng)加載 
  13. importblackbox,\ 
  14. dummy, 'Divide' 

FASM編譯器允許我們手動(dòng)地定義我們自己的庫(kù)調(diào)用到的庫(kù)和函數(shù),除了標(biāo)準(zhǔn)系統(tǒng)庫(kù),我們需要在這里添加一個(gè)對(duì) BlackBox.dll 的引用。多虧于此,當(dāng)Windows加載我們的鉤子庫(kù)的同時(shí)也會(huì)根據(jù)地址空間加載原始庫(kù),從而無(wú)需再手動(dòng)調(diào)用 LoadLibraryA() 函數(shù)來(lái)加載它。 在某些情況下想要使用導(dǎo)入表來(lái)加載庫(kù)甚至是強(qiáng)制性要求使用 LoadLibraryA() 的,它需要使用多線程應(yīng)用程序中TLS(Thread Local Storage)機(jī)制的動(dòng)態(tài)鏈接庫(kù)來(lái)支持。

代碼清單9:函數(shù)導(dǎo)出表

  1. ;------------------------------------------------- 
  2. ; 導(dǎo)出表段包含我們的庫(kù)中導(dǎo)出的函數(shù) 
  3. ; 這里我們也許要聲明原始庫(kù)中聲明的函數(shù) 
  4. ;------------------------------------------------- 
  5. section '.edata' export data readable 
  6. ; 導(dǎo)出函數(shù)列表及其指針 
  7. export'BlackBox.dll',\ 
  8. Sum'Sum',\ 
  9. Divide, 'Divide' 
  10. ; 轉(zhuǎn)發(fā)表名稱, 首先目的庫(kù)被存儲(chǔ) (無(wú)需.DLL擴(kuò)展) 
  11. ; 然后最終的函數(shù)名稱被存儲(chǔ) 
  12. Sum db 'BlackBox_org.Sum',0 

在這個(gè)段中我們必須聲明原始庫(kù)中的所有函數(shù),而且我們想要鉤取的函數(shù)必須在代碼中得以應(yīng)用,想要傳遞給原始庫(kù)的函數(shù)存儲(chǔ)在一個(gè)特殊的文本格式中:

DestinationDllLibrary.FunctionName

DestinationDllLibrary.#1

以此來(lái)順序?qū)牒瘮?shù)而非按照名稱的順序。該機(jī)制的所有內(nèi)部工作均交由Windows系統(tǒng)自身處理。以上為DLL轉(zhuǎn)發(fā)。

代碼清單10:重定位部分

  1. ;------------------------------------------------- 
  2. ; 重定位部分 
  3. ;------------------------------------------------- 
  4. section '.reloc' fixups data discardable 

我們的庫(kù)中最后一個(gè)段是重定位段,它保證了我們的庫(kù)能夠正常運(yùn)行。這是因?yàn)閯?dòng)態(tài)鏈接庫(kù)被加載的基地址是非常多變的,而引起這個(gè)多變性的原因在于指針使用的絕對(duì)地址和匯編器的指令使用的絕對(duì)地址必須根據(jù)當(dāng)前內(nèi)存中的基地址做出更新,而這個(gè)基地址的信息正是由編譯器在重定位段中生成的。

總結(jié)

這篇API鉤子介紹的方法可以被成功應(yīng)用于各種使用動(dòng)態(tài)鏈接庫(kù)的場(chǎng)合,較傳統(tǒng)的經(jīng)典API鉤子方法而言各有利弊,但是在我看來(lái)本文的方法為實(shí)踐打開(kāi)了更大的拓展空間,并提供了一種更加簡(jiǎn)單的改變軟件完整功能性的方法。該方法同樣可以在高級(jí)語(yǔ)言中以適當(dāng)?shù)膶?dǎo)出函數(shù)定義文件(DEF)的方式實(shí)現(xiàn)。

責(zé)任編輯:武曉燕 來(lái)源: overXsky
相關(guān)推薦

2009-08-05 16:49:42

C#中調(diào)用dll

2024-03-01 20:59:11

C#DLL開(kāi)發(fā)

2009-12-14 16:44:56

Ruby調(diào)用DLL

2024-09-25 08:04:58

2009-07-31 17:28:35

C#語(yǔ)言調(diào)用DLL

2009-08-03 12:57:27

C#調(diào)用DLL

2009-08-07 17:22:36

C#調(diào)用dll導(dǎo)出函數(shù)

2011-06-28 13:12:07

Qt 調(diào)用 DLL DLL

2009-08-05 16:41:36

C#調(diào)用VC dll

2009-08-05 09:30:39

C#調(diào)用DLL函數(shù)

2009-09-02 17:16:01

冒泡排序

2009-08-05 09:40:02

C#調(diào)用DLL函數(shù)

2009-06-17 13:19:50

Java調(diào)用DLL

2010-01-26 14:04:14

C++Builder

2011-12-02 09:20:58

JavaScript

2009-07-08 17:02:11

JDK實(shí)現(xiàn)調(diào)用攔截器

2022-01-08 20:04:20

攔截系統(tǒng)調(diào)用

2019-01-21 14:20:26

Java開(kāi)發(fā)代碼

2013-06-13 09:21:31

RESTful APIRESTfulAPI

2016-12-27 08:49:55

API設(shè)計(jì)策略
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)