我想创建给出偏移量(并且知道库的基地址)的简单函数可以检索或设置位于那里的值。
问题:
内存类型之间有什么实际差异?
我想知道的是:我需要关注什么,让它透明地工作。例如,根据内存的类型,我是否必须使用不同的标志或“读取+写入+执行”设置适用于所有这些?那么Windows和Linux之间的差异呢?
答案 0 :(得分:1)
读取,写入和执行在Linux下是可能的,因为在编译程序时它们的elf32链接器的性质,例如,您可以将一个段作为只读,而在另一个段中,您可以只写它。不确定Windows下的链接器是否可以这样做,我知道它们可以有一个由其他程序共享的段......
现在,为了让它透明地工作,我认为你的意思是对于Windows和Linux?两个平台下的内存在概念上类似,因为内核已经在用户域程序和内核代码之间进行了保护。
在DOS的旧时代,内存被分成了段:offset,当它们组合在一起时:
segment + offset --------- physical memory for that segment for example: B800 + 0000 ------ B8000
这是为了绕过内存寻址模式中的怪癖,因为DOS环境只能访问2 ^ 20字节的内存,因此是段+偏移对,它用于16位模式。
这是一个16位代码示例,可以确定DOS屏幕中有多少行:
int heightScreen(void)
{
return (*(unsigned char far *) 0x484) + 1;
}
段是0x484,它位于BIOS中,告诉它有多少行。
在32位模式下,它只是段选择器:段(我认为......不确定,因为我是从内存中写的),根据操作系统的设计方式,有不同的方式从中访问内存需求分页,虚拟和平面模式的上下文...对此的引用可以在互联网上的pdf的英特尔文档中找到...相关文档是'24143004.pdf',您可以在其中搜索)
处理器具有ring0模式,可以执行特权指令并仅访问内存。 ring3是运行应用程序的用户域的地方,它们限制访问特权指令(或者根本没有),并且可以访问自己的内存空间。 ring2通常由设备驱动程序占用,设备驱动程序充当内核和用户空间之间的中间人。此外,内核会在那里提供调用门或陷阱来请求某个特权(只有设备驱动程序才能这样做)。
如果您想访问特权内存并冒险让系统瘫痪,请在Linux下将suid位提供给相关程序或以root身份运行。对于Windows,您需要提升代码的权限或执行代码注入进程内存,或者更恰当地说,编写设备驱动程序以与内核代码/内存空间交互,并为用户的land代码提供API以进行交互。
如果您尝试访问您无法访问的特权指令或内存,内核将通过终止该过程来关闭您的程序,就好像说“迷路了,没有办法”。
希望这个简短的总结有所帮助。
答案 1 :(得分:0)
我不知道* nix,但在Windows上,你将无法在库中随处写。 (Windows文档称它们为模块)
库中的页面大小为4k或8k(我认为8k仅在Alpha AXP上,现在已经过时了)。基地址将是64k的倍数。页面可以是写入时复制或只读。我相信所有包含代码的页面都是copy-on-write。包含数据的页面将是只读或写入时复制,具体取决于数据的声明方式。
您无需关心标志或设置。你要么能够写作,要么你不会。只要你留在模块边界,你应该始终能够阅读。
除非您使用Win32 API ReadProcessMemory或WriteProcessMemory
,否则您只能在自己的流程中写入网页编辑:基于评论
要更改页面保护标记,请使用VirtualProtect或VirtualProtectEx
请注意,如果将页面从只读更改为读写然后写入,则可能不仅会更改内存,还会将更改写回库文件。您应该将页面保护更改为copy-on-write。
对于支持该功能的系统,VirtualProtect也可用于更改页面的执行权限。
答案 2 :(得分:0)
你没有说明你编程的语言,所以我将假设C和衍生物。
如果你有一个地址,你可以通过将它转换为指针并取消引用来读取该地址的字节:
unsigned char value = *(unsigned char *)address;
如果你想写它:
*(unsigned char *)address = value;
为了使其正常工作,必须使用适当的权限将地址所在的页面映射到您的地址空间。如果页面未映射或者您没有相应的权限,操作系统通常会终止您的程序(在Linux上,它将生成段违例,即SEGV)。
答案 3 :(得分:0)
我不确定在Windows下,但在* nix中我相信mmap
可能会完成这项工作......也许,写入动态链接的内存最多仍然是危险的。 击>
根据你的评论,我猜你会想要做一些像
这样的事情//in the library
void myfunction(){
int x;
x=x+1;
}
//in the client
uint8_t *ptr=(void*)&myfunction;
ptr[0]=some_opcode;
我必须告诉你,这可能是我见过的最不安全的事情。我知道这对于一些安全的操作系统是不可能的,例如OpenBSD,因为它是W ^ X,你不能使用mmap,因为可执行文件已经链接到那个固定地址,所以它会破坏任何非本地变量访问(在x86-64上,我认为它也可能在x86-32上)
严重。为什么你需要能够做这种自修改代码?