幻想森林

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2985|回复: 9

[通用编程] strchr和strstr

[复制链接]

23

主题

218

帖子

2470

积分

⑥精研

积分
2470
发表于 2007-8-29 21:32:59 | 显示全部楼层 |阅读模式
突然发现strchr和strstr系列的函数接受const char *的参数却返回char *,这是不是意味着把const指针给HACK了,可以读写了?

假设我通过这些函数查找一个const的字符串缓冲,然后通过返回的指针写入该缓冲,会发生什么事??
ONScripter for PSP/Windows中文版 http://blog.163.com/john_he_
回复

使用道具 举报

19

主题

842

帖子

1万

积分

⑧专业

絕望青年,一起增高吧

积分
13676
发表于 2007-8-29 22:01:28 | 显示全部楼层
接受const char*只不過是輸入唯讀,沒有人說const char*和 char*是相同的說
可能有這種結構
char* aaa(const char* input){
char* buffer = input;
//or
char* buffer;
buffer = copy_string(input);
//process...
return buffer;
}
這不就行嘛?

為著彼岸,便要與之妥協 但為著彼岸,更不能與之妥協

回复 支持 反对

使用道具 举报

23

主题

218

帖子

2470

积分

⑥精研

积分
2470
 楼主| 发表于 2007-8-29 22:51:40 | 显示全部楼层
