利用scanf函数修改内存中任意位置内容

    Scanf函数是常用的函数,它的作用一般认为是让用户给变量赋值。使用方法一般是scanf(“%d”, &num) 第二个参数是变量的地址。如果第二个参数不是变量的地址,而是变量本身,那么程序就会报错了。实际上,如果第二个参数是变量本身,程序也不一定报错,报错与否取决于这个变量的大小。

    透过现象看本质,scanf函数的作用其实是:把用户输入的字符格式化到指定格式,并输出到指定内存。事实上第二个参数可以是任意一个地址,而且我们可以直接指定它。

    如果第二个参数是一个变量本身,那么程序的报错与否,取决于变量的值所代表的那段内存地址空间是否属于Ring3,换句话说,也就是取决于这块内存是否属于用户可以编辑的内存区域。

    先来看一个会报错的情况。

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     int num = 6;
 6 
 7     printf("input num.\r\n");
 8     scanf("%d", num);
 9 
10     printf("input finished.\r\n");
11     printf("num's value is %d.\r\n", num);
12 
13     system("pause");
14 
15     return 0;
16 }

    程序第8行,scanf(“%d”, num)的第二个参数是整形变量num的自身,按常理来说是不对的,如果想把用户输入赋值给num,第二个参数应该是&num才对。编译运行后,果然报错。

    现在我们改变示例程序,尝试突破第二个参数不能为变量自身的这个认识。我们把num的初始值修改为了74565(十六进制的12345)。再次编译运行。

    这次竟然没有报错。用户成功输入了8。当然这个8并不是赋值给num了。可以看到num的值依然是74565。利用WinHex查看进程的内存,我们应该会在0x00012345开始的4字节内存空间内,发现以小尾方式存储的8,也就是08 00 00 00

 

    这证明了scanf函数的本质:把用户输入的字符格式化到指定格式,并输出到指定内存。第一个示例程序之所以报错,是因为num的初始值6,转化为十六进制地址0x00000006,是属于系统占用的内存区域,尝试修改这一区域的内存所以报错。0x000000000x0000FFFF这片区域是系统的领空,一旦num的值大于这个范围,程序便不会报错了(当然也不能太大,大于用户自身可操作的内存范围0x0001000-0x7FFF0000还是会报错)。虽然不会报错,但是程序运行的结果却并不是把用户的输入赋值给num,而是修改了内存中某个位置的内容,所以用起来要小心。

  当然也可以直接制定要修改的内存位置。示例程序如下所示,我们想要改变虚拟内存中起始地址为0x00012345,占4个字节空间(int型数值)的地址:

 

#include <stdio.h>

int main()
{
    printf("Check target RAM (00012345) status.\r\n");
    scanf("%d", 0x00012345);
printf(
"Check again.\r\n"); system("pause"); return 0; }

 

    编译运行后,首先用WinHex查看当前0x00012345处的内存内容。

 

    此时0x00012345处全是00

 

  输入任意整形数据,尝试把数据写入0x00012345起始的这片4字节区域。

 

    输入的1234567890转换成十六进制是499602D2。小尾形式是D2 02 96 49。查看内存,这段地址果然发生了改变。

  这种对scanf函数的利用方法,可以修改虚拟内存中任意位置的数据。对内存结构熟悉的话,可以用scanf函数偷偷修改一些东西。

 

posted @ 2016-09-29 07:32  zhugehq  阅读(1332)  评论(0编辑  收藏  举报