【原創】Winamp標題欄中文亂碼原因分析及修正
Winamp標題欄中文亂碼原因分析及修正
前言:
實在是忍受不了MediaPlayer對記憶體龐大的佔用率和我的小記憶體之間的矛盾,同時又找不到foobat的WMA外掛(有知道的兄弟,請給個下載地址,多謝!),於是裝上了Winamp 2.81 簡體中文經典懷念版,就是水手漢化的那個(夠豪華)。開啟一首抒情的歌曲,安撫一下我受傷的心靈――如果連續幾天倒黴,先是筆記本電池掛掉、然後是系統癱瘓、接下來是硬碟壞掉(全面癱瘓阿,可憐的國產筆記本,幸好是全球聯保的,可是又有什麼用呢?剛過了質保期,就全部壞掉,真是牛x),你就會明白什麼叫“受傷”――好久沒有用過Winamp了,記得上次用還是在N年前(懷念我的大奔133和那個激情的時代),於是隨手點選,熟悉一下功能。漢化過的版本就是好,畢竟中國人還是看中文最舒服――慢著,莫非這麼古老的BUG還沒有被修正麼?就是那個滾動標題欄顯示中文時亂碼的BUG,一個陳舊的毛病。看著下面時而清晰時而混亂的標題,總覺得不舒服,算了,修復一下這個陳年舊瘡吧。
分析物件:
Nullsoft Winamp 2.81 簡體中文經典懷念版
水手漢化 豪華版
故障分析:
從顯示的亂碼時常清晰時常混亂就能很明白的想出來,亂碼的原因就是可謂“經典”的“半個漢字”,大部分不支援中文的軟體都是因為這個。在Ascll碼中,每個英文用一個byte來記錄,而在GB碼中,每一個漢字用一個word來記錄。沒有經過改造的程式,在處理英文漢字混排的字串時,如果刪除一個漢字的話,每次只能刪除這個漢字GB碼的低8位,這時,亂碼就產生了。只要我們能正確的在英文漢字混排的字串分辨出漢字和英語,然後針對處理,問題就解決了。
程式碼分析:
要操刀修改,首先就要找到修改的位置。600多k的Winamp也不算小,蠻力去找自然是大海撈針。且先對其工作流程作一分析。要實現滾動標題欄其實很簡單,取得要顯示的字串,把它的長度按需要處理一下(不同的時間起始位置不同),然後用SetWindowTextA顯示出來即可。也就是說,Winamp也有可能是用上面的方法來實現的。
經過檢測,Winamp沒有加殼(其實這樣的軟體也沒有必要加殼:))。開啟W32Dasm載入Winamp,選擇“查詢”-〉“查詢文字”,輸入SetWindowTextA進行查詢。在經歷的N多個“查詢下一個”後,在0x42EE60停下了,這是一段可疑的程式碼(由此可見,正確的分析是很重要的,尤其在沒有任何線索的時候。當然,運氣同樣很重要:))。向上找,找到這一段子程式的起始位置,Copy下來分析:
* Referenced by a CALL at Address:
|:0041D2A1
|
:0042ED10 55 push ebp
:0042ED11 8BEC mov ebp, esp
:0042ED13 B800100000 mov eax, 00001000
:0042ED18 E8D3DF0000 call 0043CCF0
:0042ED1D A000874400 mov al, byte ptr [00448700]
:0042ED22 56 push esi
:0042ED23 57 push edi
:0042ED24 888500F0FFFF mov byte ptr [ebp+FFFFF000], al
:0042ED2A B9FF030000 mov ecx, 000003FF
:0042ED2F 33C0 xor eax, eax
:0042ED31 8DBD01F0FFFF lea edi, dword ptr [ebp+FFFFF001]
:0042ED37 BEC0384500 mov esi, 004538C0 ;在這個地方放的是完整的標題
:0042ED3C F3 repz ;清空緩衝區
:0042ED3D AB stosd
:0042ED3E 66AB stosw
:0042ED40 56 push esi
:0042ED41 AA stosb
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042ED42 E8F7DF0000 Call 0043CD3E ;算一下標題有多少個字
:0042ED47 59 pop ecx
:0042ED48 8B0D48124500 mov ecx, dword ptr [00451248] ;這個就是迴圈用的計數器地址
:0042ED4E 3BC8 cmp ecx, eax ;到頭了,就重新再來吧。走馬燈效果的實現。
:0042ED50 7D57 jge 0042EDA9
:0042ED52 8D81C0384500 lea eax, dword ptr [ecx+004538C0]
:0042ED58 50 push eax
:0042ED59 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
:0042ED5F 50 push eax
* Reference To: MSVCRT.strcpy, Ord:02BAh
|
:0042ED60 E87DDF0000 Call 0043CCE2 ;按照計數器指定的偏移,把要顯示的字串移到緩衝區
:0042ED65 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
* Possible StringData Ref from Data Obj ->" *** " ;熟悉的東西:)
|
:0042ED6B 686C644400 push 0044646C
:0042ED70 50 push eax
* Reference To: MSVCRT.strcat, Ord:02B6h
|
:0042ED71 E872DF0000 Call 0043CCE8
:0042ED76 83C410 add esp, 00000010
:0042ED79 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
:0042ED7F FF3548124500 push dword ptr [00451248]
:0042ED85 56 push esi
:0042ED86 50 push eax
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042ED87 E8B2DF0000 Call 0043CD3E
:0042ED8C 59 pop ecx
:0042ED8D 8D840500F0FFFF lea eax, dword ptr [ebp+eax-00001000] ;繼續塞字元
:0042ED94 50 push eax
* Reference To: MSVCRT.strncpy, Ord:02C1h
|
:0042ED95 FF1558E24300 Call dword ptr [0043E258]
:0042ED9B 83C40C add esp, 0000000C
:0042ED9E FF0548124500 inc dword ptr [00451248] ;重點到了!每次不判斷當前字元的型別,就簡單的把計數器加一,“半個漢字”自然就產生了
:0042EDA4 E984000000 jmp 0042EE2D
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0042ED50(C) ;這個子程式是用作字元全部顯示完成後重新開始的,沒有什麼問題。
|
:0042EDA9 56 push esi
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042EDAA E88FDF0000 Call 0043CD3E
:0042EDAF 8B0D48124500 mov ecx, dword ptr [00451248]
:0042EDB5 8D896C644400 lea ecx, dword ptr [ecx+0044646C]
:0042EDBB 2BC8 sub ecx, eax
:0042EDBD 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
:0042EDC3 51 push ecx
:0042EDC4 50 push eax
* Reference To: MSVCRT.strcpy, Ord:02BAh
|
:0042EDC5 E818DF0000 Call 0043CCE2
:0042EDCA 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
:0042EDD0 56 push esi
:0042EDD1 50 push eax
* Reference To: MSVCRT.strcat, Ord:02B6h
|
:0042EDD2 E811DF0000 Call 0043CCE8
:0042EDD7 56 push esi
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042EDD8 E861DF0000 Call 0043CD3E
:0042EDDD 83C418 add esp, 00000018
:0042EDE0 6A03 push 00000003
:0042EDE2 59 pop ecx
:0042EDE3 2B0D48124500 sub ecx, dword ptr [00451248]
:0042EDE9 03C1 add eax, ecx
:0042EDEB 50 push eax
:0042EDEC 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
* Possible StringData Ref from Data Obj ->" *** "
|
:0042EDF2 686C644400 push 0044646C
:0042EDF7 50 push eax
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042EDF8 E841DF0000 Call 0043CD3E
:0042EDFD 59 pop ecx
:0042EDFE 8D840500F0FFFF lea eax, dword ptr [ebp+eax-00001000]
:0042EE05 50 push eax
* Reference To: MSVCRT.strncpy, Ord:02C1h
|
:0042EE06 FF1558E24300 Call dword ptr [0043E258]
:0042EE0C FF0548124500 inc dword ptr [00451248] ;個人感覺沒有必要處理這個地方。
:0042EE12 56 push esi
* Reference To: MSVCRT.strlen, Ord:02BEh
|
:0042EE13 E826DF0000 Call 0043CD3E
:0042EE18 83C003 add eax, 00000003
:0042EE1B 83C410 add esp, 00000010
:0042EE1E 390548124500 cmp dword ptr [00451248], eax
:0042EE24 7C07 jl 0042EE2D
:0042EE26 83254812450000 and dword ptr [00451248], 00000000
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0042EDA4(U), :0042EE24(C)
|
* Reference To: USER32.GetWindowLongA, Ord:0156h
|
:0042EE2D 8B35B8E34300 mov esi, dword ptr [0043E3B8]
:0042EE33 6AF0 push FFFFFFF0
:0042EE35 FF3520334500 push dword ptr [00453320]
:0042EE3B FFD6 call esi
* Reference To: USER32.SetWindowLongA, Ord:0258h
|
:0042EE3D 8B3D50E34300 mov edi, dword ptr [0043E350]
:0042EE43 25FFFF3FFF and eax, FF3FFFFF
:0042EE48 50 push eax
:0042EE49 6AF0 push FFFFFFF0
:0042EE4B FF3520334500 push dword ptr [00453320]
:0042EE51 FFD7 call edi
:0042EE53 8D8500F0FFFF lea eax, dword ptr [ebp+FFFFF000]
:0042EE59 50 push eax
:0042EE5A FF3520334500 push dword ptr [00453320]
* Reference To: USER32.SetWindowTextA, Ord:025Eh ;嘿嘿,終於顯示出來了;)
|
:0042EE60 FF15E0E34300 Call dword ptr [0043E3E0]
:0042EE66 6AF0 push FFFFFFF0
:0042EE68 FF3520334500 push dword ptr [00453320]
:0042EE6E FFD6 call esi
:0042EE70 0D0000C000 or eax, 00C00000
:0042EE75 50 push eax
:0042EE76 6AF0 push FFFFFFF0
:0042EE78 FF3520334500 push dword ptr [00453320]
:0042EE7E FFD7 call edi
:0042EE80 5F pop edi
:0042EE81 5E pop esi
:0042EE82 C9 leave
:0042EE83 C3 ret
經過分析,發現從0x0042ED10到0x0042EE60的這段程式碼就是實現滾動標題效果的核心程式碼。其中0x004538C0放置的是完整的字串,0x00451248放置的是顯示位置計數器值,0x006DA9EC放置的是將要顯示出來的字串。由於0x0042ED9E處沒有對當前字元進行型別分析,簡單的移動1次計數器,導致了漢字顯示亂碼。為了證實剛才的分析,開啟TRW載入Winamp,下斷點BP 0042ED10,把0x0042ED9E處的程式碼都改為nop。然後繼續執行,發現標題欄已經不再繼續滾動。證明上面的分析正確。
修改程式碼:
經過上面分析,只要正確的判斷當前字元的型別並做出相應的處理即可。問題是怎麼區分英文和漢字。記得可以顯示的英文Ascll碼到0x80為止。查了一下資料,GB code的內碼的兩個位元組都是從A0H - FEH之間的。這樣的話,程式碼基本上就可以寫出來了:
mov ecx,dword ptr [00451248]
lea eax,dword ptr [ecx+004538C0]
cmp byte ptr [eax],A0
jb 1
inc dword ptr [00451248]
1:
inc dword ptr [00451248]
retn
由於個人比較懶的原因,這裡對漢字的判斷作了簡化處理,只要小於A0的字元都認為是英文,反之則是中文。一般情況下,這樣都是可以正常現實的,當然BIG5碼除外。關於BIG5碼的判斷標準如下:“BIG code 的內碼的第一個位元組是80H - FFH,第二個位元組是00H - FFH”有興趣的朋友可以自行修改。
下面的問題就是找一塊能夠放下程式碼的空間。感覺Winamp是用VC++編寫的,Language2000證實了這一點。由於程式碼作了最佳化處理,所以基本上沒有什麼縫隙,所以只有從.text節末端尋找可以利用的空間。根據VirtualSize、SizeOfRawData和PointerToRawData計算出在檔案0x0003C8E0偏移(RVA:0x0043D4E0)後即為空閒空間。我們就把程式碼放在這個地方。opcode如下:
8B0D481245008D81C03845008038A07206FF0548124500FF0548124500C3
然後修改0x004538C0(檔案偏移0x0002E19E)處的程式碼:
call 0043D4E0
nop ;佔位
opcode如下:E83DE7000090
再做些收尾工作,修改.text的實際尺寸。這樣就基本上已經完工了。
下面再測試一下,開啟Winamp,隨著悠揚的樂曲傳出,我們發現,滾動標題欄終於可以正確顯示中文了:)
後記:
希望上文能把我要說的表達出來。如果有描述不清晰的地方或有錯誤,請與我聯絡:coffin13@183.ha.cn。
匆忙之作,加上我水平又菜,不免錯誤百出,望高手予以指正。在此先謝過了。
monkeycz
2004年12月12日凌晨
相關文章
- [原創]Gerrit中文亂碼問題解決方案分享2016-07-15
- Java GBK 中文亂碼問題分析2021-09-09Java
- 標
題:avserve病毒初步分析!【原創】2004-05-02
- EasyUI 中文亂碼問題2020-12-30UI
- MSSQL中文亂碼問題2014-12-19SQL
- Java 中文 亂碼問題2015-12-07Java
- iOS9 pdf中文亂碼問題的原因與“妥協”辦法2016-02-02iOS
- SpringMVC中文亂碼問題2018-01-17SpringMVC
- Python中文亂碼問題2014-07-04Python
- Cookie值中文亂碼問題2012-05-31Cookie
- 解決中文亂碼問題2024-05-14
- Oracle 中文字元及中文亂碼判斷2011-02-23Oracle字元
- PDF複製亂碼 -- 原因及解決方案2016-06-10
- 深度揭祕亂碼問題背後的原因及解決方式2017-02-24
- JFreechart 在linux下不顯示及中文亂碼問題2015-04-01Linux
- Python BeautifulSoup中文亂碼問題2020-12-12Python
- MySql中文亂碼問題解決2020-11-13MySql
- Jmeter 解決中文亂碼問題2020-10-10JMeter
- Java 解決中文亂碼問題2018-01-23Java
- RDSSQLSERVER解決中文亂碼問題2016-11-28SQLServer
- 解決MySQL中文亂碼問題2014-04-20MySql
- ubuntu 中文顯示亂碼問題2015-04-18Ubuntu
- Java,MySQL中文亂碼問題求教2004-12-23JavaMySql
- java處理中文亂碼問題2009-12-18Java
- vscode中文亂碼問題2024-09-29VSCode
- FCKeditor原始碼分析(一)—–fckeditor.js的中文註釋分析(原創)薦2010-04-20原始碼JS
- iOS導航欄標題錯亂的解決方法2017-04-23iOS
- 解決plsql中中文亂碼問題2020-12-12SQL
- Java Web開發中文亂碼問題2018-07-10JavaWeb
- 關於中文亂碼問題(總結)2013-08-07
- MySQL客戶端中文亂碼問題。2011-08-08MySql客戶端
- BIP輸出PDF中文亂碼問題2012-08-23
- Ruby On Rails實踐—中文亂碼問題2010-05-19AI
- Ubuntu 字元介面中文亂碼問題2011-12-01Ubuntu字元
- Navicat for mysql 顯示中文亂碼問題2012-09-05MySql
- springmvc 解決中文亂碼問題2024-05-14SpringMVC
- js解決url中文亂碼問題2024-06-05JS
- SpringBoot整合Redis亂碼原因及解決方案2021-11-05Spring BootRedis