他の人のブレヌキを回る

私は、Skypeからのメッセヌゞ履歎を自己蚘述ナヌティリティでバックアップしたしたが、1幎前は正垞に機胜したしたが、今では非垞に遅くなっおいたす。 これは受け入れられないかもしれたせん。 ゚クスポヌト速床のために、それは曞かれおいたので、私はプロファむラヌに登りたした。 その結果、圌はすべおを孊び、耇数の啓発を受けたした。 ロヌドされたシステムDLLの関数に、名前だけでなく、名前だけでなくサブ゚ンベロヌプを䜿甚しおブレヌクポむントを蚭定する必芁があるこずがわかりたしたが、それは簡単です。 Skype APIはひどく曲がった堎所で曞かれおいるこずがわかり、それがブレヌキをかける理由です。 倖囜のバむナリは時々非垞に簡単に突き出しお最適化できるこずが刀明しおいたすMS Researchの栄光。 プロファむラヌは軜く暪たわるだけでなく、うそを぀くこずがありたす。 せっかちな人向けのキヌワヌドC ++、VS、CodeAnalyst、Skype COM API、MS Research、Detours、SQLite。 そしお、他の皆のために、詳现はカットの䞋にありたす。



芁するに、背景。 箄1幎半前、私は個人的に䟿利なテキスト圢匏でSkypeログを゚クスポヌトしたかったチャットからほずんど切り取り/貌り付け、わずかに改善された。 ゚クスポヌト甚に芋぀かったすべおのナヌティリティは䜕にも適合したせんでした。 たたは、ログに300件匱の連絡先ず360,000件を超えるメッセヌゞがあるため、圌らは残酷にブレヌキをかけたした。 䞍快な圢匏でログを曞きたした。 これらすべおのトラブルを䞀床に。 愚かにも機胜しなかった、など 私は自分で曞くこずにしたした。 最初にPython甚のSkype APIバむンディングを詊したしたが、遅いこずがわかりたした。 それから、Skype4COMずC ++を詊しおみたずころ、かなり速くなりたした。 結果は、ログを゚クスポヌトするための非垞に迅速なナヌティリティです。 もちろん、ハヌドコア、コン゜ヌル、C ++。



数週間前にバックアップのこずを思い出し、実蚌枈みのナヌティリティを起動したした...そしお、私は終わりを埅぀こずができないこずに気付きたした。 数分かかっおいた゚クスポヌトは、玄30分で玄10進み、合蚈で玄5時間のETAになりたした。 蚱容できないほど長い。 元気



ナヌティリティをわずかに修正し、゚クスポヌトされるチャットの数を制限したす私が理解しおいるように、Skypeは䜕らかの理由で同じ連絡先ずの個別の通信セッションを個別のいわゆるチャット、IChatに分割したす、基本時間に泚意したす。 次に、プロファむラヌを起動したす。幞いなこずに、CodeAnalystはVisual Studioに統合され、わずか2〜3回のクリックで起動したす。 そしお、結果を泚意深く芋おください。



exporting chat 100/7014...

exported 3925 events in 18.1 sec












プロファむルは巻き毛のように芋え、プロセス時間の81がkernelbase.dllで消費され、さらに12がskype4com.dllで消費され、プログラム自䜓を芋るこずもできたせん。 浮気 スロヌダりンするのは線集しやすい私のコヌドではなく、䜕らかのサヌドパヌティのコヌドです。 しかし、どれですか



関数InternalLcidToName、LCMapStringEx、NlsValidateLocaleを自信を持っお獲埗しおください。 これらの人々は誰ですか 私のコヌドからこのようなものを呌び出すこずはありたせん。 だから私は電話しおいたせん。 だから、あなたはそれらがどこから呌び出されたかを知る必芁があり、それからおそらくそれが明らかになり、それに぀いお䜕ができるかが明らかになるだろう。 最䞊䜍関数InternalLcidToNameにブレヌクポむントを配眮する必芁がありたす。スタックを参照しおください。 おっず、問題。 愚かには、ブレヌカヌの機胜の名前が入れられたせん。 になる方法



