例如:
    有一个游戏修改器;其中有一个按钮“自动打怪”;点击时游戏会实现相应的功能;
    对于游戏程序来说,自动打怪操作本质上就是call调用一个函数;
    但是修改器和游戏是两个独立的程序,游戏无法直接调用修改器中的函数;
    可以考虑将修改器中的函数封装成一个dll,然后想办法将dll放到游戏的4gb空间,这样就可以了;
    怎么想办法将dll放进目标程序的4gb空间中去,这就是dll注入的本质;
    又有一个问题:修改器如何指挥注入到游戏中的dll做哪些操作;
    这涉及到了进程通信;
    需要了解进程相关的api,还需要了解线程的控制;也就是要了解win32的知识;
    比如想实现“自动打怪”,一般怪物相关的数据会存放在数组、链表、二叉树等数据结构中;需要遍历相关的数据结构;因此需要了解数据结构相关知识;
    
1.dll的入口函数
可以在dll中添加一个入口函数;
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Init();        //自定义的函数,在dll被加载前执行            
        break;
    case DLL_PROCESS_DETACH:
        Destroy();    //自定义的函数,在dll被移出时执行           
        break;
    }
    return TRUE;
} 
当dll在被加载时或被移除时会执行入口函数,并根据switch语句的case对应的阶段来决定执行相关语句;
利用入口函数,可以检测是否将dll注入成功;
 
创建一个用来注入的dll
头文件:
MyDll.h
#if !defined(AFX_MYDLL_H__A5631B62_A8CF_4C84_B164_75BDD6108880__INCLUDED_)
#define AFX_MYDLL_H__A5631B62_A8CF_4C84_B164_75BDD6108880__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

void Init();
void Destroy();
extern "C" _declspec(dllexport) void ExportFunction();

#endif // !defined(AFX_MYDLL_H__A5631B62_A8CF_4C84_B164_75BDD6108880__INCLUDED_)
Stdafx.h
#if !defined(AFX_STDAFX_H__77F350E0_A16A_4DEF_ACEE_3955B97E200C__INCLUDED_)
#define AFX_STDAFX_H__77F350E0_A16A_4DEF_ACEE_3955B97E200C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// Insert your headers here
#define WIN32_LEAN_AND_MEAN        // Exclude rarely-used stuff from Windows headers

#include <windows.h>

// TODO: reference additional headers your program requires here
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__77F350E0_A16A_4DEF_ACEE_3955B97E200C__INCLUDED_)
实现:
MyDll.cpp
#include "stdafx.h"
#include "MyDll.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void Init()
{
    MessageBox(0,"Dll加载前:我从未见过有如此厚颜无耻之人","Init",MB_OK);
}
void Destroy()
{
    MessageBox(0,"Dll销毁时:诸葛村夫,你敢","Destroy",MB_OK);
}
void ExportFunction()
{
    MessageBox(0,"不能打架,不能打架金坷垃好处都有啥,谁说对了就给他","ExportFunction",MB_OK);
}
入口主函数:
#include "stdafx.h"
#include "MyDll.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Init();    //DLL被加载的时候会调用;HINSTANCE   hModule = LoadLibrary("InjectDll.dll");
        break;
    case DLL_PROCESS_DETACH:
        Destroy();    // DLL被销毁的时候;FreeLibrary(hModule);
        break;
    }
    return TRUE;
}
该dll只有一个导出函数,作用是弹一个标题为ExportFunction的框;
有两个不导出的函数,Init()和Destory();在dll入口函数的加载和销毁阶段调用,用来提示dll是否成功注入;
如果dll注入成功,则在启动目标程序前会弹init框;
 
2.关于dll注入
1)常见的3环dll注入种类
    注册表注入                                        
    导入表注入                                        
    特洛伊注入                                        
    远程线程注入                    
    无DLL注入
    Apc 注入
    Windows挂钩注入DLL
    输入法注入
 
特洛伊注入的原理:
    exe根据导入表来加载dll,一个exe加载dll时首先要找到这个dll;
    例如:如果一个程序要加载kernel32.dll,首先会在exe所在目录去找该dll,如果找不到就去系统目录找;
    根据这个特点,可以自己发布一个dll,也叫kernel32,并且该dll的导出表导出的函数和kernel32一样,并放到exe所在目录;
    为了防止程序崩溃,在自己的dll的导出函数中执行自己的操作后转发kernel32函数;相当于在执行真正的kernel32函数前先
    
 
3.导入表注入
1)原理:    
    当Exe被加载时,系统会根据Exe导入表信息来加载需要用到的DLL,
    导入表注入的原理就是修改exe导入表,将自己的DLL添加到exe的导入表中,
    这样exe运行时可以将自己的DLL加载到exe的进程空间.    
 
2)主要步骤
1】根据目录项(第二个就是导入表)得到导入表信息
数据目录结构:                
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
VirtualAddress :指向导入表结构            
Size:导入表的总大小            
 
2】计算插入新导入表所需要的空间
导入表结构:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;
        DWORD   OriginalFirstThunk;
    };
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain;
    DWORD   Name;
    DWORD   FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
INT表和IAT表中以名字导出的函数rva指向的结构:
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    BYTE    Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
新增一个导入表所需的空间如图:
A:20字节    ->导入表占20个字节                                                        
B:16字节    ->INT表每一项是一个4字节的结构,再加上结尾的全0的一项共需8字节,IAT表和IAT表在文件镜像中一样也要8字节;                                                        
C:取决于DLL名串的长度+1    ->Dll的名字符串,加1字节的表示字符串结尾的0                                                        
D:取决于函数名的长度+1+2    ->函数名字符串 + 1字节的字符串结尾符0 + 2字节的Hint                            
                                                        
判断哪一个节的空白区 > Size(原导入表的大小) + 20 + A + B + C + D                                                        
如果空间不够:可以将C/D 存储在其他的空白区                                                        
也就是,只要空白区 > Size + 0x20就可以了                                                        
如果仍然不够,就需要扩大最后一个节,或者新增节来解决.    
 
3】将原导入表全部Copy到空白区            
                        
4】在新的导入表后面,追加一个导入表.            
 
5】追加8个字节的INT表  8个字节的IAT表            
 
6】追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串                            
 
7】将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项                        
 
8】分配空间存储DLL名称字符串 并将该字符串的RVA赋值给Name属性                        
 
9】修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size                    
 
 
 
 
01-13 09:35