本人菜鸟,有不对的地方请各位大侠轻点拍砖,谢谢
  打开记事本,输入“去”字,用ANSI方式保存。再重新打开,如果不出意外的话,看到的竟然是乱码。为了搞清楚“去”到底怎么变的身,开始了我非常不熟练的IDA+WinDbg逆向,权当是练手。

  首先用UE以十六进制的方式打开txt文件,其十六进制内容竟然是 0xFEFF 0x0225。0xFEFF是UNICODE编码的签名字节,而“去”的UNICODE编码是0x53bb,此处的0x0225显然是错误的。起初怀疑是记事本在保存时,就以其他的编码当作UNICODE写入到了文件中。WinDbg载入,跟踪到notepad!SaveFile,仔细看了一下,保存的过程如下:WideCharToMultiByte( GetACP(), ... , lpEditWCharBuf, ... );然后直接WriteFile写入到txt文件中,并没有进行任何附加的操作。那么多余的0xFEFF UNICODE签字和0x0225哪儿来的呢?无奈自己手写了CreateFile、ReadFile,终于看清,txt文件里保存的确实是“去”的ANSI编码的两个字节:0xA5C8。原来UE在以16进制方式查看有问题的txt文件时,也犯了跟记事本一样的解码错误。

  重新从记事本载入文件入手。从LoadFile跟踪到NpOpenDialogHookProc,一直到fDetermineFileType,终于找到了问题的根源:notepad!IsTextUTF8函数。先说说fDetermineFileType,其汇编代码不长,如下:

; int __stdcall fDetermineFileType(LPVOID lpBuffer,int cb)
_fDetermineFileType@8 proc near         ; CODE XREF: NpOpenDialogHookProc(x,x,x,x)+24A p
lpBuffer        = dword ptr  8
cb              = dword ptr  0Ch

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                push    edi
                mov     edi, [ebp+cb]
                xor     eax, eax
                cmp     edi, 1
                jbe     short loc_10023FF
                push    esi
                mov     esi, [ebp+lpBuffer]
                movzx   ecx, word ptr [esi]
                cmp     ecx, 0BBEFh
                jz      short loc_10023F0
                cmp     ecx, 0FEFFh
                jz      short loc_10023D7
                cmp     ecx, 0FFFEh
                jz      short loc_10023EC
                push    edi             ; cb
                push    esi             ; lpBuffer
                call    _IsInputTextUnicode@8 ; IsInputTextUnicode(x,x)
                test    eax, eax
                jz      short loc_10023DC

loc_10023D7:
                xor     eax, eax
                inc     eax
                jmp     short loc_10023FE

loc_10023DC:
                push    edi
                push    esi
                call    _IsTextUTF8@8   ; IsTextUTF8(x,x)
                neg     eax
                sbb     eax, eax
                and     eax, 3
                jmp     short loc_10023FE
loc_10023EC:                            
                push    2
                jmp     short loc_10023FD
loc_10023F0:                            
                cmp     edi, 2
                jbe     short loc_10023FE
                cmp     byte ptr [esi+2], 0BFh
                jnz     short loc_10023FE
                push    3

loc_10023FD:                            
                pop     eax

loc_10023FE:                            
                                        
                pop     esi

loc_10023FF:                            
                pop     edi
                pop     ebp
                retn    8
_fDetermineFileType@8 endp

  翻译成C代码更直观:
int __stdcall fDetermineFileType(LPVOID lpBuffer,int cb)
{
  int iType = 0;
  WORD wSign = 0;

  if( cb <= 1 )
    return 0;

  wSign = *(PWORD)lpBuffer;
  switch( wSign )
  {
  case 0xBBEF:
    {
      if( cb >= 3 && (PBYTE)lpBuffer[3] == 0xBF)
        iType = 3;
    }
    break;
  case 0xFEFF:
    {
      iType = 1;
    }
    break;
  case 0xFFFE:
    {
      iType = 2;
    }
    break;
  default:
    {
      if( !IsInputTextUnicode( lpBuffer, cb ) )
      {
        if( IsTextUTF8( lpBuffer, cb ) )
          iType = 3;
      }
      else
        iType = 1;
    }
  }
  return iType;
}
  当记事本打开任何一个文件时,首先会调用这个函数确定这个文本文件的编码方式。对于ANSI编码的GBXXXX标准的中文字符来说,直到IsTextUTF8函数返回FALSE,Notepad.exe才会以CP_ACP的方式调用MultiByteToWideChar,直到这时中文字符才会被正确解析,也就是说,如果IsTextUTF8返回了TRUE,那么ANSI的中文字符就会被当成UTF8编码转换成Unicode再显示出来。问题的根源就是IsTextUTF8了。看MS的家伙们是怎么写的IsTextUTF8:
