实现控制器翻转与依赖注入

实现控制器翻转与依赖注入

Spring.net 
IOC:Invasion of Control,控制器翻转,名字由英文翻译过来就是这个意思了,其实用通俗的话来说就是:将创建对象的职责交给控制器来做,这个控制器就是spring了
 
DI:依赖注入,通俗点说就是给IOC反射出来的对象的属性赋默认值
 
Spring.net的好处:将层与层之间完全的解耦,也就是说,在我的ui层完全不需要通过new的方式或者工厂的方式来获得一个对象了,只需要配置下xml文件就可以实现给对象赋值了、
 
Spring.net配置步骤
 
首先需要的是 引用一些程序集,程序集在这个目录下
 
Spring.Net\Spring.NET-2.0.0-M1\Spring.NET\examples\Spring\Spring.Mvc3QuickStart\packages
当然这些文件也建议拷贝到你的MVC项目的packages文件夹下
 
需要的DLL文件有如下选中的。文件夹下有各种与.net版本对应的程序集
实现控制器翻转与依赖注入_第1张图片
 
应用程序集完成之后,剩下来的工作就比较简单了。
 
1、配置web.config文件
这个配置,你可以去spring.net里面的demo里面去拷贝,没必要记住,Demo的目录都在这里
 
Spring.NET-2.0.0-M1\Spring.NET\examples\Spring
 
我选中的是MVC3的demo,你需要根据你的项目来确定版本,不过配置都不会有什么差别的
 
将Demo里面的web.config文件下的两个节点拷贝到你的web.config文件中
实现控制器翻转与依赖注入_第2张图片
 
2、Controllers文件下配置对象
实现控制器翻转与依赖注入_第3张图片
     第二个object节点就是我配置给第一个object节点对象的属性。
做个比喻吧,这里对刚接触spring.net的人来说比较难理解。
 
看下我的案例吧
实现控制器翻转与依赖注入_第4张图片
 
3、配置Global文件
     这一步很简单,只需要将我们的MvcApplication从原先继承HttpApplication改成继承我们的SpringMvcApplication就行了
 
总的来说:Spring.Net容器里面其实就是一个复杂的抽象工厂,它功能很强大,我只懂皮毛而已。希望对你有所帮助
 
 
对了,看别人的博客都写的很漂亮,不知道怎么弄的,我很忙,没时间学,会的朋友直接在回复中指点下,谢谢啦
 
 
--------生活很精彩,后天很美好,我不能死在明天晚上
 
 
分类:  Asp.Net

注入代码

有使用过外挂的朋友应该知道,我们在玩游戏的时候,有很多辅助功能给你使用,比如吃药,使用物品等功能,这个时候我们就是使用注入代码的技术,简单的来将就是我们让另外一个进程去执行我们想让它执行的代码,这中间的关键函数是CreateRemoteThread

复制代码
HANDLE WINAPI CreateRemoteThread(
  _In_   HANDLE hProcess,
  _In_   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_   SIZE_T dwStackSize,
  _In_   LPTHREAD_START_ROUTINE lpStartAddress,
  _In_   LPVOID lpParameter,
  _In_   DWORD dwCreationFlags,
  _Out_  LPDWORD lpThreadId
);
复制代码

CreateRemoteThread的参数跟CreateThread的参数差不多,多出来的hProcess是我们要对其操作的进程HANDLE,在早期的WINDOWS版本CreateThread确实是使用CreateRemoteThread实现的,就是把hProcess传入我们自己的进程HANDLE


CreateRemoteThread的功能就是在指定的进程创建一个线程,这个线程运行我们指定的函数,看起来很简单,但是有一个问题,就是虚拟内存导致的问题

大家都知道,在WINDOWS下是使用虚拟内存来进行数据管理的,每个进程都有自己独立的地址空间,假设进程A准备向进程B注入一段代码,他要让进程B执行他进程空间的函数InjectionCode(),这个函数在进程A的地址空间地址为0X3000