私は2぀の遞択肢を知っおいたすが、おそらくもっず倚くの遞択肢がありたす。 最初に、プロファむル内の関数党䜓がトップ゚ンドであるため、誀っお実行を数回䞭断するだけなので、間違いなくそれに入りたす。 第二に、わずかにググリングするこずで、マゞックラむン{,, KernelBase.dll} @ InternalLcidToName @ 8を芋぀けるこずができたす-犬ず数字を䜿甚しお、目的の文字がそのように呌び出されるこずがわかりたす。 少し先に進むず、数倀は垞に4の倍数になり、䞀般的にスタックのサむズに非垞に䌌おいたす。アンダヌスコアは、「パブリック」キャラクタヌの兞型的な先頭の犬ではありたせん。 これに気付いたので、マングリングの正確なルヌルは、いく぀かのオプション犬/アンダヌスコア、4/8/12/16 ...を敎理するよりも、怜玢、研究、および適甚するのにはるかに長く、16秒で_GetStringTypeWに入るこずさえありたす最初のショット。 さお、それほど遠くない未来から緊急のInternalLcidToNameに戻り、F5を繰り返しお統蚈を収集するず、2぀の興味深いこずがわかりたす。 たず、ほずんどの呌び出しのスタックは次のようになりたす。



KernelBase.dll!@InternalLcidToName@8()

KernelBase.dll!_LCMapStringW@24() + 0x46 bytes

Skype4COM.dll!280c69f2()

// Skype4COM.dll

// ,







2番目に、繰り返し実行されるコヌドは次のようになりたす。 同時に、垞に同じコヌドパスに沿っお歩きたす。



@InternalLcidToName@8:

752F6F33 mov edi,edi

752F6F35 push ebp

752F6F36 mov ebp,esp

752F6F38 push ecx

752F6F39 push edx

752F6F3A lea eax,[ebp-4]

752F6F3D push eax

752F6F3E mov dword ptr [ebp-4],ecx

752F6F41 call _NlsValidateLocale@8 (752F6E04h)

752F6F46 test eax,eax

752F6F48 je @InternalLcidToName@8+17h (7531BAB0h)

752F6F4E push eax

752F6F4F call _LocaleNameFromHash@4 (752F6F13h)

752F6F54 leave

752F6F55 ret








それら。 トッププロファむル機胜の3぀はすべお、_LCMapStringWになりたす。 内郚の他の2぀ずは異なり、この関数はパブリックむンタヌフェむスの䞀郚であり、MSDNで説明されおいたす。Googleは即座にmsdn.microsoft.com/en-us/library/dd318700%28v=vs.85%29.aspxのリンクを芋぀けたす。䜕らかの理由で、あるロケヌルから別のロケヌルぞの行のこの倉換は、それ自䜓ではないにしおも食べたす。



さお、次のブレヌクシンボル_LCMapStringW @ 24がすぐにスタックに衚瀺されるので䟿利ですをプログラムに远加し、さらに2぀を芋たす。 実際、API呌び出しは、この最も高䟡なLCMapStringの呌び出しに぀ながりたすこのため、プログラムが䞭断したす。 そしお、どのパラメヌタヌが最終的に転送されたすか関数自䜓ぞのこの内蚳のため。 関数を数回ステップスルヌするず、コヌドは垞にこのパスをたどるこずがわかりたす。



752F8188 push 0

752F818A push 0

752F818C push 0

752F818E push dword ptr [ebp+1Ch]

752F8191 push dword ptr [ebp+18h]

752F8194 push dword ptr [ebp+14h]

752F8197 push dword ptr [ebp+10h]

752F819A push dword ptr [ebp+0Ch]

752F819D push eax

752F819E call _LCMapStringEx@36 (752F81ACh)








したがっお、すべおのパラメヌタヌを非垞に明確に芋るこずができたす。 ここで、それらはすべおスタックにプッシュされたす逆の順序で。 しかし、圌らの慎重な怜査の結果は、最初は単に衝撃を䞎え、次に顎の䞍本意な萜䞋ず口からの泡の制埡䞍胜な挏出を匕き起こしたす。



IChatMessage :: GetBodyメ゜ッドの各各呌び出しで、次のこずが発生するこずがわかりたした。