; __stdcall IsTextUTF8(x,x)
_IsTextUTF8@8   proc near               ; CODE XREF: fDetermineFileType(x,x)

Buffer          = dword ptr  8
Size            = dword ptr  0Ch

                mov     edi, edi
                push    ebp
                mov     ebp, esp
                push    esi
                xor     esi, esi
                xor     ecx, ecx
                inc     esi
                xor     edx, edx
                cmp     [ebp+Size], ecx
                jle     short FALSE

Loop:                                  
                mov     eax, [ebp+Buffer]
                mov     al, [ecx+eax]
                test    al, al
                jns     short MayBeAscii
                xor     esi, esi

MayBeAscii:                         
                test    edx, edx
                jnz     short LeftBytes
                cmp     al, 80h
                jb      short LoopContinue

BytesCount:                           
                shl     al, 1
                inc     edx
                test    al, al
                js      short BytesCount
                dec     edx
                jz      short FALSE
                jmp     short LoopContinue

LeftBytes:                           
                and     al, 0C0h
                cmp     al, 80h
                jnz     short FALSE
                dec     edx

LoopContinue:                        
                inc     ecx
                cmp     ecx, [ebp+Size]
                jl      short Loop
                test    edx, edx
                ja      short FALSE
                test    esi, esi
                jz      short TRUE

FALSE:          
                xor     eax, eax
                jmp     short QUIT

TRUE:            
                xor     eax, eax
                inc     eax
QUIT:           
                pop     esi
                pop     ebp
                retn    8
_IsTextUTF8@8   endp
还是翻译成C:
BOOL IsTextUTF8( LPSTR lpBuffer, int iBufSize )
{
  /*
  0zzzzzzz;
  110yyyyy, 10zzzzzz
  1110xxxx, 10yyyyyy, 10zzzzzz
  11110www, 10xxxxxx, 10yyyyyy, 10zzzzzz
  */
  
  int iLeftBytes = 0;
  BOOL bUtf8 = FALSE;
  if( iBufSize <= 0 )
    return FALSE;

  for( int i=0;i<iBufSize;i++)
  {
    char c = lpBuffer[i];
    if( c < 0 )        //至少有一个字节最高位被置位
      bUtf8 = TRUE;
    if( iLeftBytes == 0 )//之前尚无UTF-8编码的字符的前导字节,或者是下个字符。
    {
      if( c >= 0 )  //0000 0000 - 0100 0000
        continue;
      do//统计出高位连续的的个数
      {
        c <<= 1;
        iLeftBytes++;
      }while( c < 0 );
      iLeftBytes--;    //表示本字符的剩余字节的个数;
      if( iLeftBytes == 0 )//最高位是10,不能作为UTF-8编码的字符的前导字节。
        return FALSE;
    }
    else           
    {
      c &= 0xC0;       //1100 0000
      if( c != (char)0x80 )//1000 0000 对于合法的UTF-8编码,非前导字节的前两位必须为。
        return 0;
      else
        iLeftBytes--;
    }
  }
  if( iLeftBytes )
    return FALSE;
  return bUtf8;
}
  根据国际相关组织的规定,UTF8用1-4个字节来表示UNICODE,对于绝对值在7F以上的字符,UTF-8用两个及以上的字节表示,其中第一个字节的高位连续的1的个数,表示“用UTF8编码表示的字符的字节数”,此字符其他的字节必须是以10开头。如某UTF8编码的字符,第一个字节为110X XXXX,那么说明表示此字符的UTF-8编码要用到两个字节,其第二个字节必须是10YY YYYY。否则就是非法的UTF8编码。
  这样说来,只要其ANSI编码值为0x8yCx 0xByCx和0x8yDx-0xByDx字符,都被会解释成UTF-8编码:
  WORD wChar = 0;
  char szBuf[3]={0};
  for( wChar = 0x80C0; wChar < 0xBFDF; )
  {
    *(PWORD)szBuf = wChar;
    cout<<szBuf;
    if( (wChar & 0xFF) == 0xDF )//11011111
    {
      wChar += 0x100;
      wChar &= 0xFFC0;
    }
    else
      wChar += 1;
  }
  这个编码范围的汉字,绝大多数我还是不认识的,但常用的还是有不少。以至于你写个“小丫头”、“泉水”、“来去”、“千十”等常用字保存,再用记事本打开,看到的都是乱码。
  至此,“去”字引起的乱码现象,终于得到了完满解决。