现在我们开始进行代码注入,利用CreateRemoteThread我们告诉B进程,请执行虚拟内存地址为0X3000的代码,这个时候B进程该干什么呢??B进程收到这个命令后,他很听话地创建了线程,然后乖乖得CALL了0X3000的内容,请注意,现在B进程CALL的是它自己内存空间内0X3000的代码而不是A进程的,那么现在B进程的0X3000是什么内容??没人知道,运气好的话说不定真的有段代码给你执行,运气不好你自己也不知道会发生什么事情,这就跟你进错了学生公寓一样,同样号码的房间,运气好是校花的房间,运气不好就是如花的房间

那么我们怎么才能让进程去执行我们对应的代码呢??我们只要在B进程内开辟一块内存,然后把我们的代码或者数据复制进去,再执行对应的代码就可以了,我们需要用到这几个函数:

复制代码
LPVOID WINAPI VirtualAllocEx(
  _In_      HANDLE hProcess,
  _In_opt_  LPVOID lpAddress,
  _In_      SIZE_T dwSize,
  _In_      DWORD flAllocationType,
  _In_      DWORD flProtect
);

BOOL WINAPI WriteProcessMemory(
  _In_   HANDLE hProcess,
  _In_   LPVOID lpBaseAddress,
  _In_   LPCVOID lpBuffer,
  _In_   SIZE_T nSize,
  _Out_  SIZE_T *lpNumberOfBytesWritten
);
复制代码

这两个函数跟我们平常用的函数都差不多,只是多了个进程的选项,大概步骤如下图:
实现控制器翻转与依赖注入_第5张图片

 

现在我们将实际操作一下:

下面是我们要注入的程序,在这之前,我们最好把基地址固定掉,这样我们不会每次重新运行程序的时候函数的地址都会改变,在VS2008中,项目属性->链接器->高级,把随机基址和固定基址选择默认值

复制代码
void PrintMsg(const char *msg)
{
    printf("ThreadI D:%d Msg:%s\n",GetCurrentThreadId(),msg);
}
int main(void)
{
    printf("%d\n",GetCurrentThreadId());
    printf("Print Msg Function Address:%X\n",PrintMsg);
    system("pause");
}
复制代码

假设我们的PrintMsg的地址是0x401000,现在我们需要往这个进程里面注入一段代码,让她可以自动调用PrintMsg这个函数

复制代码
 1 static const char *msg = "INJECTION CODE SUCESS\n";
 2 static const unsigned int PARAM_SIZE = 100;
 3 static const unsigned int EXE_SIZE = 500;
 4 
 5 
 6 void InjectionCode(const char *msg)
 7 {
 8     __asm
 9     {
10         push eax
11         push msg
12         mov eax,0x401000
13         call eax            //因为在我们要注入的进程中,PrintMsg位于0x401000这个位置
14         pop eax
15         pop eax
16     }
17 }
18 int main(void)
19 {
20     HANDLE hProcess = OpenProcessByProcessNmae("main.exe"); //这个函数在上一章
21     
22     if (hProcess == INVALID_HANDLE_VALUE)
23     {
24         printf("error open process %d\n",GetLastError());
25         return 1;
26     }
27     //一定要把函数的代码和msg写入要注入的进程,否则会发生位置错误(一般是崩溃)
28     LPVOID RemoteExe = VirtualAllocEx(hProcess,NULL,EXE_SIZE,MEM_COMMIT,PAGE_EXECUTE);
29     LPVOID RemoteParam = VirtualAllocEx(hProcess,NULL,PARAM_SIZE,MEM_COMMIT,PAGE_READWRITE);
30 
31     SIZE_T WriteCount = 0;
32     int ret = 0;
33     ret = WriteProcessMemory(hProcess,RemoteParam,msg,PARAM_SIZE,&WriteCount);
34     ret = WriteProcessMemory(hProcess,RemoteExe,InjectionCode,0x13,&WriteCount);
35 
36     HANDLE hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)RemoteExe,RemoteParam,0,NULL);
37     WaitForSingleObject(hThread,INFINITE);
38 }
复制代码

