原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题 如何编写病毒代码? 首先我把最重要的两个方面列举出来。 1. 处理病毒各个绝对地址的重定位。 2. 所有需调用的api函数地址,均通过动态搜索来获得。 做到以上2点,我们的病毒代码就可以移植到任意的程序中。没错,就和我们写shellcode一样。 先来看一些代码的优化方法: (1)测试寄存器是否为0 cmp eax,00000000h ; 6 bytes jz bribriblibli ; 2 bytes (if jz is short) optimization: or eax,eax ; 2 bytes jz bribriblibli ; 2 bytes (if jz is short) xchg eax,ecx ; 1 byte jecxz bribriblibli ; 2 bytes (if it is short) (2)测试寄存器是否为-1 cmp eax,0FFFFFFFFh ; 6 bytes jz insumision ; 2 bytes (if short) optimization: inc eax ; 1 byte xchg eax,ecx ; 1 byte jecxz insumision ; 2 bytes (if short) dec ecx ; 1 byte inc eax ; 1 byte jz insumision ; 2 bytes dec eax ; 1 byte (3)寄存器清0并传送低位字数值 xor eax,eax ; 2 bytes mov ax,word ptr [esi+6] ; 4 bytes optimization: movzx eax,word ptr [esi+6] ; 4 bytes (4) 关于push的优化 mov eax, 50h ; 5 bytes optimization: push 50h ; 2 bytes pop eax ; 1 bytes push 0 ; 2 bytes push 0 ; 2 bytes push 0 ; 2 bytes push 0 ; 2 bytes push 0 ; 2 bytes push 0 ; 2 bytes push 0 ; 2 bytes optimization: xor eax, eax ; 2 bytes push eax ; 1 byte push eax ; 1 byte push eax ; 1 byte push eax ; 1 byte push eax ; 1 byte push eax ; 1 byte push eax ; 1 byte push 7 ; 2 bytes pop ecx ; 1 byte _loop: push 0 ; 2 bytes loop _loop ; 2 bytes (5) 操作FS寄存器相关优化 push dword ptr fs:[00000000h] ; 6 bytes mov fs:[0],esp ; 6 bytes [...] pop dword ptr fs:[00000000h] ; 6 bytes optimization: xor eax,eax ; 2 bytes push dword ptr fs:[eax] ; 3 bytes mov fs:[eax],esp ; 3 bytes [...] pop dword ptr fs:[eax] ; 3 bytes (6) 字符串操作 mov al/ax/eax, [esi] ; 2/3/2 bytes inc esi ; 1 byte optimization: lodsb/w/d ; 1 or 2 byte 到达字符串尾部。 lea edi,[ebp+ASCIIz_variable] ; 6 bytes @@1: cmp byte ptr [edi],00h ; 3 bytes inc edi ; 1 byte jz @@2 ; 2 bytes jmp @@1 ; 2 bytes @@2: inc edi ; 1 byte optimization: lea edi,[ebp+ASCIIz_variable] ; 6 bytes xor al,al ; 2 bytes @@1: scasb ; 1 byte jnz @@1 ; 2 bytes (7)乘法 mov ecx,28h ; 5 bytes mul ecx ; 2 bytes optimization: imul eax,eax,28h ; 3 bytes (8)置edx寄存器为0. xor edx, edx ; 2 bytes optimization: cdq ; 1 bytes (9)交换寄存器4字节的顺序 mov eax, 00200000h ; 5 bytes bswap eax ; 2 bytes ;eax = 00002000h now (10)乘2、除2 shl eax, 1 ; 2 bytes ;*2 shr eax, 1 ; 2 bytes ;/2 (11)分配堆栈空间 push ebp ; 1 byte mov ebp,esp ; 2 bytes sub esp,20h ; 3 bytes optimization: enter 20h,00h ; 4 bytes (12)压入字符串指针时尽量使用 call @f db \'string\', 0 @@:
下面的代码例子,是首先通过VirtualAlloc申请一段ShellcodeSize大小的可读可写可执行内存区,然后将我们按照病毒思路编写的Shellcode Copy到申请的内存区中。然后call eax, 跳转到我们申请的内存区中去执行。从这个例子我们可以看到,我们的Shellcode(Virus Code)可以移植到任意的内存地址中去执行,它完成了我们预期想要的目的
.386 02. .model flat, stdcall 03. option casemap:none 04. 05.include windows.inc 06.include kernel32.inc 07.includelib kernel32.lib 08. 09. .code 10. 11.ShellCode: 12. call GetKrnlBase ; Get kernel32.dll base address 13. call Dels 14.Dels: 15. pop ebx 16. sub ebx, Dels 17. lea edi, [ebx + szFuncs] ; 重定位szFuncs 18. push edi ; szFuncs 19. push eax ; kernel32.dll base address 20. call GetFuncAddress 21. call @f 22. db \'user32.dll\', 0 23.@@: 24. call dword ptr [edi] ; LoadLibrary user32 25. 26. push edi 27. push eax 28. call GetFuncAddress ; Get User32 Func 29. 30. mov edx, edi ; edx = szFuncs的偏移 31. add edx, szText - szFuncs ; edx加上szFuncs和szText的偏移 = szText 32. 33. xor eax, eax 34. push eax 35. push eax 36. push edx ; push szText 37. push eax 38. call dword ptr [edi + 4] ; MessageBoxA 39. ret 40. 41.szFuncs dd 0a412fd89h ; LoadLibraryA 42. dd 014d14c51h ; MessageBoxA 43. dd 0 44.szText db \'如何编写病毒代码例子\', 0 45. 46.GetKrnlBase: 47. 48. assume fs:nothing 49. mov eax, fs:[30h] 50. mov eax, [eax + 0ch] 51. mov eax, [eax + 1ch] ; 第一个LDR_MODULE 52.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 53.; 压入kernel32.dll的unicode字符串 54.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55. push dword ptr 006ch 56. push dword ptr 6c0064h 57. push dword ptr 2e0032h 58. push dword ptr 33006ch 59. push dword ptr 65006eh 60. push dword ptr 720065h 61. push word ptr 006bh 62. mov ebx, esp ; ebx指向字符串 63. 64._Search: 65. or eax, eax 66. jz _NotFound 67. inc eax 68. jz _NotFound 69. dec eax 70. lea esi, [eax + 1ch] ; esi指向UNICODE_STRING结构 71. xor ecx, ecx 72. mov cx, 13 ; 比较长度 73. mov esi, [esi + 4] ; 获得UNICODE_STRING结构的Buffer成员 74. mov edi, ebx ; edi指向kernel32unicode字符串 75. repz cmpsw 76. or ecx, ecx ; ecx减完说明相等 77. jz _Found 78. mov eax, [eax] ; 获取下一个LDR_MODULE 79. jmp _Search 80. 81._NotFound: 82. or eax, 0ffffffffh 83. jmp _Over 84. 85._Found: 86. mov eax, [eax + 08h] ; 获得地址 87. 88._Over: 89. add esp, 26 90. ret 91. 92.GetFuncAddress: 93. 94. pushad 95. mov ebx, [esp + 4 * 8 + 4] ; ebx = hModule 96. mov edx, [ebx + 3ch] ; PE 97. mov esi, [ebx + edx + 78h] ; Export Table RVA 98. lea esi, [esi + ebx + 18h] ; Export Table VA+18H NumberOfNames 99. lodsd 100. xchg eax, ecx 101. lodsd 102. add eax, ebx 103. xchg eax, ebp ; ebp = AddressOfFunctions 104. lodsd 105. add eax, ebx 106. xchg eax, edx ; edx = AddressOfNames 107. lodsd 108. add eax, ebx 109. push eax ; [esp] = AddressOfNameOrdinals 110. mov esi, edx 111. 112._Next_Func: 113. lodsd 114. add eax, ebx 115. 116. ; Make Func Hash 117. xor edx, edx 118._Make_Hash: 119. rol edx, 3 120. xor dl, byte ptr [eax] 121. inc eax 122. cmp byte ptr [eax], 0 123. jnz _Make_Hash 124. 125. mov eax, [esp] 126. add dword ptr [esp], 2 127. mov edi, [esp + 4 * 8 + 8 + 4] 128._Scan_Dw_Funcs: 129. cmp dword ptr [edi], edx 130. jnz _Next_List ; 不等于比较hash list的下一个hash 131. 132. movzx eax, word ptr [eax] 133. mov eax, [ebp + eax * 4] 134. add eax, ebx ; 获得api地址 135. stosd ; 将其填充到对应的edi指向的hash list处 136. jmp _Ret 137. 138._Next_List: 139. scasd ; 增加edi使其指向下一个hash 140. cmp dword ptr [edi], 0 141. jnz _Scan_Dw_Funcs 142. 143._Ret: 144. loop _Next_Func 145. pop ecx 146. popad 147. ret 4 * 2 148. 149.ShellCodeSize equ $ - ShellCode ; ShellCode到此处的代码长度 150. 151.Start: 152. invoke VirtualAlloc, NULL, ShellCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE 153. pushad 154. push ShellCodeSize 155. pop ecx 156. push ShellCode 157. pop esi 158. xchg eax, edi 159. rep movsb 160. popad 161. call eax 162. ret 163. 164. end Start