我试图理解经典的缓冲区溢出漏洞,即输入缓冲区覆盖堆栈、保存在堆栈上的函数返回地址和上部内存区域(通常用于放置shell代码)的情况。互联网上有许多这方面的例子,我认为我已经理解得很好了:
1. 输入数据超过开发人员设定的固定大小的输入缓冲区。 2. 溢出的输入将覆盖堆栈上调用函数的函数参数和返回地址。 3. 当操作系统尝试从发生溢出的函数返回时,该地址将被加载到EIP中,这就允许您将受控制的数据加载到EIP寄存器中(如果不仔细阅读一些文章,您会得出可以覆盖CPU寄存器的印象。当然,事实并非如此,您只能覆盖堆栈,但CPU将从堆栈中加载地址到其寄存器中)。 4. 如果攻击设计得很好,那么加载到EIP中的值将使程序跳转到shell代码的开头(参见第5点)。 5. 我认为我已经很好地理解了“JMP ESP”机制。在您的实验环境中复制崩溃时,您要查找内存中包含“JMP ESP”指令的位置,并用该地址覆盖EIP(现在我的措辞不太准确,我知道...)。无论您运行此操作多少次、在何时、哪个线程执行您的操作等,该地址都需要相同,至少对于相同的操作系统版本,且该地址不能包含任何禁止使用的字节。代码将跳转到该地址(同时堆栈减少4个字节),因此“jmp esp”会跳转到溢出缓冲区中我设置用于覆盖EIP的值之后的下一个地址,通常这是shellcode所在的地方,可能附加了NOP。
现在来谈问题。
所有我读过的文章都在DLL中寻找“JMP ESP”指令的地址(必须不可重定位,未使用ASLR等)。为什么不在exe本身中寻找“jmp esp”?为什么它必须在DLL中?
我在Immunity Debugger中运行了“!mona modules”命令,唯一显示满足所有这些条件的模块是exe本身。当我查看流行的漏洞数据库时,地址总是在已加载的DLL中。
我看不出任何明显的原因。exe也可以在内存中以与DLL相同的方式位于相同的地址。有什么区别吗?
1. 输入数据超过开发人员设定的固定大小的输入缓冲区。 2. 溢出的输入将覆盖堆栈上调用函数的函数参数和返回地址。 3. 当操作系统尝试从发生溢出的函数返回时,该地址将被加载到EIP中,这就允许您将受控制的数据加载到EIP寄存器中(如果不仔细阅读一些文章,您会得出可以覆盖CPU寄存器的印象。当然,事实并非如此,您只能覆盖堆栈,但CPU将从堆栈中加载地址到其寄存器中)。 4. 如果攻击设计得很好,那么加载到EIP中的值将使程序跳转到shell代码的开头(参见第5点)。 5. 我认为我已经很好地理解了“JMP ESP”机制。在您的实验环境中复制崩溃时,您要查找内存中包含“JMP ESP”指令的位置,并用该地址覆盖EIP(现在我的措辞不太准确,我知道...)。无论您运行此操作多少次、在何时、哪个线程执行您的操作等,该地址都需要相同,至少对于相同的操作系统版本,且该地址不能包含任何禁止使用的字节。代码将跳转到该地址(同时堆栈减少4个字节),因此“jmp esp”会跳转到溢出缓冲区中我设置用于覆盖EIP的值之后的下一个地址,通常这是shellcode所在的地方,可能附加了NOP。
现在来谈问题。
所有我读过的文章都在DLL中寻找“JMP ESP”指令的地址(必须不可重定位,未使用ASLR等)。为什么不在exe本身中寻找“jmp esp”?为什么它必须在DLL中?
我在Immunity Debugger中运行了“!mona modules”命令,唯一显示满足所有这些条件的模块是exe本身。当我查看流行的漏洞数据库时,地址总是在已加载的DLL中。
我看不出任何明显的原因。exe也可以在内存中以与DLL相同的方式位于相同的地址。有什么区别吗?