免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: 思一克
打印 上一主题 下一主题

一个所谓的全局变量地址改变的例子 [复制链接]

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
31 [报告]
发表于 2010-01-30 17:24 |只看该作者

回复 #31 swordfish.cn 的帖子

原帖由 swordfish.cn 于 2010-1-30 17:06 发表
下面你不是已经定义了一个变量top了吗?再用一个anchor是不是有点多余呢?


top <- 是在这里?
RA  <- 进入函数时的esp
P0

top <- 还是在这里?
pad
RA  <- 进入函数时的esp
P0


需要修改的是return address, 它和top之间的offset是未知的。 和编译器编译选项都有关系。

而return address 和参数之间的offset就要稳定得多, 虽然依旧没有标准保证是多少, 但肯定比使用局部变量定位要更准确。


原帖由 swordfish.cn 于 2010-1-30 17:06 发表
我比较在意代码的紧凑

翻译的结果是否高效与正确, 同源代码是否紧凑没有必然联系。
编译器不是傻瓜。

同时, 我觉得即使是C语言, 代码的首要功能也是给人读。


原帖由 swordfish.cn 于 2010-1-30 17:06 发表
在Linux下面,如何使用mmap来提权?


不好意思……   写错了, 是mprotect ……

论坛徽章:
0
32 [报告]
发表于 2010-01-30 17:29 |只看该作者
翻译的结果是否高效与正确, 同源代码是否紧凑没有必然联系。
编译器不是傻瓜。

同时, 我觉得即使是C语言, 代码的首要功能也是给人读。


那就怪我原来没有说明白吧,我的意思是想在写尽量少的代码的时候,实现功能。原来的代码,可能有需要加上stack相关的编译参数才可以实现。

很明显,这是一个编译相关的问题。我想搞清楚这一点,请问你的写法,是否能保证在stack相关选项变化的时候,得到一样的效果?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
33 [报告]
发表于 2010-01-30 17:29 |只看该作者

回复 #32 OwnWaterloo 的帖子

要改写printf的实现, 还需要注意的是 (是Windows下的dll机制的特有问题, 可能so没有)

&printf指向的并不一定是printf真正的代码。 而只是一个函数指针。
可能需要这样, 覆盖的才是printf的代码。
memcpy( *(void**)&printf, dst, size );

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
34 [报告]
发表于 2010-01-30 17:33 |只看该作者

回复 #33 swordfish.cn 的帖子

原帖由 swordfish.cn 于 2010-1-30 17:29 发表
是否能保证在stack相关选项变化的时候,得到一样的效果?


当然是不能保证的。 C标准里几乎没怎么提stack-frame, 好像只有一个function image还是什么的。
也没说参数一定是由栈传入。


如果将讨论限制到i386下的cdecl调用约定, 你觉得呢? 返回地址和第0个参数之间的offset是多少?

论坛徽章:
0
35 [报告]
发表于 2010-01-30 17:42 |只看该作者
原帖由 OwnWaterloo 于 2010-1-30 17:33 发表


当然是不能保证的。 C标准里几乎没怎么提stack-frame, 好像只有一个function image还是什么的。
也没说参数一定是由栈传入。


如果将讨论限制到i386下的cdecl调用约定, 你觉得呢? 返回地址和第0个参 ...


是我的话,我就直接加编译参数了。所以这个offset是可计算的。所以,回到我的出发点,我就觉得anchor有点多余了。

发现整天对着计算机,要和人解释清楚自己的想法还真不是一般的难啊。

另,我在Linux试了一下,即使是mprotect改了权限,也不能把printf改写,怎么办?


  1. unsigned long page = ((unsigned long)printf >> 12) << 12;
  2. unsigned long offset = ((unsigned long)printf) % 4096;

  3. mprotect(page, 4096, PROT_WRITE|PROT_READ);

  4. memcpy((void*)printf, "12345", 5);
复制代码


会segfault的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
36 [报告]
发表于 2010-01-30 17:43 |只看该作者

回复 #31 swordfish.cn 的帖子

原帖由 swordfish.cn 于 2010-1-30 17:06 发表
请问这是由于“溢出”而导致的改写吗?


看这个:
void f(void) {
      void* a[] = { xxx, yyy, };
      memcpy( a[2], dst, size );
}

谁知道a[2] 指向哪呢?  万一那个地址真的被VirtualProtect或者mprotect打开了写权限呢?

并且, 我提出这种假设, 是因为:
1. 我觉得23楼的代码并不算是直接调用printf
用这个去解释以前那个贴里的问题并不妥当。

2. 那个贴里说的是调用printf得到的结果不同, 而不是返回地址被修改了, 无意间跳转到printf中
无论栈被怎么破坏了, 无论哪存在溢出 —— 只要没伤及printf的代码, 只要链接没出问题, 只要&gi没被宏替换什么的 —— 我想不出其他理由会使得printf("%p\n", (void*)&gi ); 输出不同的地址。

反之, 既然输出的不同的地址, 那可能就是上面的一些前提被破坏了, 比如printf的代码被改写了。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
37 [报告]
发表于 2010-01-30 18:13 |只看该作者