问题是一般编译器是不允许const xxx*向xxx*转换的,只允许xxx*向const xxx*转换(xxx*转换成const xxx*表示放弃写权限),所以
  1. char* aaa(const char* input){
  2. char* buffer = input;
复制代码
编译是过不了的。
  1. char* buffer;
  2. buffer = copy_string(input);
  3. //process...
  4. return buffer;
复制代码
这个倒是可能,但是strchr所得的指针是可以通过减运算得到与原指针相差的字符数的。
  1. const char *str = "This is a string.";
  2. char *pc = strchr(str,'h');
  3. return pc-str;
复制代码
这样是会返回1的,以前试过了。所以strchr应该不是使用复制缓冲区的方法实现,应该就是HACK了。
ONScripter for PSP/Windows中文版 http://blog.163.com/john_he_
回复 支持 反对

使用道具 举报

19

主题

842

帖子

1万

积分

⑧专业

絕望青年,一起增高吧

积分
13676
发表于 2007-8-30 00:44:52 | 显示全部楼层
這個看來在486時代已經有定案了,找出一個asm-i486版本的string.h
查找了strchr, strstr依然是extern的,找不到

static inline char * strchr(const char * s, int c)
{
int d0;
register char * __res;
__asm__ __volatile__(
    "movb %%al,%%ah\\n"
    "1:\\tlodsb\\n\\t"
    "cmpb %%ah,%%al\\n\\t"
    "je 2f\\n\\t"
    "testb %%al,%%al\\n\\t"
    "jne 1b\\n\\t"
    "movl $1,%1\\n"
    "2:\\tmovl %1,%0\\n\\t"
    "decl %0"
    :"=a" (__res), "=&S" (d0)
    :"1" (s),"0" (c)
    :"memory");
return __res;
}
某還在解譯中

為著彼岸,便要與之妥協 但為著彼岸,更不能與之妥協

回复 支持 反对

使用道具 举报

50

主题

994

帖子

6699

积分

管理员

爱干啥干啥!

Rank: 9Rank: 9Rank: 9

积分
6699
发表于 2007-8-30 00:50:53 | 显示全部楼层
HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

strstr这种接受了const char*返回char*是有点恶搞哦-_-b

“放下屠刀,立地成佛” 故应先杀生,然后再成佛。

(\\_/) (-_-) ()+() this is bunny priest.
回复 支持 反对

使用道具 举报

19

主题

842

帖子

1万

积分

⑧专业

絕望青年,一起增高吧

积分
13676
发表于 2007-8-30 00:55:48 | 显示全部楼层
投降了!某對GCC的內部絕望了,找來了MSVC 6 的strstr asm內解讀,看來一切已經超過了C
有注解好多了,一直只是使用不覺得有啥問題,給LZ一問呆了...
  1.         page    ,132
  2.         title   strstr - search for one string inside another
  3. ;***
  4. ;strstr.asm - search for one string inside another
  5. ;
  6. ;       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
  7. ;
  8. ;Purpose:
  9. ;       defines strstr() - search for one string inside another
  10. ;
  11. ;*******************************************************************************
  12.         .xlist
  13.         include cruntime.inc
  14.         .list
  15. page
  16. ;***
  17. ;char *strstr(str1, str2) - search for str2 in str1
  18. ;
  19. ;Purpose:
  20. ;       finds the first occurrence of str2 in str1
  21. ;
  22. ;Entry:
  23. ;       char *str1 - string to search in
  24. ;       char *str2 - string to search for
  25. ;
  26. ;Exit:
  27. ;       returns a pointer to the first occurrence of string2 in
  28. ;       string1, or NULL if string2 does not occur in string1
  29. ;
  30. ;Uses:
  31. ;
  32. ;Exceptions:
  33. ;
  34. ;*******************************************************************************
  35. __from_strstr_to_strchr proto
  36.         CODESEG
  37.         public  strstr
  38. strstr  proc
  39.         mov     ecx,[esp + 8]       ; str2 (the string to be searched for)
  40.         push    edi                 ; Preserve edi, ebx and esi
  41.         push    ebx
  42.         push    esi
  43.         mov     dl,[ecx]            ; dl contains first char from str2
  44.         mov     edi,[esp + 10h]     ; str1 (the string to be searched)
  45.         test    dl,dl               ; is str2 empty?
  46.         jz      empty_str2
  47.         mov     dh,[ecx + 1]        ; second char from str2
  48.         test    dh,dh               ; is str2 a one-character string?
  49.         jz      strchr_call         ; if so, go use strchr code
  50. ; length of str2 is now known to be > 1 (used later)
  51. ; dl contains first char from str2
  52. ; dh contains second char from str2
  53. ; edi holds str1
  54. findnext:
  55.         mov     esi,edi             ; esi = edi = pointers to somewhere in str1
  56.         mov     ecx,[esp + 14h]     ; str2
  57. ;use edi instead of esi to eliminate AGI
  58.         mov     al,[edi]            ; al is next char from str1
  59.         inc     esi                 ; increment pointer into str1
  60.         cmp     al,dl
  61.         je      first_char_found
  62.         test    al,al               ; end of str1?
  63.         jz      not_found           ; yes, and no match has been found
  64. loop_start:
  65.         mov     al,[esi]            ; put next char from str1 into al
  66.         inc     esi                 ; increment pointer in str1
  67. in_loop:
  68.         cmp     al,dl
  69.         je      first_char_found
  70.         test    al,al               ; end of str1?
  71.         jnz     loop_start          ; no, go get another char from str1
  72. not_found:
  73.         pop     esi
  74.         pop     ebx
  75.         pop     edi
  76.         xor     eax,eax
  77.         ret
  78. ; recall that dh contains the second char from str2
  79. first_char_found:
  80.         mov     al,[esi]            ; put next char from str1 into al
  81.         inc     esi
  82.         cmp     al,dh               ; compare second chars
  83.         jnz     in_loop             ; no match, continue search
  84. two_first_chars_equal:
  85.         lea     edi,[esi - 1]       ; store position of last read char in str1
  86. compare_loop:
  87.         mov     ah,[ecx + 2]        ; put next char from str2 into ah
  88.         test    ah,ah               ; end of str2?
  89.         jz      match               ; if so, then a match has been found
  90.         mov     al,[esi]            ; get next char from str1
  91.         add     esi,2               ; bump pointer into str1 by 2
  92.         cmp     al,ah               ; are chars from str1 and str2 equal?
  93.         jne     findnext            ; no
  94. ; do one more iteration
  95.         mov     al,[ecx + 3]        ; put the next char from str2 into al
  96.         test    al,al               ; end of str2
  97.         jz      match               ; if so, then a match has been found
  98.         mov     ah,[esi - 1]        ; get next char from str1
  99.         add     ecx,2               ; bump pointer in str1 by 2
  100.         cmp     al,ah               ; are chars from str1 and str2 equal?
  101.         je      compare_loop
  102. ; no match. test some more chars (to improve execution time for bad strings).
  103.         jmp     findnext
  104. ; str2 string contains only one character so it's like the strchr functioin
  105. strchr_call:
  106.         xor     eax,eax
  107.         pop     esi
  108.         pop     ebx
  109.         pop     edi
  110.         mov     al,dl
  111.         jmp     __from_strstr_to_strchr
  112. ;
  113. ;
  114. ; Match!  Return (ebx - 1)
  115. ;
  116. match:
  117.         lea     eax,[edi - 1]
  118.         pop     esi
  119.         pop     ebx
  120.         pop     edi
  121.         ret
  122. empty_str2:           ; empty target string, return src (ANSI mandated)
  123.         mov     eax,edi
  124.         pop     esi
  125.         pop     ebx
  126.         pop     edi
  127.         ret
  128. strstr  endp
  129.         end
复制代码

為著彼岸,便要與之妥協 但為著彼岸,更不能與之妥協

回复 支持 反对

使用道具 举报

19

主题

842

帖子

1万

积分

⑧专业

絕望青年,一起增高吧

积分
13676
发表于 2007-8-30 01:10:40 | 显示全部楼层
某的解譯大約昰這樣

mov    edi,[esp + 10h]    ; str1 (the string to be searched)

edi是內部次理用的存址,[esp + 10h]是str1
基本上在這個mov之後,[esp + 10h]便沒有被存取
也就是說原地址(esp + 10h),只有讀的份,便滿足const協定

edi在程序中是有讀寫部份
    lea    edi,[esi - 1]
讀不寫,自己找吧

而傳回值eax相關指令部份
xor    eax,eax 只是把xor    eax,eax清0,不多說
lea    eax,[edi - 1]
mov    eax,edi
這兩個也只是讀取edi,其實便是包了把結果經bufer存到edi,再傳回eax

也就是
const char*               char*
[esp+...] >>> edi >>> eax

這感覺

換句話,用C/C++語法就是
char* aaa(const char* input){
char* buffer = input;
//process...
return buffer;
}
的structure

為著彼岸,便要與之妥協 但為著彼岸,更不能與之妥協

回复 支持 反对

使用道具 举报

19

主题

842

帖子

1万

积分

⑧专业

絕望青年,一起增高吧

积分
13676
发表于 2007-8-30 01:11:57 | 显示全部楼层
引用第4楼FantasyDR于2007-08-30 00:50发表的  :
HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

strstr这种接受了const char*返回char*是有点恶搞哦-_-b

的確是協定,但問題是在於這個協定是如何做到,上面某想是最完整的解釋了。。。

為著彼岸,便要與之妥協 但為著彼岸,更不能與之妥協

回复 支持 反对

使用道具 举报

23

主题

218

帖子

2470

积分

⑥精研

积分
2470
 楼主| 发表于 2007-8-30 11:28:11 | 显示全部楼层
引用第4楼FantasyDR于2007-08-30 00:50发表的  :
HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

strstr这种接受了const char*返回char*是有点恶搞哦-_-b


记得强制转换也是不行的,以前试过了。不过可以先转成unsigned int再转,所以也不成问题……
HACK是指把本来不允许的变允许了,这个用词纯粹道听途说……

我也知道这个函数是汇编写的,汇编是没有const这概念,所以绝对可行。


经过测试,也参照coolpay64的代码,strchr只是把const char*偷换成char *罢了,内存页的属性还是没有变的,用只读内存的指针调用strchr,返回的指针还是不能写,不过不是编译器提醒,而是直接runtime error。如果用读写内存指针调用就相安无事。
  1. const char *str = "This is test.";          /* str指向程序的字符串池,只读内存 */
  2. char *pc = strchr(str, 'h');
  3. *pc = 0;      /* 访问违规 */
  4. const char str[] = "This is test.";        /* str指向系统栈,读写内存 */
  5. char *pc = strchr(str, 'h');
  6. *pc = 0;      /* 通过,产生预期结果 */
复制代码

我的理解是C不支持函数重载,所以返回char *以保证兼容const char *和char *,返回的指针能怎么用还要靠自我认识。
ONScripter for PSP/Windows中文版 http://blog.163.com/john_he_
回复 支持 反对

使用道具 举报

50

主题

742

帖子

402

积分

版主

自定义头衔

Rank: 7Rank: 7Rank: 7

积分
402
发表于 2007-8-30 22:38:21 | 显示全部楼层
这些貌似在新版本的编译器上面有部分改动了……
偶印象中这样的,因为在编译的时候会有二个版本出现:
const char* + const char*
char* + char*
但忘了是不是给这些函数用的………………
总之C下面const可以是无视掉的XD
Style-C
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|幻想森林

GMT+8, 2024-4-30 01:48 , Processed in 0.023934 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表