john_he 发表于 2007-8-29 21:32:59

strchr和strstr

突然发现strchr和strstr系列的函数接受const char *的参数却返回char *,这是不是意味着把const指针给HACK了,可以读写了?

假设我通过这些函数查找一个const的字符串缓冲,然后通过返回的指针写入该缓冲,会发生什么事??

coolpay64 发表于 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;
}
這不就行嘛?

john_he 发表于 2007-8-29 22:51:40

问题是一般编译器是不允许const xxx*向xxx*转换的,只允许xxx*向const xxx*转换(xxx*转换成const xxx*表示放弃写权限),所以
char* aaa(const char* input){
char* buffer = input;
编译是过不了的。

char* buffer;
buffer = copy_string(input);
//process...
return buffer;
这个倒是可能,但是strchr所得的指针是可以通过减运算得到与原指针相差的字符数的。
const char *str = "This is a string.";
char *pc = strchr(str,'h');
return pc-str;
这样是会返回1的,以前试过了。所以strchr应该不是使用复制缓冲区的方法实现,应该就是HACK了。

coolpay64 发表于 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;
}
某還在解譯中

FantasyDR 发表于 2007-8-30 00:50:53

HACK指得是啥操作?
那个,编译不过可以写强制类型转换……const这种玩意儿只是编译期的约定吧,编译器保证标记了const的在函数体内不被改写,其实硬要改写也没办法,编译器还是很好骗的。

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

coolpay64 发表于 2007-8-30 00:55:48

投降了!某對GCC的內部絕望了,找來了MSVC 6 的strstr asm內解讀,看來一切已經超過了C
有注解好多了,一直只是使用不覺得有啥問題,給LZ一問呆了...

      page    ,132

      title   strstr - search for one string inside another

;***

;strstr.asm - search for one string inside another

;

;       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.

;

;Purpose:

;       defines strstr() - search for one string inside another

;

;*******************************************************************************



      .xlist

      include cruntime.inc

      .list



page

;***

;char *strstr(str1, str2) - search for str2 in str1

;

;Purpose:

;       finds the first occurrence of str2 in str1

;

;Entry:

;       char *str1 - string to search in

;       char *str2 - string to search for

;

;Exit:

;       returns a pointer to the first occurrence of string2 in

;       string1, or NULL if string2 does not occur in string1

;

;Uses:

;

;Exceptions:

;

;*******************************************************************************





__from_strstr_to_strchr proto



      CODESEG



      publicstrstr



strstrproc



      mov   ecx,       ; str2 (the string to be searched for)



      push    edi               ; Preserve edi, ebx and esi

      push    ebx

      push    esi



      mov   dl,            ; dl contains first char from str2



      mov   edi,   ; str1 (the string to be searched)



      test    dl,dl               ; is str2 empty?

      jz      empty_str2



      mov   dh,      ; second char from str2

      test    dh,dh               ; is str2 a one-character string?

      jz      strchr_call         ; if so, go use strchr code



; length of str2 is now known to be > 1 (used later)

; dl contains first char from str2

; dh contains second char from str2

; edi holds str1



findnext:

      mov   esi,edi             ; esi = edi = pointers to somewhere in str1

      mov   ecx,   ; str2



;use edi instead of esi to eliminate AGI

      mov   al,            ; al is next char from str1



      inc   esi               ; increment pointer into str1



      cmp   al,dl

      je      first_char_found



      test    al,al               ; end of str1?

      jz      not_found         ; yes, and no match has been found



loop_start:

      mov   al,            ; put next char from str1 into al

      inc   esi               ; increment pointer in str1

in_loop:

      cmp   al,dl

      je      first_char_found



      test    al,al               ; end of str1?

      jnz   loop_start          ; no, go get another char from str1



not_found:

      pop   esi

      pop   ebx

      pop   edi

      xor   eax,eax

      ret



; recall that dh contains the second char from str2



first_char_found:

      mov   al,            ; put next char from str1 into al

      inc   esi



      cmp   al,dh               ; compare second chars

      jnz   in_loop             ; no match, continue search



two_first_chars_equal:

      lea   edi,       ; store position of last read char in str1



compare_loop:

      mov   ah,      ; put next char from str2 into ah

      test    ah,ah               ; end of str2?

      jz      match               ; if so, then a match has been found



      mov   al,            ; get next char from str1

      add   esi,2               ; bump pointer into str1 by 2



      cmp   al,ah               ; are chars from str1 and str2 equal?

      jne   findnext            ; no



; do one more iteration



      mov   al,      ; put the next char from str2 into al

      test    al,al               ; end of str2

      jz      match               ; if so, then a match has been found



      mov   ah,      ; get next char from str1

      add   ecx,2               ; bump pointer in str1 by 2

      cmp   al,ah               ; are chars from str1 and str2 equal?

      je      compare_loop



; no match. test some more chars (to improve execution time for bad strings).



      jmp   findnext



; str2 string contains only one character so it's like the strchr functioin



strchr_call:

      xor   eax,eax

      pop   esi

      pop   ebx

      pop   edi

      mov   al,dl

      jmp   __from_strstr_to_strchr



;

;

; Match!Return (ebx - 1)

;

match:

      lea   eax,

      pop   esi

      pop   ebx

      pop   edi

      ret



empty_str2:         ; empty target string, return src (ANSI mandated)

      mov   eax,edi

      pop   esi

      pop   ebx

      pop   edi

      ret



strstrendp

      end

coolpay64 发表于 2007-8-30 01:10:40

某的解譯大約昰這樣

mov    edi,    ; str1 (the string to be searched)

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

edi在程序中是有讀寫部份
    lea    edi,
讀不寫,自己找吧

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

也就是
const char*               char*
>>> edi >>> eax

這感覺

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

coolpay64 发表于 2007-8-30 01:11:57

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

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

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

john_he 发表于 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。如果用读写内存指针调用就相安无事。
const char *str = "This is test.";          /* str指向程序的字符串池,只读内存 */
char *pc = strchr(str, 'h');
*pc = 0;      /* 访问违规 */


const char str[] = "This is test.";      /* str指向系统栈,读写内存 */
char *pc = strchr(str, 'h');
*pc = 0;      /* 通过,产生预期结果 */


我的理解是C不支持函数重载,所以返回char *以保证兼容const char *和char *,返回的指针能怎么用还要靠自我认识。

lw 发表于 2007-8-30 22:38:21

这些貌似在新版本的编译器上面有部分改动了……
偶印象中这样的,因为在编译的时候会有二个版本出现:
const char* + const char*
char* + char*
但忘了是不是给这些函数用的………………
总之C下面const可以是无视掉的XD
页: [1]
查看完整版本: strchr和strstr