运行上面的程序,我们就可以在另外一个进程中创建一个线程,并且这个线程将会输出该线程的ID以及我们要输出的消息

上面的程序还有几个要注意的:

1.资源竞争

由于是创建线程执行相应代码所以肯定会有资源竞争的问题,以后要写代码一定要注意,在本例中我忽略了这个问题

2. 关于代码的长度问题

在本例中,我们的代码长度是0X13,但是要知道,汇编代码的长度随便懂一下就可能更改,可能因为一个指令,也可能因为一个参数,所以我们需要时刻注意这点,关于代码长度怎么测量,我是看了反汇编的代码后计算的,这个方法比较准确,也可以大概估计下,只要能把代码复制完整就可以,超出也没关系,只要不超出申请的内存大小就可以

3.记得备份我们使用的寄存器

这个十分重要,一旦你更改了寄存器,如果没有后面没有恢复,可能会导致一系列错误,特别是ESP,EBP等重要的寄存器

3.注入代码多次调用系统DLL中的函数

<<WINDOWS核心编程>>里面说,系统的DLL都会加载到一个固定的地址,比如VirtualAllocEx,一般我们在A进程和B进程的时候,call或者jmp的地址都是一样的,所以一般我们如果调用的是系统函数,一般我们不需要担心,但是,昨天我想到了一个问题,比如我们进程A要命令进程B调用CreateToolhelp32Snapshot这个系统API,现在我们假设CreateToolhelp32Snapshot这个API在单独的TLHELP32.DLL里面(实际上这个在KERNEL32.DLL里面,所有进程都会加载这个DLL,所以不需要担心下面的问题,这个只是举例),操作系统在加载DLL的时候,会统一把这个API的地址映射到虚拟内存的0XFF40100的地址,按照我们原来的想法进程B会自己跑去call 0XFF40100这个地址。但问题在于,如果我们的进程根本就没有加载TLHELP32.DLL这个DLL,那么进程call 0XFF40100会怎么样??这个就要看你这个地方是什么代码了,有人说操作系统会帮你加载这个DLL,但我觉得是错的,因为操作系统要帮你加载的DLL都在PE头里面的导入表里面,要嘛就是我们要显示地去加载,否则操作系统不会知道我们的API在哪个DLL里面

4.注入代码多次调用我们自己编写的函数

比如我们有IntejectionCode,里面调用了IntejectionCode1,这个时候我们需要把IntejectionCode1也写入对方进程里面,不能只写入IntejectionCode,并且,我们需要更改IntejectionCode里面call IntejectionCode1跳转指令,让其跳转到正确的位置。总之,别人的地盘别人做主,对方进程想把代码放哪里就放哪里,我们无法管理(实际上virtualAllocEx是可以指定位置的,但是一般我们都尽量让操作系统去指定),我们只能入乡随俗,人家让我们去哪里调用我们就要去哪里调用,不然很容易导致进程崩溃

5.关于代码的基地址

在本例中PrintMsg的基地址是固定的,是我们人为去固定基地址的,我们在开发的时候很少人会去把基地址固定掉,所以在进程运行的时候,PrintMsg这个函数的地址是会改变的,当然我们也可以算出来这段代码在运行的时候会放在哪里,因为PrintMsg这段代码以二进制放在EXE文件的时候,也有一个文件偏移量,当操作系统把EXE文件加载进内存后,会根据基地址和文件偏移量,来算出PrintMsg在虚拟内存中的位置,所以我们只要能拿到进程运行时候的基地址,并且把EXE反汇编查看这段代码的文件偏移量,也能算出每次运行的时候PrintMsg的地址,虽然很麻烦,特别是反汇编找代码的那部分,但也没办法,这个在后面我会讲

 
 
分类:  windows编程

你可能感兴趣的:(实现控制器翻转与依赖注入)