Closed BlueArc0 closed 7 months ago
测试编译器:x86-64 gcc 13.2,x86-64 clang 17.0.1,MinGW gcc 13.2.0,x64 msvc v19.37.32824
适配编译器适配的头大>_<
call_todo
函数将该函数的返回地址(调用该函数时call
指令压入栈中的地址)传递给todo
,todo
从该地址开始搜索以8B
开头的mov
指令,并计算出ss::a
的地址(ss::a
的引用即[i+[i+2]+6]
,其中i
是该条mov
指令的开始位置,[i+2]
是mov
指令中存储的偏移地址,6
是该条指令的长度)。
由于std::cout << ss::a << '\n';
代码读取了ss::a
,我就希望从这段代码入手获取ss::a
的地址,经过测试发现,读取ss::a
的通常是一条以8B
开头的mov
指令,于是就有了todo
函数;
call_todo
是为了避免CWG2811中不得使用main
函数的规定,通过返回地址间接获取到main
函数中mov
指令前的地址,其中函数参数void*
、局部变量void* p2
是用来对齐不同编译器栈中内存布局不同,此时*(&p+3)
即该函数的返回地址。
由于编译器不一定把读取ss::a
编译成以8B
开头的mov
指令,以及由于栈中内存布局不同而导致*(&p+3)
并不是函数的返回地址,所以这段代码不支持所有编译器,以下是测试能够正确运行的编译器:
x86-64 gcc 13.2,x86-64 clang 17.0.1,MinGW gcc 13.2.0,x64 msvc v19.37.32824
最后把那个0xCC_b改成0xC3_b是因为在gcc上函数结束使用nop(90)
而不是msvc的int 3(CC)
,所以改成了ret(C3)
。这是失败路径防止意外修改的代码。
思路挺个性,但你最好把这玩意儿写项目里去
我其实根本不懂这写法,不过看起来你写的很不错。
call_todo
函数将该函数的返回地址(调用该函数时call
指令压入栈中的地址)传递给todo
,todo
从该地址开始搜索以8B
开头的mov
指令,并计算出ss::a
的地址(ss::a
的引用即[i+[i+2]+6]
,其中i
是该条mov
指令的开始位置,[i+2]
是mov
指令中存储的偏移地址,6
是该条指令的长度)。 由于std::cout << ss::a << '\n';
代码读取了ss::a
,我就希望从这段代码入手获取ss::a
的地址,经过测试发现,读取ss::a
的通常是一条以8B
开头的mov
指令,于是就有了todo
函数;call_todo
是为了避免CWG2811中不得使用main
函数的规定,通过返回地址间接获取到main
函数中mov
指令前的地址,其中函数参数void*
、局部变量void* p2
是用来对齐不同编译器栈中内存布局不同,此时*(&p+3)
即该函数的返回地址。 由于编译器不一定把读取ss::a
编译成以8B
开头的mov
指令,以及由于栈中内存布局不同而导致*(&p+3)
并不是函数的返回地址,所以这段代码不支持所有编译器,以下是测试能够正确运行的编译器: x86-64 gcc 13.2,x86-64 clang 17.1.0,MinGW gcc 13.2.0,x64 msvc v19.37.32824
Markdown 别写一坨,稍微列举分类一下,我看着都麻烦。
等我整理一下注释,没想好怎么塞代码里
在 WSL 和虚拟机上测试未通过,系统均为 Ubuntu22.04 LTS ,编译器为 G++11.4
(gdb) p &p
$1 = (void **) 0x7fffffffdb98
(gdb) x /10x &p
0x7fffffffdb98: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffdba8: 0xe7777300 0xc38ba66e 0xffffdbc0 0x00007fff
0x7fffffffdbb8: 0x55555305 0x00005555
(gdb) p main
$2 = {int (void)} 0x5555555552d5 <main()>
(gdb)
由于整个仓库的 git history 被改写,请更新 fork(rebase)。
在 WSL 和虚拟机上测试未通过,系统均为 Ubuntu22.04 LTS ,编译器为 G++11.4
(gdb) p &p $1 = (void **) 0x7fffffffdb98 (gdb) x /10x &p 0x7fffffffdb98: 0x00000000 0x00000000 0x00000000 0x00000000 0x7fffffffdba8: 0xe7777300 0xc38ba66e 0xffffdbc0 0x00007fff 0x7fffffffdbb8: 0x55555305 0x00005555 (gdb) p main $2 = {int (void)} 0x5555555552d5 <main()> (gdb)
看起来把*(&p+3)
改成*(&p+4)
就能解决,这个位置确实挺玄学的;或者直接使用todo(reinterpret_cast<std::byte*>(main));
而不是call_todo(nullptr);
@rsp4jack 我其实很好奇,你改写了啥,让这个 pr 直接有了 701 个提交。
@Mq-b 从整个 git history 里删了一些很大的文件,然后从上传那个文件的 commit 到 HEAD 之间的所有 commit 都被修改了。
哦,,,你直接删了 Git 的历史是吧。
^_^