2015移動安全挑戰賽(阿里&看雪主辦)全程回顧
Author: 上海交通大學密碼與電腦保安實驗室軟體安全小組GoSSIP
第一題
0x1 分析
本次比賽的第一個題目是一個APK檔案,安裝後,需要使用者輸入特定的密碼,輸入正確會顯示破解成功。該題目的APK檔案沒有太多的保護,可以直接使用各種分析工具(如jeb等)反編譯得到Java程式碼。
獲得正確註冊碼的程式碼邏輯為: 1. 從logo.png這張圖片的偏移89473處,讀取一個對映表,768位元組編碼成UTF-8,即256箇中文表 2. 從偏移91265處讀取18個位元組編碼的UTF-8(即6箇中文字元)為最終比較的密碼。然後透過輸入的字元的轉換,轉換規則就是ASCII字元編碼,去比較是否和最終密碼相等。
0x2 巧妙的解法
我們在這裡提供一種非常愉快的解法,不需要複雜的工具和分析,大家可以參見影片
開啟app後,我們使用adb logcat並加上這個app獨有的 lil 標籤過濾日誌輸出,發現app輸出日誌中有table,pw以及enPassword。隨意輸入字串如123456789,發現enPassword中有對應的中文輸出,根據輸出反饋,可以知道有如下對應關係
- 1 - 麼
- 2 - 廣
- 3 - 亡
- 4 - 門
- 5 - 義
- 6 - 之
- 7 - 屍
- 8 - 弓
- 9 - 己
透過觀察Logcat輸出可知,最終目標pw應為義弓麼丸廣之,根據上述table中的對應關係,我們可以得到最終密碼為:
581026
第二題
0x1 分析
本次比賽的第二個題目仍然是一個獨立的APK檔案,安裝後,需要使用者輸入特定的密碼,輸入正確會顯示成功。第二題APK在Java層程式碼中並沒有關鍵邏輯,將使用者輸入直接傳給native so層中securityCheck這個native method(securityCheck方法在libcrackme.so中),由native code來決定返回正確與否。
用IDA工具開啟libcrackme.so,首先看下程式的大致流程,可以看到在securityCheck這個方法呼叫前,在init_array段和JNI_Onload函式里程式都做了些處理,而在securityCheck方法的最後有一個判斷,將使用者輸入和wojiushidaan做比較。嘗試直接輸入wojiushidaan,發現密碼錯誤,因此可以猜測前面一大段邏輯的作用就是會把這個最終的字串改掉。此時的思路是隻需知道最終判斷時候這個wojiushidaan地址上的變換後的值就行了。嘗試使用IDA除錯發現一旦attach上去,整個程式就退出,想必一定是在之前的程式碼中有反除錯的程式碼。
0x2 巧妙的解法
同上一題一樣,我們提供一種非常巧妙的解法:
注意到在最終比較之前,程式使用了android_log_print函式,當我們直接執行程式時,發現這裡固定輸出了
I/yaotong ( XXX): SecurityCheck Started...
這時候我們想,是否可以直接patch這個libcrackme.so,修改列印的內容,直接利用這個函式幫我們輸出此時真正需要比較的值。
我們選擇patch的方法是直接把這個log函式往下移,因為在0x12A4地址處正好有我們需要的列印的資料地址賦值給了R2暫存器(本來是為了給後面做比較用的),因此將程式碼段從0x1284到0x129C的地方都用NOP改寫,在0x12AC的地方呼叫log函式,同時為了不影響R1的值,把0x12A0處的R1改成R3。
下面是對比patch前和patch後的圖:
參考影片給出了完整的解決過程:
透過觀察Logcat輸出可知,最終密碼為:
aiyou,bucuoo
第三題
在介紹本次比賽第三道題目之前,首先要介紹一個我們GoSSIP小組開發的基於Dalvik VM的插樁分析框架InDroid,其設計思想是直接修改AOSP上的Dalvik VM直譯器,在直譯器解釋執行Dalvik位元組碼時,插入監控的程式碼,這樣就可以獲取到所有程式執行於Dalvik上的動態資訊,如執行的指令、呼叫的方法資訊、引數返回值、各種Java物件的資料等等。InDroid只需要修改AOSP的dalvik vm部分程式碼,編譯之後,可直接將編譯生成的新libdvm.so刷入任何AOSP支援的真機裝置上(目前我們主要使用Nexus系列機型特別是Nexus4和Galaxy Nexus)。在本次比賽的第三題和第四題分析過程中,我們使用該工具進行分析,大大提高了分析效率。具體細節可以參考我們發表的CIT 2014論文DIAS: Automated Online Analysis for Android Applications
回到題目上,將第三題的APK進行反編譯後發現程式碼使用了加殼保護,對付這類加殼的APK,最方便的方法就是使用InDroid來進行動態監控,因為靜態加密的DEX一定會在執行時在Dalvik上時解密執行,這樣我們可以直接在InDroid框架裡對解釋執行過程中釋放出來的指令進行監控。在我們自己使用的工具裡,我們開發了一個動態讀取整個dex資訊的介面,執行時去讀DexFile這個結構,然後對其進行解析(解析時直接複用了Android自帶的dexdump的程式碼)。這樣,我們的插樁工具在執行程式後,能夠直接得到程式的dex資訊,同未經保護時使用dexdump後得到的結果基本一致。雖然我們得到的資訊是dalvik位元組碼,沒有直接反編譯成Java程式碼那麼友好,但由於程式不大,關鍵邏輯不多,因此對我們的分析效率影響並不大。
使用InDroid進行脫殼的演示影片:
在得到脫殼之後的dexdump結果後,我們可以對程式碼進行靜態分析。我們發現使用者的輸入會傳遞給繼承自Class timertask的Class b,被Class b的run方法處理。在run方法中,如果sendEmptyMessage方法被呼叫時的引數為0,就會導致Class c的handleMessage這個方法中得到的message的what值為0,進而導致103除0跳入異常處理中,觸發成功的提示。
繼續分析這個run方法的邏輯,可以知道使用者的輸入會被傳遞到Class e的a方法中,做個類似摩爾斯譯碼的過程(其譯碼與標準的摩爾斯電碼不太一樣),然後經過下面一系列大量的混淆用的無用處理和不可能相等的比較後,將譯碼後得到的字串送入到關鍵的判斷中去。這個判斷成功的條件比較複雜:對於譯碼後得到的字串的前兩個位元組,要求使用hashcode方法的結果等於3618,並且這兩個位元組相加等於168,才會進入後面的比較。我們窮搜尋一下符合這類輸入的字串:
#!java
for ( size_t i = 33; i < 127; ++i )
{
for ( size_t j = 33; j < 127; ++j )
{
String x =String.valueOf((char)j)+String.valueOf((char)i);
if (x.hashCode()==3618 && (i+j) == 168)
{
System.out.println(x);
System.out.println(j+i);
}
}
}
輸出為:
s5
168
也就是說只有s5滿足hashcode為3618,而相加等於168這個條件。
確定前兩個字元後,後面還有四個字元需要同Class e和Class a的Annotation值比較。因為我們做脫殼的時候直接使用了dexdump的程式碼,而dexdump即使到最新版裡也無法很好地處理Annotations:
// TODO: Annotations.
不過沒關係,我們還有動態分析工具這一利器,因為最終目的是得到getAnnotation方法的返回值,依然可以用InDroid在Dalvik執行到getAnnotation方法時監控返回值,就能得到Annotation的具體值。使用InDroid獲取具體資訊的影片如下:
最後可知,符合程式需求的字串是
s57e1p
使用程式內部的對應表,對其進行逆變換,能夠讓程式輸入成功提示的輸入應該是:
… _____ ____. . ..___ .__.
第四題
本次移動安全挑戰賽的第四題和第三題一樣,是一個包含了加殼dex的APK檔案,我們使用同解決上一題一樣的方法,用InDroid得到原始dex檔案的dexdump結果:
使用InDroid進行脫殼的演示影片:
Dex整體處理過程和上一題也類似,使用handleMessage
處理最後的判斷輸入成功與否,只有sendEmptyMessage(0)
後,觸發除以0的異常才能成功。不過這一題將使用者輸入轉成byte後,傳給一個native的方法:ali$a
的M$j
方法,另外引數還包括一個常數48和Handler。看樣子逆向native庫勢在必行了。這一題的lib資料夾下檔案和上一題是一樣的,有三個檔案,其中libmobisecy.so
其實是個zip檔案,解壓後是個classes.dex,直接反彙編後,類和方法的名字都在,只是裡面的程式碼都是
throw new RuntimeException();
而libmobisecz.so
直接就是一堆binary資料,猜測應該是執行時會被解密,透過某種方式對映到為真正的程式碼執行。因此我們的目標就是libmobisec.so
這個ELF檔案。
直接用IDA開啟libmobisec.so
,發現IDA會崩潰。用readelf發現正常的節區頭資料都被破壞了,因此應該這個so本身也被加過殼了,很多資料只有在動態執行時才會解開,所以直接使用動態的方法,先執行這個程式後,直接在記憶體中把這個so dump出來。
首先需要在輸入框中隨便輸入些資料後,點選確定,保證使用者輸入資料執行到native方法裡後再做dump。我們使用的方法是檢視maps後,使用dd命令把整個so都dump出來。
輸入命令:
[email protected]:/ # ps | grep crackme.a4
u0_a73 1935 126 512204 48276 ffffffff 400dc408 S crackme.a4
[email protected]:/ # cat /proc/1935/maps
5e0f2000-5e283000 r-xp 00000000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
5e283000-5e466000 r-xp 00000000 00:00 0
5e466000-5e467000 rwxp 00000000 00:00 0
5e467000-5e479000 rw-p 00000000 00:00 0
5e479000-5e490000 r-xp 00191000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
5e490000-5e491000 rwxp 001a8000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
5e491000-5e492000 rw-p 001a9000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
5e492000-5e493000 rwxp 001aa000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
5e493000-5e4c1000 rw-p 001ab000 103:04 741132 /data/app-lib/crackme.a4-1/libmobisec.so
使用dd命令將libmobisec.so的記憶體dump出來
[email protected]:/ # dd if=/proc/1935/mem of=/sdcard/alimsc4 skip=1578049536 ibs=1 count=3993600
dd命令使用的數字都是十進位制的,skip就是libmobisec.so
的起始地址,count是總長度。 為了讓IDA還能夠識別libmobisec裡的libc函式,我們還需要把libc也載入到IDA中,libc就直接從system/lib裡拖出來就行了。
adb pull /system/lib/libc.so ./
用IDA先開啟libc,調整好是在記憶體中的偏移即rebase program,再在load additional binary裡載入dd出來的libmobisec.so,透過maps裡的偏移後載入。接下來的任務就是在其中找到M\$j這個函式的地址。
一開始嘗試直接在dd出來的ELF檔案中找這個M\$j這個函式名,類似的名字會被處理成
Java_ali_00024a_M_00024j
類似下圖:
不過我沒找到這個M\$j
這個名字,逆過JNI庫的都知道,如果符號表裡找不到這個函式名,說明在JNI_Onload
的時候,使用RegisterNatives
函式重新將一個JNI函式對映為Native函式了。
正當一籌莫展的時候,我再次想起了InDroid系統。在Dalvik中,每個方法都是一個Method的結構體,其中當這個方法是native的時候,Method的insns這個指標會指向native方法的起始地址。因此我們修改了下InDroid,讓Dalvik在執行M\$j
這個方法前,去列印了M\$j
方法的insns
指標。這時我們得到了一個指向另一片記憶體區域的值,既不在libdvm中,也不在libmobisec中,並且這片記憶體頁被對映成了rwx,由此推斷裡面也極有可能是程式碼,我們繼而又dd出了這塊記憶體,用IDA開啟,使用ARM平臺反彙編,發現該處就一條指令,是LOAD PC到另一個地址,而這個地址恰好在libmobisec中。於是我們直接到IDA中跳到這個地址,發現正好是個壓棧指令,印證了我們的想法,此處就是M$j函式,於是在在IDA裡該地址指令處,右擊選擇create function,讓IDA識別這一段彙編指令為函式指令後,就可以透過F5檢視看反編譯的C程式碼了。
這個函式本身做了一些控制流混淆,同時還有很多字串加解密的功能函式,一些簡單的如異或操作,也被展開成與和或的組合等更長更復雜的表示式形式。另外還看到一些變形過的RC4,等等。不過因為我們已經是dump出來執行過的資料,所以必要的資料都已經解密了。如下圖:
透過檢視反編譯的C程式碼,我發現程式中是直接透過JNI方法呼叫了Java中的bh
類的方法a
(在圖2常量中也可以看到)。 再次回到dex層檢視a
方法,該方法是不斷的將輸入傳遞給不同的函式進行處理,先是cd
的a
方法,cC
的a
方法,p
的a
方法,x
的a
方法,ali$a
的M$d
方法(native),aS
的a
方法,x
的a
方法,ali$a
的M&z
方法(native),cd
的a
方法,cC
的a
方法,每一個方法都是些簡單的數學運算,編碼,以及密碼學處理等可逆的操作,結合逆向和Indroid對輸入輸出的監控,都可以輕鬆確定每個Java函式的作用,具體過程如下程式碼顯示:
invoke-static {}, LbKn;.a:()Z // [email protected]
move-result v3
invoke-static {v3}, LbKn;.b:(I)V // [email protected]
add-int/lit8 v0, v5, #int 1 // #01
invoke-static {v4, v5}, Lcd;.a:([BI)[B // [email protected]
move-result-object v1
add-int/lit8 v2, v0, #int 1 // #01
invoke-static {v1, v0}, LcC;.a:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v1, v2, #int -1 // #ff
invoke-static {v0, v2}, Lp;.a:([BI)[B // [email protected]
move-result-object v0
invoke-static {v0, v1}, Lx;.a:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v2, v1, #int -1 // #ff
invoke-static {v0, v1}, Lali$a;.M$d:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v1, v2, #int 1 // #01
invoke-static {v0, v2}, LaS;.a:([BI)[B // [email protected]
move-result-object v0
invoke-static {v0, v1}, Lx;.a:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v2, v1, #int 1 // #01
invoke-static {v0, v1}, Lali$a;.M$z:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v1, v2, #int 1 // #01
invoke-static {v0, v2}, Lcd;.a:([BI)[B // [email protected]
move-result-object v0
add-int/lit8 v2, v1, #int 1 // #01
invoke-static {v0, v1}, LcC;.a:([BI)[B // [email protected]
move-result-object v0
return-object v0
值得注意的是,其中有兩個native的方法,因為InDroid還可以監控呼叫native方法的引數以及返回值,我們發現這幾個native都沒有對輸入做複雜的處理,只有M\$d對輸入的第四個位元組做了減8的處理。
做了這些逆變換以後我們其實並沒有找到最終比較的處理,不過在解密過的資料中(圖2),不僅有之前需要呼叫的各種方法和類,還可以發現有個十分可疑的Base64的字串。
aJTCZnf6NyBPYJfbrBuLu0wOhRFbPtvqpYjiby5J81M=
並且在native的M\$z方法的反彙編程式碼中,可以看到有對這個Base64字串的長度比較,由於我們並沒有找到真正的比較函式,因此得到這個字串後,我們直接從M\$z開始向上逆推之前的變換就得到了的答案。
具體解密程式碼如下:
#!python
#!/usr/bin/env python
# encoding: utf-8
from Crypto.Cipher import AES
def Lcda(s):
return ''.join(map(lambda x: chr((ord(x) + 3) & 0xff), s))
def de_Lcda(s):
return ''.join(map(lambda x: chr((ord(x) - 3) & 0xff), s))
def LcCa(s, a):
return ''.join([chr(((ord(s[i]) ^ a) + i) & 0xff) for i in xrange(len(s))])
def de_LcCa(s, a):
return ''.join([chr(((ord(s[i]) - i) & 0xff) ^ a) for i in xrange(len(s))])
def Lpa(s):
return s[1:] + s[0]
def de_Lpa(s):
return s[-1] + s[:-1]
def Lxa(s):
return s.encode("base64")[:-1]
def de_Lxa(s):
return s.decode("base64")
def LaliaMd(s):
return s[:3] + chr((ord(s[3]) - 8) & 0xff) + s[4:]
def de_LaliaMd(s):
return s[:3] + chr((ord(s[3]) + 8) & 0xff) + s[4:]
def LaSa(s):
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
cc = AES.new("qqVJwt11yyLm7hVK1iI2aw==".decode("base64"), AES.MODE_ECB)
cipher = cc.encrypt(pad(s))
return cipher
def de_LaSa(s):
cc = AES.new("qqVJwt11yyLm7hVK1iI2aw==".decode("base64"), AES.MODE_ECB)
cipher = cc.decrypt(s)
return cipher
res = "aJTCZnf6NyBPYJfbrBuLu0wOhRFbPtvqpYjiby5J81M="
flag = de_Lcda(de_LcCa(de_Lpa(de_Lxa(de_LaliaMd(de_LaSa(de_Lxa(res))))), 49))
print flag
結果為:
alilaba2345ba
這裡還需要提一下如何尋找M\$d
和M\$z
兩個函式在so庫中的地址的方法,不過這個方法是一些經驗的總結,原因是整個native ELF檔案的節區結構是被修改過的。這兩個方法和M\$j不太一樣,因為在dump出的libmobisec裡可以找到M\$z的函式名,證明這個方法沒有使用RegiterNatives
來做變換,因此我們可以透過符號表來找這個函式與檔案頭部的偏移。方法是找M\$z
和字串表的偏移,如0x03FE,然後窮搜整個檔案:
因為符號表應該會把字串表偏移作為一項,這塊區域的結構體,我們對照ELF結構發現並不是標準的符號表,但還是可以大概看出結構體的內容,包括索引,字串表偏移,以及ELF特殊的標誌數,因此推測0x57BE4偏移是M\$z
函式。該地址也正好是個壓棧的指令,證明了我們的猜想。
第五題
2015年移動安全挑戰賽的最後一道題目,在規定的比賽時間內,僅有來自我們GoSSIP的wbyang一名選手解決了這道問題,今天我們就來揭開這一道最高難度題目的神秘面紗。
先把名為AliCrackme_5.apk
的檔案丟到JEB裡看一看:
dex檔案並沒有進行加殼和混淆,看上去是一個非常簡單的程式,Java程式碼部分使用函式Register("Bomb_Atlantis", input)
對輸入進行判斷。所以需要分析的邏輯應該都在libcrackme.so
裡的Register
函式中。
接下來我們用IDA開啟這個libcrackme.so
,不出所料的發現IDA完全沒法處理,應該是進行了強烈的混淆和加殼處理:
使用和解決前面題目相同的技巧,我們繼續使用dd
的方法來去處一部分的混淆和加殼。執行一次程式後,從/proc/self/maps
裡找到libcrackme.so
在記憶體中的位置,使用dd
命令從/proc/self/mem
中提取出記憶體中的libcrackme.so
,接著使用在解決第四題時使用過的技巧,將libcrackme.so
和libc.so
一起載入到IDA裡。
用IDA開啟dump出的程式碼後,我們發現仍然有大部分的程式碼無法被IDA識別,需要手動定位到需要分析的程式碼然後手工定義(IDA快捷鍵C)程式碼,同時由於程式碼會在THUMB指令集和ARM指令集之間切換,有時候需要用快捷鍵ALT+G來將T暫存器設定為不同的值,設定正確後才能正確翻譯出程式碼。這裡我們首先遇到的問題是無法定位Register
函式,同樣使用第四題中的技巧,用InDroid監控到Register
函式的真實地址,就可以在該地址上開始分析。
在libcrackme.so
這個動態庫裡使用的一些混淆方法,對於處理了前面一些類似混淆後現在的我們來說已經不是問題(^_^)。透過分析程式碼,我們定位了幾個函式,這些函式的偏移在不同的裝置上應該是不同的。整體的邏輯其實並不複雜,首先會有一個固定的字串“Bomb_Atlantis”和一個固定的salt去進行一次md5運算,salt是動態生成的,不過由於dump記憶體的時候這些動態的值已經生成好了,所以能夠直接發現這個salt(出於一些版權原因我們不便公佈本題目的一些內部細節,因此該salt值請大家自己分析)
之後程式會將這個md5值和我們的輸入進行一些異或和計算的操作,經過幾步比較簡單、可逆的變換之後,進入一個比較複雜的函式,經過這個函式處理後直接和一個記憶體中的值進行比較,返回比較結果。
這裡說一個我們在做第五題時用到的分析方法——動態hook。由於libcrackme.so
中並沒有對呼叫自身的上層應用進行驗證,這就導致了我們可以自己寫一個程式去載入這個so,呼叫其中的方法。這也導致了我們在載入libcrackme.so
後,可以載入另一個用於hook的so,這樣我們可以hook libcrackme.so
中的任意函式,從而知道任意函式的引數和返回值,這對於我們理解程式有著非常大的幫助。這裡我們使用的hook框架是著名Android安全研究人員Collin Mulliner開發的Android平臺上的一個二進位制注入框架adbi。當然這道題目並不能夠透過注入的方法將我們的so注入進去,因為源程式禁掉了ptrace這樣的系統呼叫。我們對adbi稍作修改,使之成為一個可以手動載入的動態hook框架。同時由於我們沒法透過符號表來定位函式的地址,所有的hook地址都需要硬編碼,並且要和執行這道題目程式的Android裝置記憶體對映嚴格對應。
需要指出的是adbi中存在一個小bug,hook.c
這個檔案的118行應該是
h->jumpt[0]=0x60
而不是0x30,對應的thumb彙編應該是
push{r5,r6}
而不是
push{r4,r5},
這個小bug在解題過程中會造成一些影響。使用adbi來hook這道題目的函式還需要注意一點,這題的程式碼中有一些函式使用的THUMB指令集,hook這些函式時,不要忘記人工的對hook地址+1。
透過hook的方法,我們已經能夠動態的分析libcrackme.so
,首先我們驗證了我們對之前幾步變換的分析結果。之後就是分析最後一個複雜的處理函式,透過靜態分析+動態除錯,我們發現這是一個類似於白盒密碼學的加密函式。我們的輸入進入函式後,首先經過幾步類似DES的預處理,之後會進行若干輪的查表,透過查詢一個巨大的表將我們的輸入進行某種加密,生成一段密文,再經過幾次簡單的處理後和最後記憶體中的一段常量(出於一些版權原因我們不便公佈本題目的一些內部細節,因此該常量請大家自己分析)進行比較。
透過動態除錯,我們能夠計算出加密演算法最後應該輸出的值,但是由於這個加密演算法的金鑰融入了整個置換表中,要找出一個逆置換表顯然不太可能。我們簡單過濾了libcrackme.so
的其他函式,也沒有發現用於解密的函式,想要正常解密密文是不太現實了。不過根據對加密演算法的分析,我們發現這若干輪的置換是相互獨立的,並且每一輪的複雜度並不高,這就意味這我們可以在可以接受的時間內對演算法進行爆破。我們一開始的想法是code reuse,直接在Android裝置上爆破,但是發現速度太慢,最後只能用笨辦法,透過hook從記憶體中dump出來置換表,用C程式碼重寫了這個演算法,有驚無險地在比賽結束前半小時搜尋出結果。根據逆推演算法推出正確輸入是:
3EFoAdTxepVcVtGgdVDB6AA=
好了,我們的2015移動安全挑戰賽全系列回顧就到此為止了!希望大家能和我們多多交流討論,歡迎大家關注我們的微博GoSSIP_SJTU,基本上每天都會有精彩的內容釋出哦。
相關文章
- 【看雪2017安全開發者峰會演講回顧0x3】淺析WEB安全程式設計2017-11-23Web程式設計
- 【看雪2017安全開發者峰會演講回顧0x9】Windows 10新子系統*新挑戰2017-11-30Windows
- 【看雪2017安全開發者峰會演講回顧0x4】業務安全發展趨勢及對安全研發的挑戰2017-11-24
- 看雪安全程式設計培訓目錄2018-03-10程式設計
- 【看雪2017安全開發者峰會演講回顧0xA】移動APP灰色產業案例分析與防範2017-12-01APP產業
- 2020 看雪SDC議題回顧 | 高通移動基帶系統內部揭密2020-11-04
- 一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)2017-12-04瀏覽器
- 企業安全三大挑戰:移動辦公、資料洩漏、病毒入侵2010-12-31
- 2021看雪SDC議題回顧 | APT針對恐怖主義的間諜活動剖析2021-11-04APT
- 【看雪2017安全開發者峰會演講回顧0x1】如何黑掉無人機2017-11-21無人機
- 看雪安全網站2015-11-15網站
- .NET 20週年學習挑戰賽| .NET 移動應用2022-04-28
- 2020 看雪SDC議題回顧 | Android WebView安全攻防指南20202020-10-29AndroidWebView
- 安全AI挑戰者大賽啟動 阿里清華聯手送優勝者上全球頂會2020-07-17AI阿里
- 書生安全雲發起安全技術挑戰賽2017-09-11
- 【看雪2017安全開發者峰會演講回顧0x6】智慧化的安全: 裝置&應用&ICS2017-11-27
- Java JSON 反序列化之殤(看雪2017安全開發者峰會演講回顧11)2017-12-04JavaJSON
- 新加坡政府將舉辦區塊鏈挑戰賽2018-04-02區塊鏈
- 回顧·如何打造主動對話式AI2018-08-24AI
- 圓滿落幕!回顧 eBPF 技術的發展與挑戰2022-11-03eBPF
- 雲靶場、實戰攻防、前沿趨勢...本屆雲安全挑戰賽的看點都在這裡2020-10-26
- 阿里雲IoT將舉辦第三屆極客創新挑戰賽 吸引全球極客參賽2018-07-02阿里
- [推薦][看雪2017安全開發者峰會演講回顧11]Java JSON 反序列化之殤2017-12-02JavaJSON
- 【看雪2017安全開發者峰會演講回顧0x7】開啟IoT裝置的上帝模式2017-11-28模式
- 1月第4周安全回顧移動安全受關注黑客威脅公共設施2017-11-17黑客
- 看雪論壇版主座談 | 看雪2017安全開發者峰會2017-12-09
- [原創]乾貨 | 雲安全挑戰賽線上熱身賽冠軍戰隊解題分享2019-06-12
- 【看雪2017安全開發者峰會演講回顧0x5】Flash 之殤:漏洞之王 Flash Player 的末路2017-11-25
- CDA學員參加首屆阿里雲安全演算法挑戰賽獲季軍2016-11-02阿里演算法
- xss挑戰賽writeup2020-08-19
- 【看雪2017安全開發者峰會演講回顧 0x2】定製化對抗——遊戲反外掛的安全實踐2017-11-22遊戲
- 2020 看雪SDC議題回顧 | 生物探針技術研究與應用2020-10-30
- KubeMeet 深圳站回顧:應對雲原生邊緣計算落地挑戰2021-11-29
- 騰訊主機安全“獵刃計劃”正式釋出!WebShell挑戰賽再燃起,PHPer燥起來!2022-03-22WebshellPHP
- 看雪CTF.TSRC 2018 團隊賽 第十二題『移動迷宮』 解題思路2018-12-25
- 2020 看雪SDC議題回顧 | 麒麟框架:現代化的逆向分析體驗2020-11-03框架
- WePlay主舞臺回顧:CiGA x Fami通・電擊遊戲 - 直面會全程影片公佈2023-12-06遊戲
- 2021看雪SDC議題回顧 | SaTC:一種全新的物聯網裝置漏洞自動化挖掘方法2021-11-09