20145319 《网络渗透》免考—病毒感染

20145319 《网络渗透》免考—病毒感染

概述

  • 本次实验主要是讲述了如何往pe病毒中写入我们的恶意代码,并且在ollydbg下写入汇编代码展示病毒感染的流程和效果
  • 主要知识如下
    • pe文件结构
    • 汇编语言
    • 堆栈结构
    • 代码重定位技术
    • api函数的动态加载调用

实验内容

概述

  • 本来是学习如何自己使用c++编写一个简易的壳的,但是其中涉及到大量汇编代码和知识实在是让我觉得有一些苦恼,于是想来想去还是决定来学习一下如何将病毒加载到我们的宿主文件体内,也算是一种“文件加壳”了

pe病毒的传统感染方式

  • 第一种就是通过直接在pe文件之后新增一个节,将病毒自身代码节直接复制到pe文件的尾部。在感染的过程中修改pe文件表头数据中的节的数目,即IMAGE_FILE_HEADER结构体中的numberOfSection参数值加一,修改节表中的节大小,IMAGE_SECTION_HEDAER结构体中sizeOfRawData参数,修改入口地址至病毒代码的起始地址(即原Pe文件的结束地址)即可
  • 上述这种方法的确能够胜任感染工作,但是缺点就是pe文件的大小发生变化,容易惹人注意
  • 相应的,为了应对这种方法,就有第二种感染方式,将病毒代码复制到pe文件的空白字段中去,为什么pe文件会有许多空白的字段呢,根据pe文件的相关知识,pe文件都在文件中会按照文件大小(一般是200H的整数倍)对齐(可以类比一本书,每个章节结束,该页剩下的部分均会留成空白,新的一章会在新的一页开始),这些“留白”就成为了我们的目标,但同样,这些空间有限,也大大限制我们代码的长度,在实际中感觉并不算太过于实用

注入

  • 在大概理解了其中的原理之后,我们通过实际操作来演示一下效果

  • 使用Ollydbg加载一个可执行文件,跳转到入口地址,我们先记下这段代码的地址,和前五个字节(因为Jmp指令长五个字节,我们之后还原也需要还原五个字节)

  • 随意找一块空白处写入我们要注入的汇编代码,先写好我们要用的字符串变量,hellozk,5319(选择binary edit,输入字符串,使用ctrl+a编译 )

  • 按照堆栈知识,我们可以知道,函数传参,都是通过堆栈来进行的,按照参数从右往左,以此入栈,在此就不详细展示了,总代码如下

  • 注入汇编,记住我们代码的地址011455ed,回到程序入口处,将前几个字节修改成jmp指令,指向我们的地址,运行则出现弹窗

  • 但是按照我们刚刚的理论,我们还需要回转到原位置,执行原本的代码,所以我们还需要附加上如下代码

      mov dword ptr [01141780], 558bec81
      mov byte ptr [01141784],ec
      jmp 01141780
    

动态加载和代码重定位

  • 这种将可执行代码从内存的一个地方移动到另一个地方去的手法往往面临着一个核心问题,就是函数调用和操作数的地址的变化问题

  • 在函数调用的问题上,我们通常使用API的动态加载技术,其核心在于loadlibrary函数GetProcAddress函数函数原型如下

  • kernel32.dll中的creatProcessW函数为例,通过Loadlibrary返回该dll的句柄,传递给GetProcAddress函数,就可以返回kernel32.dll文件中creatProcessW函数的地址

      HWND hwnd=LoadLibrary(kernel32);
      theAddress=GetProcAddress(hwnd,"creatProcessW");
    
  • 第二个核心技术就是代码的重定位技术,如果我们能通过特殊手法计算出变量或者函数在两个内存环境下地址的差值,是不是就可以让这部分可执行代码移动到内存任意一处地址中执行了呢?这种思路就是我们的代码重定位技术

  • 汇编代码如下

      	call relocate
      relocate: 
      	pop ebx
      	sub ebx,offset relocate
      	mov eax,[ebx+offset Var]
    
  • 其中核心部分就是动态加载和静态编译rl标号地址值的不同,通过两个部分相减得到差值,实现重定位

进阶注入

  • 在我们第一个注入的实验中呢,我们调用的是系统函数,而且是因为我们知道该函数的地址,直接通过call 函数地址的方式来调用的,如果我们并不能事先知道准确地址那么应该如何加载呢?这就要用到我们的动态调用技术了

  • 下面给一段汇编代码给大家参考一下

      PUSH EBP
      MOV EBP,ESP                        ; 重新定位堆栈位置
      MOV ESI,DWORD PTR SS:[EBP+8]       ; 从栈上获取函数参数       
      PUSH 6C6C                      
      PUSH 642E3233
      PUSH 72657375
      PUSH ESP                           ; - "user32.dll"(ESP为字符串首地址),LoadLibraryA的参数
      CALL LoadlibraryA            ; - LoadLibraryA("user32.dll")
      PUSH 41786F
      PUSH 42656761
      PUSH 7373654D
      PUSH ESP                           ; - "MessageBoxA"(同上"user32.dll"的解释)
      PUSH EAX                           ; - hMod(EAX是LoadLibraryA的返回值)
      CALL GetProcAddress         ; - GetProcAddress(hMod, "MessageBoxA")
      PUSH 0                             ; - 参数MB_OK 
      PUSH <ASCII>                       ; - string2
      PUSH <ASCII>                       ; - string1
      PUSH 0                             ; - 窗口句柄hWnd,无窗口为NULL
      CALL EAX                           ; - MessageBoxA(NULL,string1, string2, MB_OK)
    
  • 我们如何知道GetProcAddress,LoadLibrary两个函数本身的地址呢?这个我也查了资料,可以使用如下代码动态定位kernel32.dll的地址

      mov edx,fs:[30h]        //获得PEB
      mov edx,[edx+0ch]       //get peb_ldr_data
      mov edx,[edx+1ch]       //get InInitializationOrderModuleList
      mov edx,[edx]
      mov eax,[edx+50h]       //此时eax中存放的就是kernel32.dll的地址
    
  • eax寄存器中保存的就是我们kernel32.dll的地址了,只要使用eax加上两个函数在kernel32中的偏移量就可以准确定位到该函数的地址(系统中该偏移量是固定的,但是不同系统则有不同的偏移量,我没查到win8下的偏移量,所以上述代码大家就当做参考,学习思路,我就不展示结果和详细流程了)

原文地址:https://www.cnblogs.com/20145319zk/p/7044022.html