循环渐进NsDoor(五)
虚拟机已经配好了,开发环境好了,剩下的就是不辞辛苦,写代码的事情了
高级语言写程序大家都见过,汇编也有一部分人用,但是用的不多,但是反汇编的魅力却很少有人注意
首先要说下shellcode的原理吧
基础知识应该是C++和ASM
随便贴段代码《缓冲区溢出教程》:
#include<windows.h>
int main()
{
LoadLibrary(“msvcrt.dll”);
system(“command.com”);
return 0;
}
//这个是开DOS窗口的代码,没什么普通,典型语法糖…..
#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC)(LPTSTR); //定义函数指针
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary(“msvcrt.dll”);
ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system"); //查找system函数地址
(ProcAdd) ("command.com"); //其实就是执行system(“command.com”)
return 0;
}
int main()
{
LoadLibrary(“msvcrt.dll”);
system(“command.com”);
return 0;
}
//这个是开DOS窗口的代码,没什么普通,典型语法糖…..
#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC)(LPTSTR); //定义函数指针
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary(“msvcrt.dll”);
ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system"); //查找system函数地址
(ProcAdd) ("command.com"); //其实就是执行system(“command.com”)
return 0;
}
这段代码实现上面那段一样的功能,但是语法糖成分少了许多…
PS:这段代码其实是有BUG的,我测试过,压入的数据有问题,不应该是字符串,应该是付给它个正确的地址….
仔细看下,应该发现些许程序执行的原理
任何程序代码都是要压入内存的,ASM中是code segment
代码段也是地址啊,所以调用函数,就是call
Call函数的地址而已,这是写把高级语言转换成shellcode的基础
现在转向要分析的reverse.cpp
先分析出这个文件中调用的所有函数:
有这些个函数:
自然每个函数对应一个地址,明白windows一点原理的应该知道,这些函数的地址是固定的(在各个版本系统内应该是固定的)
我用的是XP SP3,本机VISTA下地址和XP就不一样了,如何做到宇宙通用版的函数地址?这个是shellcode的高级技术,我只听说过,等以后认真学习了汇编再尝试下,呵呵
如何找到这些函数的地址呢,我写了个程序:
//address.cpp
#include<iostream>
#include<windows.h>
using namespace std;
typedef void (*MYPROC)(LPWSTR);
int main()
{
char adrStr[200];
HINSTANCE LibHandle;
MYPROC ProcAdd;
cout<<" Function Address Hunter "<<endl;
while(true)
{
cout<<"The Dll name:";
cin>>adrStr;
LibHandle = LoadLibraryA(adrStr);
cout<<LibHandle<<endl<<endl;
cout<<"Function Name:";
cin>>adrStr;
while(strncmp(adrStr,"exit",4) != 0)
{
ProcAdd = (MYPROC)GetProcAddress(LibHandle,adrStr);
cout<<ProcAdd<<endl;
cout<<"Function Name:";
cin>>adrStr;
}
}
return 0;
}
#include<iostream>
#include<windows.h>
using namespace std;
typedef void (*MYPROC)(LPWSTR);
int main()
{
char adrStr[200];
HINSTANCE LibHandle;
MYPROC ProcAdd;
cout<<" Function Address Hunter "<<endl;
while(true)
{
cout<<"The Dll name:";
cin>>adrStr;
LibHandle = LoadLibraryA(adrStr);
cout<<LibHandle<<endl<<endl;
cout<<"Function Name:";
cin>>adrStr;
while(strncmp(adrStr,"exit",4) != 0)
{
ProcAdd = (MYPROC)GetProcAddress(LibHandle,adrStr);
cout<<ProcAdd<<endl;
cout<<"Function Name:";
cin>>adrStr;
}
}
return 0;
}
测试报告:
VISTA下的
XP SP3下的:
地址还是不一样的哈
对了,这个函数地址查询你要确定函数所在的dll,其实这个还算简单,网上一搜就知道了
说个比较特殊的
Memset这个函数是在ntdll中,比较例外的一个
找到了地址就成功了很多
我们可以把地址压入栈,然后call地址就行了,相当于调用了函数
但是函数的参数怎么得到,这是个问题,而且涉及了很多调试的技巧…
下面虚拟个例子:
假设是Sleep(200)
这个函数,首先分析Sleep在哪个dll
发现实在kernel32中,用上面的程序查找出Sleep的地址:
0x7C802446
参数怎么构造的?
涉及汇编语言了,原理就是读取最近压入 的数据
所以你可以明白为什么函数的参数是从又向左压入栈了….
程序中的
Sleep(200);
现在替换掉:
_asm
{
Push 200
Mov eax, 0x7C802446
Call eax
}
随便找个程序插入测试,完全可以通过,这就是构造参数
其实这中间过程也蛮复杂的,要注意各个寄存器的数值的变化,例如EIP,记录程序执行位置,ESP记录堆栈顶,EAX记录返回值等等
后面我还会用到EBP记录这些地址数据
额…
再接再厉,后面还有三篇要写
--------------by NewSketcher
Time : 080822 15:55