我正在阅读关于自修改代码的密码破解者期刊文章,并且有这段代码:
void Demo(int (*_printf) (const char *,...))
{
_printf("Hello, OSIX!n");
return;
}
int main(int argc, char* argv[])
{
char buff[1000];
int (*_printf) (const char *,...);
int (*_main) (int, char **);
void (*_Demo) (int (*) (const char *,...));
_printf=printf;
int func_len = (unsigned int) _main - (unsigned int) _Demo;
for (int a=0; a<func_len; a++)
buff[a] = ((char *) _Demo)[a];
_Demo = (void (*) (int (*) (const char *,...))) &buff[0];
_Demo(_printf);
return 0;
}
这段代码应该在堆栈上执行Demo()。我理解大部分代码,但是他们分配'func_len'的部分让我困惑。据我所知,他们从另一个随机指针地址中减去一个随机指针地址。
有人在意解释吗?
答案 0 :(得分:7)
代码依赖于编译器函数布局的知识 - 这可能与其他编译器不可靠。
func_len
行经过更正以包含最初缺失的-
后,通过减去Demo
中的地址来确定函数_Demo
的长度(应该从Demo()
中的地址(包含起始地址_main
)中包含main()
)的起始地址。这被假定为函数Demo
的长度,然后将其按字节方式复制到缓冲区buff
中。然后将buff
的地址强制转换为函数指针,然后调用该函数。但是,由于_Demo
和_main
实际上都没有被初始化,因此代码在极端情况下是错误的。此外,还不清楚unsigned int
是否足以准确地保持指针;演员应该是来自uintptr_t
或<stdint.h>
的{{1}}。
如果错误是固定的,如果关于代码布局的假设是正确的,如果代码是与位置无关的代码,并且没有防止执行数据空间的保护,则这是有效的。它不可靠,不便携,不推荐。但它确实说明了,如果它有效,代码和数据非常相似。
我记得在两个进程之间拉一个类似的特技,将一个函数从一个程序复制到共享内存,然后让另一个程序从共享内存中执行该函数。这是大约四分之一世纪以前的事情,但这种技术与它所尝试的机器相似并且“有效”。我从来不需要使用这种技术,谢天谢地!
答案 1 :(得分:5)
此代码使用未初始化的变量_main
和_Demo
,因此它无法正常工作。即使它们意味着不同的东西,它们也可能假设在内存中有一些特定的函数排序。
我的意见:不要相信这篇文章。