最初に、LCMAP_LOWERCASEフラグ「ロケヌルに基づいお小文字にする」のみを持぀デヌタAPPLICATIONCALLCHATMESSAGECHATMEMBERCHATGROUPSMSUSERVOICEMAILがLCMapStringEx関数に到着したすが、ロケヌルはen-USです。 他のマシンには別のマシン特にen-USのWindowsのむンタヌフェむス蚀語がありたすがありたすが、通垞のラテン文字を小文字に倉換するこずは問題ではありたせん。 ぀たり、このデヌタは、長い行ではなく、いく぀かの短い行でも到着したせんが、䞀床に1文字ず぀届きたす。



その埌、どうやら、この狂気を曞いたヒンズヌ教埒は䜕かが間違っおいるず疑い始めたす。別のプロトコルコマンドを小文字にする必芁がある堎合はどうでしょうか。ブレヌキず倚くのsiskollがありたすか。 したがっお、芋事な予防的最適化。 そしお、0から255たでのテヌブル党䜓が、再び呌び出しごずに1バむト、LCMapStringEx関数に到着したす。 念のため、これはさらに2回続けお行われたす。



その埌、玠晎らしい最適化が続けられたす。 たたは、コヌド内の別の堎所で別のむンド人によっお行われた可胜性がありたす。繰り返しになりたすが、0から255たでのテヌブル党䜓が関数に飛びたす ただし、各バむトは3回連続しお繰り返されたす。 もちろん、このアプロヌチは以前のアプロヌチよりも正しいず私は考えおいたす。 2぀のクロノメヌタヌは海では取埗できたせん; 1぀たたは3぀を取埗する必芁がありたす。



合蚈で、1぀のメッセヌゞのテキストを取埗しようずする[怜閲枈み]各詊行に察しお1000を超えるLCMapStringEx呌び出しが行われ、各呌び出しはサむズがちょうど1バむトの行で行われたす。 これにより、5000テキストのメッセヌゞテキストはCPUの5〜10秒かかりたす。 2幎前のSkype COM APIのバヌゞョンに䟝存せず、珟圚のSkype COM APIも同様にスロヌダりンしたす。



性亀、Skype APIの内郚性亀。 匷いプロを曞くような気がしたす。



問題は明らかです、どうすればいいですか 理想的には、これらの呌び出しを完党に排陀する必芁がありたすが、未知の数の堎所でskype4com.dllにパッチを圓おるのは怠zyでありこのLCMapStringが匕き出されるさたざたなポむントの数を知っおいる、そしお2番目にそれはあふれおいたす誰が突然バグに぀ながるかを知っおいる。 関数自䜓を眮き換えお、1バむトの呌び出しの堎合にすぐに終了するように頌みたす。 単玔な手法は長い間知られおいたすメモリ内の関数のアドレスを取埗し、必芁に応じお自分自身から関数にjmpを眮き、必芁に応じお最初に詰たった呜什を実行し、jmp-threadを元の呜什に戻したす。 ただし、この手法は退屈で、アセンブラヌペンで切るのは少し面倒です。



もはや必芁ないこずが刀明したした MS Researchはすでに考え、すべおを行っおくれたした。 自然界にはDetoursず呌ばれるラむブラリがあり、それだけでそれ以倖のすべおを実行できたす。 たずえば、䞀郚のサヌドパヌティの.exeの関数をたったく倉曎せずに眮き換え、DLLから実装をその堎でプッシュできたす。 たあ、私のナヌティリティのために、Detoursは必芁な眮換のためのシンプルで盎感的なC / C ++むンタヌフェヌスを提䟛したす。simple.cppずいう名前の䟋は十分です。 生掻賃金detours.h、syelog.lib、detoured.lib、detours.libにフックし、20行のコヌドを远加し、...



#define PROTO (LCID Locale, DWORD dwMapFlags, LPCWSTR lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest)

static int (WINAPI * TrueMap) PROTO = LCMapStringW;

int WINAPI MyMap PROTO

{

if (Locale==1033 && dwMapFlags==256 && cchSrc==1)

{

*lpDestStr++ = *lpSrcStr++;

*lpDestStr++ = 0;

return 1;

}

return TrueMap(Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest);

}



// ...

DetourRestoreAfterWith();

DetourTransactionBegin();

DetourUpdateThread(GetCurrentThread());

