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

[通用编程] strchr和strstr

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

假设我通过这些函数查找一个const的字符串缓冲,然后通过返回的指针写入该缓冲,会发生什么事??
发表于 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;
}
這不就行嘛?
 楼主| 发表于 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了。
发表于 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;
}
某還在解譯中
发表于 2007-8-30 00:50:53 | 显示全部楼层
HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

strstr这种接受了const char*返回char*是有点恶搞哦-_-b
发表于 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
复制代码
发表于 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
发表于 2007-8-30 01:11:57 | 显示全部楼层
引用第4楼FantasyDR于2007-08-30 00:50发表的  :
HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

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

的確是協定,但問題是在於這個協定是如何做到,上面某想是最完整的解釋了。。。
 楼主| 发表于 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 *,返回的指针能怎么用还要靠自我认识。
发表于 2007-8-30 22:38:21 | 显示全部楼层
这些貌似在新版本的编译器上面有部分改动了……
偶印象中这样的,因为在编译的时候会有二个版本出现:
const char* + const char*
char* + char*
但忘了是不是给这些函数用的………………
总之C下面const可以是无视掉的XD
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2026-11-6 18:26 , Processed in 0.033748 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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