操作系统:Windows XP 64位,SP2。
我有一个不寻常的问题。 我将一些代码从32位移植到64位。 32位代码工作得很好。 但是当我为64位版本调用CreateThread()时,调用失败。 我有三个地方失败。 2调用CreateThread()。 1调用了调用CreateThread()的beginthreadex()。
所有三个调用失败,错误代码为0x3E6,“访问内存位置无效”。
问题是所有的input参数都是正确的。
HANDLE h; DWORD threadID; h = CreateThread(0, // default security 0, // default stack size myThreadFunc, // valid function to call myParam, // my param 0, // no flags, start thread immediately &threadID);
所有这三个对CreateThread()的调用都是由我在程序执行开始时注入到目标程序中的一个DLL(在程序到达main()/ WinMain()之前)完成的。 如果我通过说一个菜单从目标程序(相同的参数)调用CreateThread(),它就可以工作。 相同的参数等exception。
如果我传递NULL而不是&threadID,它仍然失败。
如果我作为myParam传递NULL,它仍然失败。
我不从DllMain()中调用CreateThread,所以这不是问题。 我很困惑,search谷歌等没有显示任何相关的答案。
如果有人以前见过或有任何想法,请让我知道。
谢谢阅读。
回答
简短的回答:x64上的堆栈帧需要16字节alignment。
较长的回答:在我的头撞在debugging器墙上,并发布对各种build议的回应(所有这些都有所帮助,促使我尝试新的方向)之后,我开始探索如果在调用CreateThread ()。 这certificate是一个红鲱鱼,但它确实导致了解决scheme。
向堆栈中添加额外的数据将改变堆栈帧alignment。 迟早会有一个testing让你得到16字节的堆栈帧alignment。 在这一点上的代码工作。 所以我回顾了我的步骤,并开始将NULL数据放入堆栈,而不是我认为是正确的值(我一直在推回传地址来伪造呼叫帧)。 它仍然工作 – 所以数据并不重要,它必须是实际的堆栈地址。
我很快意识到这是堆栈的16字节alignment方式。 以前我只知道8字节alignment的数据。 这个微软文件解释了所有的alignment要求 。
如果堆栈帧在x64上不是16字节alignment,则编译器可能会在将数据压入堆栈时将大的(8字节或更多)数据放在错误的alignment边界上。
因此,我面临的问题 – 钩子代码被调用堆栈,没有alignment在16字节的边界。
alignment要求的快速总结,表示为大小:alignment
大于8个字节的任何东西都与2个边界的下一个幂alignment。
我认为微软的错误代码有点误导。 最初的STATUS_DATATYPE_MISALIGNMENT可以表示为STATUS_STACK_MISALIGNMENT,这会更有帮助。 但是,然后把STATUS_DATATYPE_MISALIGNMENT变成ERROR_NOACCESS – 这实际上是伪装和误导了什么问题。 非常无益。
感谢大家提出的build议。 即使我不同意这些build议,也促使我在各种各样的方向进行testing(包括我不同意的方面)。
在这里写下更详细的数据types错位问题描述: 64位移植gotcha#1! x64数据types错位。
64位的唯一原因是64位的线程需要64位对齐的值。 如果threadID不是64位对齐,则可能导致此问题。
好吧,那个主意不是这样。 你确定在main / WinMain之前调用CreateThread是有效的吗? 这将解释为什么它在菜单中工作 – 因为这是在主/ WinMain之后。
另外,我会三重检查myParam的生命周期。 在你传入的函数被调用之前,CreateThread返回(我从经验中知道)。
发布线程例程的代码(或只是几行)。
它突然发生在我身上:你确定你正在注入你的64位代码到一个64位进程? 因为如果你有一个64位的CreateThread调用,并试图将其注入在WOW64下运行的32位进程,可能会发生坏事。
开始认真地用完想法。 编译器是否报告了任何警告?
错误是由于宿主程序中的错误,而不是DLL? 还有一些其他的代码,比如在main / WinMain之前使用了__declspec(import / export)加载DLL。 例如,如果该 DLLMain有一个bug。
尝试使用_beginthread()或_beginthreadex(),而不是直接使用CreateThread。
看到这个前面的问题。