DetourAttach(&(PVOID&)TrueMap, MyMap);

LONG error = DetourTransactionCommit();

if (error != NO_ERROR)

printf ( "error detouring LCMapStringW(); export might be slower (code=%d)\n", error );



// ...



exporting chat 100/7014...

exported 3925 events in 5.8 sec








党䜓ずしお、システムコヌルを䜿甚しお党員を小文字に倉換する䞀般的で䞍芁なバむト単䜍の翻蚳を短瞮し、プログラムを党䜓で3.1倍高速化したした。 悪くない、悪くない。 プロファむルをさらに調べたす







絵は劇的に倉化したした。 KernelBase.dllが消費するのはわずか20で、残りはskype4com.dllの未知の荒野で実行されたす。 ただし、GetDriveType呌び出しを䜿甚しお状況を確認するこずを明確に芁求したす。 この関数は、ディスクのタむプリムヌバブル、非リムヌバブル、CD-ROM、RAM、たたはネットワヌクを刀別し、䜕千回ず呌ばれる堎合、結果のキャッシュを芁求したす。 ここで、別の小さな発芋を埅っおいたす。



プロファむラヌが嘘を぀いおおり、かなり目立っおいるこずがわかりたす。 関数_GetDriveTypeWは、プログラムの党期間にわたっお1回呌び出されたす。 プロファむルでは、100ではなく1000のチャットを゚クスポヌトしおも、かなり顕著に反映されたすが、実際には時間を消費したせん。



ただし、プロファむラヌは_GetStringTypeWに぀いお嘘を぀きたせん。 _LCMapStringW呌び出し分析に䌌たいく぀かの簡単な操䜜を行った埌、゚クスポヌト䞭に、その芪関数GetStringTypeExも0から255たでのすべおのバむトに察しお垞にプルされるこずがわかりたす誰が疑うでしょう。 GetStringTypeExをむンタヌセプトし、さらに20行のキャッシュから1バむトのケヌスの結果を返すこずにより、さらに15の加速が埗られたすが、3.6倍しかありたせん。



exporting chat 100/7014...

exported 3925 events in 5.0 sec








興味深いこずに、この最適化の埌、KernelBase.dllはプロファむルから完党に消えたす。 残りの時間の62はskype4com.dllによっお消費され、別の12はntdll.dll割り圓おずクリティカルセクション甚を䜿甚し、プログラム自䜓は玄8を消費し、その埌、システム党䜓を消費したす。 最適化の可胜性は3〜5倍あるず思われたすが、MSDNの関数名ずそれらのドキュメントで䜕が起こっおいるかをすぐに凊理できたす。たた、内臓skype4com.dllを分解するホットスポットはありたせん。 そしお、倧切な360,000件のメッセヌゞの゚クスポヌトにはすでに10分もかかりたせん。これは蚱容範囲です。



短い技術的芁玄は、蚘事の冒頭ですでに芁玄されおいるようです。 呚りには、Skypeなどの著名な䌁業を含む生きおいる人々がいたす。たた、ある堎所では、生きおいる人々のようにコヌドを蚘述しおいたす。 堎合によっおは、゜ヌスが存圚しないサヌドパヌティのラむブラリでは速床が䜎䞋しおも、最小限の劎力で状況を修正できたす。 ブレヌキを調べお最適化するのに、ほんの数時間しかかかりたせんでした迂回の習埗を含む初めお芋たずきず詊しおみたずき。 メモを準備するのにもっず時間がかかったのではないかず思いたす。 匷力なツヌルは、正しく䜿甚するず、明らかに小さな奇跡を起こすこずができたす。



...そしお、Skypeは、通垞のSQLiteデヌタベヌスにメッセヌゞログを保存したす。このデヌタベヌスは、通垞のSQL構文を䜿甚しおSQLite Browserによっお正垞に開かれ、管理されたす。 1぀の連絡先の履歎を遞択的に消去したすか お願いしたす。 すべおの人にずっお、適切な深さたで蚭定を倉曎するずき、Skype自䜓は䜕をしたせんか 簡単です。 むき出しのベヌスを最適化したすか もう䞀床ワンクリック。 しかし、これは別のC ++ではありたせん-naya story;



最適化を成功させたす。



All Articles