回复 #36 swordfish.cn 的帖子

原帖由 swordfish.cn 于 2010-1-30 17:42 发表
是我的话,我就直接加编译参数了。所以这个offset是可计算的。所以,回到我的出发点,我就觉得anchor有点多余了。
发现整天对着计算机,要和人解释清楚自己的想法还真不是一般的难啊。


嘿, 我也有同感。 附加一个 : 要理解别人的想法还真不是一般的难。

难道你觉得让代码的行为依赖于编译参数是很好的做法? 你依然觉得anchor多余?
这是你的审美观, 我不作评价。

该说的我都说了, 我不觉得anchor是多余的。



原帖由 swordfish.cn 于 2010-1-30 17:42 发表
另,我在Linux试了一下,即使是mprotect改了权限,也不能把printf改写,怎么办?

unsigned long page = ((unsigned long)printf >> 12) << 12;
unsigned long offset = ((unsigned long)printf) % 4096;

mprotect(page, 4096, PROT_WRITE|PROT_READ);

memcpy((void*)printf, "12345", 5);

会segfault的。


mprotect首个参数的类型是long???

你可以先检查一下mprotect的返回值。  看是否执行成功。
而且, 你将printf的执行权限给取消了。


另外, 附一段代码, Windows下的, msvc8 和mingw4.4.0 通过。


#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <windows.h>

void f(const char* s) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts("f");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts(s);
}

void forwarding2f(char* function)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;long offset = (long)&f - (long)function - 5;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned char jmp = 0xe9;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWORD old = 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;VirtualProtect(function, 5, PAGE_EXECUTE_READWRITE, &old);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(function, &jmp, 1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(function+1, &offset, sizeof offset);
}

void g(const char* s) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts("g");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts(s);
}

int main(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forwarding2f( (char*)&g );
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g("hello");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forwarding2f( (char*)&printf );
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("printf");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
}


输出 :
f
hello
f
printf

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
38 [报告]
发表于 2010-01-30 18:29 |只看该作者

回复 #38 OwnWaterloo 的帖子

上面的代码, msvc如果添加优化选项, 会对g进行inline展开, 所以修改了g的代码也没用。
故将代码改为如下形式 :

#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <windows.h>

int f(char const* s, ...)
{
      puts("in f");
      puts(s);
      return 0;
}

void forwarding(unsigned char* src, long dst)
{
      long offset = dst - (long)src - 5;
      DWORD old = 0;
      VirtualProtect(src, 5, PAGE_EXECUTE_READWRITE, &old);
      src[0] = 0xe9;
      memcpy(src+1, &offset, sizeof offset);
}

int g(char const* s, ...)
{
      puts("in g");
      puts(s);
      return 0;
}

int main(void)
{
      int (* volatile disable_inline)(char const* , ...);

      forwarding( (void*)&g, (long)&f );
      disable_inline = g;
      disable_inline("call g");

      forwarding( (void*)&printf, (long)&f );
      disable_inline = printf;
      disable_inline("call printf");

      return 0;
}


还需要注意的是, msvc不要链接到动态库, 否则&printf实际上只是一个函数指针的地址。

使用编译参数:
cl /W3 /O2
gcc -Wall -O2


输出:
in f
call g
in f
call printf

[ 本帖最后由 OwnWaterloo 于 2010-1-30 18:30 编辑 ]

论坛徽章:
0
39 [报告]
发表于 2010-01-30 18:37 |只看该作者
难道你觉得让代码的行为依赖于编译参数是很好的做法? 你依然觉得anchor多余?


“让代码的行为依赖于编译参数”不是好办法。但是在这个例子的情况下,我认为是最好的选择。既然anchor也无法完全确保程序的行为,我也就不再需要它了。当然,这是个别情况。


mprotect首个参数的类型是long???

你可以先检查一下mprotect的返回值。  看是否执行成功。
而且, 你将printf的执行权限给取消了。


实际调用的时候做了转换,而且执行也成功了(返回0)。现在尚未到执行一步,直接在memcpy就segfault了。

Windows的代码就没办法测试了,没有环境。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
40 [报告]
发表于 2010-01-30 18:54 |只看该作者

回复 #40 swordfish.cn 的帖子

原帖由 swordfish.cn 于 2010-1-30 18:37 发表
不是好办法。但是在这个例子的情况下,我认为是最好的选择。既然anchor也无法完全确保程序的行为,我也就不再需要它了。当然,这是个别情况。


anchor 能保证的情况比不使用anchor要多得多
只要是i386下的cdecl调用约定, stdcall也可以。 fastcall不行。
而不使用anchor, 即使在上面那个范围内, 依然需要依赖编译器参数。

算了, 你继续固执吧, 我没兴趣说服你了。

原帖由 swordfish.cn 于 2010-1-30 18:37 发表
实际调用的时候做了转换,而且执行也成功了(返回0)。现在尚未到执行一步,直接在memcpy就segfault了。
Windows的代码就没办法测试了,没有环境。


我有xnix环境。 同样, 没兴趣换过去测了。

对牛弹琴。

[ 本帖最后由 OwnWaterloo 于 2010-1-30 18:57 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP