2016年4月

一些有趣的问题(2)——Chromium是如何处理history.pushState的?

采用Blink Master的代码进行分析。history.pushState的处理代码在blink\Source\core\frame\history.h中。

本地代码调试,调用到pushState时的调用栈如下:

    webcore_shared.dll!blink::History::pushState(WTF::PassRefPtr<blink::SerializedScriptValue> data, const WTF::String & title, const WTF::String & url, const blink::StateOptions & options, blink::ExceptionState & exceptionState) Line 63   C++
    webcore_shared.dll!blink::HistoryV8Internal::pushStateMethod(const v8::FunctionCallbackInfo<v8::Value> & info) Line 188 C++
    webcore_shared.dll!blink::HistoryV8Internal::pushStateMethodCallback(const v8::FunctionCallbackInfo<v8::Value> & info) Line 198 C++
    v8.dll!v8::internal::FunctionCallbackArguments::Call(void (const v8::FunctionCallbackInfo<v8::Value> &) * f) Line 34    C++
    v8.dll!v8::internal::HandleApiCallHelper<0>(v8::internal::Isolate * isolate, v8::internal::`anonymous-namespace'::BuiltinArguments<1> & args) Line 1094 C++
    v8.dll!v8::internal::Builtin_Impl_HandleApiCall(v8::internal::`anonymous-namespace'::BuiltinArguments<1> args, v8::internal::Isolate * isolate) Line 1116   C++
    v8.dll!v8::internal::Builtin_HandleApiCall(int args_length, v8::internal::Object * * args_object, v8::internal::Isolate * isolate) Line 1111    C++
    38f0bd3c()  Unknown
    [Frames below may be incorrect and/or missing]  
    38f3fb68()  Unknown
    38f2d541()  Unknown
    38f170df()  Unknown
    v8.dll!v8::internal::Invoke(bool is_construct, v8::internal::Handle<v8::internal::JSFunction> function, v8::internal::Handle<v8::internal::Object> receiver, int argc, v8::internal::Handle<v8::internal::Object> * args) Line 128  C++
    v8.dll!v8::internal::Execution::Call(v8::internal::Isolate * isolate, v8::internal::Handle<v8::internal::Object> callable, v8::internal::Handle<v8::internal::Object> receiver, int argc, v8::internal::Handle<v8::internal::Object> * argv, bool convert_receiver) Line 179    C++
    v8.dll!v8::Script::Run(v8::Local<v8::Context> context) Line 1671    C++
    webcore_shared.dll!blink::V8ScriptRunner::runCompiledScript(v8::Isolate * isolate, v8::Local<v8::Script> script, blink::ExecutionContext * context) Line 391    C++
    webcore_shared.dll!blink::ScriptController::executeScriptAndReturnValue(v8::Local<v8::Context> context, const blink::ScriptSourceCode & source, blink::AccessControlStatus accessControlStatus, double * compilationFinishTime) Line 186    C++
    webcore_shared.dll!blink::ScriptController::evaluateScriptInMainWorld(const blink::ScriptSourceCode & sourceCode, blink::AccessControlStatus accessControlStatus, blink::ScriptController::ExecuteScriptPolicy policy, double * compilationFinishTime) Line 560 C++
    webcore_shared.dll!blink::ScriptController::executeScriptInMainWorld(const blink::ScriptSourceCode & sourceCode, blink::AccessControlStatus accessControlStatus, double * compilationFinishTime) Line 533   C++
    webcore_shared.dll!blink::ScriptLoader::executeScript(const blink::ScriptSourceCode & sourceCode, double * compilationFinishTime) Line 401  C++
    webcore_shared.dll!blink::ScriptLoader::prepareScript(const WTF::TextPosition & scriptStartPosition, blink::ScriptLoader::LegacyTypeSupport supportLegacyTypes) Line 271    C++
    webcore_shared.dll!blink::HTMLScriptRunner::runScript(blink::Element * script, const WTF::TextPosition & scriptStartPosition) Line 354  C++
    webcore_shared.dll!blink::HTMLScriptRunner::execute(WTF::PassRefPtr<blink::Element> scriptElement, const WTF::TextPosition & scriptStartPosition) Line 216  C++
    webcore_shared.dll!blink::HTMLDocumentParser::runScriptsForPausedTreeBuilder() Line 319 C++
    webcore_shared.dll!blink::HTMLDocumentParser::processParsedChunkFromBackgroundParser(WTF::PassOwnPtr<blink::HTMLDocumentParser::ParsedChunk> popChunk) Line 503 C++
    webcore_shared.dll!blink::HTMLDocumentParser::pumpPendingSpeculations() Line 563    C++
    webcore_shared.dll!blink::HTMLDocumentParser::resumeParsingAfterYield() Line 308    C++
    webcore_shared.dll!blink::HTMLParserScheduler::continueParsing() Line 166   C++
    webcore_shared.dll!WTF::FunctionWrapper<void (__thiscall blink::HTMLParserScheduler::*)(void)>::operator()(blink::HTMLParserScheduler * c) Line 83  C++
    webcore_shared.dll!WTF::PartBoundFunctionImpl<1,WTF::FunctionWrapper<void (__thiscall blink::HTMLParserScheduler::*)(void)>,void __cdecl(blink::HTMLParserScheduler *)>::operator()() Line 179  C++
    blink_platform.dll!blink::CancellableTaskFactory::CancellableTask::run() Line 34    C++
    scheduler.dll!scheduler::WebSchedulerImpl::runTask(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > task) Line 45   C++
    scheduler.dll!base::internal::RunnableAdapter<void (__cdecl*)(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >)>::Run(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > <args_0>) Line 157   C++
    scheduler.dll!base::internal::InvokeHelper<0,void,base::internal::RunnableAdapter<void (__cdecl*)(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >)>,base::internal::TypeList<scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > > >::MakeItSo(base::internal::RunnableAdapter<void (__cdecl*)(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >)> runnable, scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > <args_0>) Line 294   C++
    scheduler.dll!base::internal::Invoker<base::IndexSequence<0>,base::internal::BindState<base::internal::RunnableAdapter<void (__cdecl*)(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >)>,void __cdecl(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >),base::internal::TypeList<base::internal::PassedWrapper<scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > > > >,base::internal::TypeList<base::internal::UnwrapTraits<base::internal::PassedWrapper<scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > > > >,base::internal::InvokeHelper<0,void,base::internal::RunnableAdapter<void (__cdecl*)(scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> >)>,base::internal::TypeList<scoped_ptr<blink::WebThread::Task,base::DefaultDeleter<blink::WebThread::Task> > > >,void __cdecl(void)>::Run(base::internal::BindStateBase * base) Line 346  C++
    base.dll!base::Callback<void __cdecl(void)>::Run() Line 396 C++
    base.dll!base::debug::TaskAnnotator::RunTask(const char * queue_function, const char * run_function, const base::PendingTask & pending_task) Line 64    C++
    scheduler.dll!scheduler::TaskQueueManager::ProcessTaskFromWorkQueue(unsigned int queue_index, bool has_previous_task, base::PendingTask * previous_task) Line 738   C++
    scheduler.dll!scheduler::TaskQueueManager::DoWork(bool posted_from_main_thread) Line 691    C++
    scheduler.dll!base::internal::RunnableAdapter<void (__thiscall scheduler::TaskQueueManager::*)(bool)>::Run(scheduler::TaskQueueManager * object, const bool & <args_0>) Line 176    C++
    scheduler.dll!base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__thiscall scheduler::TaskQueueManager::*)(bool)>,base::internal::TypeList<base::WeakPtr<scheduler::TaskQueueManager> const &,bool const &> >::MakeItSo(base::internal::RunnableAdapter<void (__thiscall scheduler::TaskQueueManager::*)(bool)> runnable, const base::WeakPtr<scheduler::TaskQueueManager> & weak_ptr, const bool & <args_0>) Line 304  C++
    scheduler.dll!base::internal::Invoker<base::IndexSequence<0,1>,base::internal::BindState<base::internal::RunnableAdapter<void (__thiscall scheduler::TaskQueueManager::*)(bool)>,void __cdecl(scheduler::TaskQueueManager *,bool),base::internal::TypeList<base::WeakPtr<scheduler::TaskQueueManager>,bool> >,base::internal::TypeList<base::internal::UnwrapTraits<base::WeakPtr<scheduler::TaskQueueManager> >,base::internal::UnwrapTraits<bool> >,base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__thiscall scheduler::TaskQueueManager::*)(bool)>,base::internal::TypeList<base::WeakPtr<scheduler::TaskQueueManager> const &,bool const &> >,void __cdecl(void)>::Run(base::internal::BindStateBase * base) Line 346   C++
    base.dll!base::Callback<void __cdecl(void)>::Run() Line 396 C++
    base.dll!base::debug::TaskAnnotator::RunTask(const char * queue_function, const char * run_function, const base::PendingTask & pending_task) Line 64    C++
    base.dll!base::MessageLoop::RunTask(const base::PendingTask & pending_task) Line 481    C++

相关处理代码如下:

namespace blink {
....

class History final : public GarbageCollectedFinalized<History>, public ScriptWrappable, public DOMWindowProperty {
    DEFINE_WRAPPERTYPEINFO();
    WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(History);
public:
    ....
    void pushState(PassRefPtr<SerializedScriptValue> data, const String& title, const String& url, ExceptionState& exceptionState)
    {
        stateObjectAdded(data, title, url, scrollRestorationInternal(), FrameLoadTypeStandard, exceptionState);
    }

History::stateObjectAdded 代码如下:

void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& /* title */, const String& urlString, HistoryScrollRestorationType restorationType, FrameLoadType type, ExceptionState& exceptionState)
{
    if (!m_frame || !m_frame->page() || !m_frame->loader().documentLoader())
        return;

    KURL fullURL = urlForState(urlString);
    if (!fullURL.isValid() || !m_frame->document()->securityOrigin()->canRequest(fullURL)) {
        // We can safely expose the URL to JavaScript, as a) no redirection takes place: JavaScript already had this URL, b) JavaScript can only access a same-origin History object.
        exceptionState.throwSecurityError("A history state object with URL '" + fullURL.elidedString() + "' cannot be created in a document with origin '" + m_frame->document()->securityOrigin()->toString() + "'.");
        return;
    }

    m_frame->loader().updateForSameDocumentNavigation(fullURL, SameDocumentNavigationHistoryApi, data, restorationType, type);
}

导航流程是:判断URL是否有效?——有效(Y)——重定向到新URL上。无效则抛出DOMError。m_frame->loader()将返回一个与当前页面关联的FrameLoader对象。

mutable FrameLoader m_loader;
inline FrameLoader& LocalFrame::loader() const
{
    return m_loader;
}

FrameLoader::updateForSameDocumentNavigation代码如下:

void FrameLoader::updateForSameDocumentNavigation(const KURL& newURL, SameDocumentNavigationSource sameDocumentNavigationSource, PassRefPtr<SerializedScriptValue> data, HistoryScrollRestorationType scrollRestorationType, FrameLoadType type)
{
    // Update the data source's request with the new URL to fake the URL change
    m_frame->document()->setURL(newURL);
    documentLoader()->setReplacesCurrentHistoryItem(type != FrameLoadTypeStandard);
    documentLoader()->updateForSameDocumentNavigation(newURL, sameDocumentNavigationSource);

    // Generate start and stop notifications only when loader is completed so that we
    // don't fire them for fragment redirection that happens in window.onload handler.
    // See https://bugs.webkit.org/show_bug.cgi?id=31838
    if (m_frame->document()->loadEventFinished())
        client()->didStartLoading(NavigationWithinSameDocument);

    HistoryCommitType historyCommitType = loadTypeToCommitType(type);
    if (!m_currentItem)
        historyCommitType = HistoryInertCommit;

    setHistoryItemStateForCommit(historyCommitType, sameDocumentNavigationSource == SameDocumentNavigationHistoryApi ? HistoryNavigationType::HistoryApi : HistoryNavigationType::Fragment);
    if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) {
        m_currentItem->setStateObject(data);
        m_currentItem->setScrollRestorationType(scrollRestorationType);
    }
    client()->dispatchDidNavigateWithinPage(m_currentItem.get(), historyCommitType);
    client()->dispatchDidReceiveTitle(m_frame->document()->title());
    if (m_frame->document()->loadEventFinished())
        client()->didStopLoading();
}

FrameLoader::setHistoryItemStateForCommit中维护需要提交的HistoryItem,并放置在RefPtrWillBeMember<HistoryItem> m_currentItem;中。

其中,DocumentLoader::updateForSameDocumentNavigation代码如下。

void DocumentLoader::updateForSameDocumentNavigation(const KURL& newURL, SameDocumentNavigationSource sameDocumentNavigationSource)
{
    KURL oldURL = m_request.url();
    m_originalRequest.setURL(newURL);
    m_request.setURL(newURL);
    if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) {
        m_request.setHTTPMethod("GET");
        m_request.setHTTPBody(nullptr);
    }
    clearRedirectChain();
    if (m_isClientRedirect)
        appendRedirect(oldURL);
    appendRedirect(newURL);
}

最终,添加History Entry处代码的调用栈如下:

>   content.dll!content::RenderFrameImpl::didCommitProvisionalLoad(blink::WebLocalFrame * frame, const blink::WebHistoryItem & item, blink::WebHistoryCommitType commit_type) Line 2665 C++
    content.dll!content::RenderFrameImpl::didNavigateWithinPage(blink::WebLocalFrame * frame, const blink::WebHistoryItem & item, blink::WebHistoryCommitType commit_type) Line 2950    C++
    blink_web.dll!blink::FrameLoaderClientImpl::dispatchDidNavigateWithinPage(blink::HistoryItem * item, blink::HistoryCommitType commitType) Line 405  C++
    webcore_shared.dll!blink::FrameLoader::updateForSameDocumentNavigation(const blink::KURL & newURL, blink::SameDocumentNavigationSource sameDocumentNavigationSource, WTF::PassRefPtr<blink::SerializedScriptValue> data, blink::HistoryScrollRestorationType scrollRestorationType, blink::FrameLoadType type) Line 651 C++
    webcore_shared.dll!blink::History::stateObjectAdded(WTF::PassRefPtr<blink::SerializedScriptValue> data, const WTF::String & __formal, const WTF::String & urlString, const blink::StateOptions & options, blink::FrameLoadType type, blink::ExceptionState & exceptionState) Line 159   C++
    webcore_shared.dll!blink::History::pushState(WTF::PassRefPtr<blink::SerializedScriptValue> data, const WTF::String & title, const WTF::String & url, const blink::StateOptions & options, blink::ExceptionState & exceptionState) Line 64

在didCommitProvisionalLoad中,我们可以看到render_view->UpdateSessionHistory(frame);的语句。在启动一个新导航前,Chrome会更新最后提交的Session History条目。这个操作是通过IPC消息来完成的。在一个重复循环的pushState操作中,这将在队列中产生大量的IPC消息,导致页面看起来产生了明显卡顿

void RenderViewImpl::SendUpdateState(HistoryEntry* entry) {
  if (!entry)
    return;

  // Don't send state updates for kSwappedOutURL.
  if (entry->root().urlString() == WebString::fromUTF8(kSwappedOutURL))
    return;

  Send(new ViewHostMsg_UpdateState(
      routing_id_, page_id_, HistoryEntryToPageState(entry)));
}

接着,Chrome使用render_view_->history_controller()->UpdateForCommit(this, item, commit_type, navigation_state->WasWithinSamePage());来提交历史项。Chrome通过CreateNewBackForwardItem(frame, item, navigation_within_page);来向历史列表添加一个后退项。

紧接着……
QQ图片20160429181600.jpg

不要紧,继续分析,Chromium/Chrome中限制了history对象的数量为50。这是标准的做法。接下来的问题就和《1》一样了,内存占用飙升,完全是因为字符串自拼接导致的,这种长字符串最终会要求G级别的内存空间。

f1.png

网页卡顿则是因为有大量IPC消息占用CPU(同时也有大量字符的拼接操作,也会占用CPU和内存)。在我的测试中,Chrome并没有崩溃。关闭Tab即可。所以这也是为什么Google不认为这是漏洞的原因。(确实也不是漏洞:))。

所以Chrome篇就这么结束了,有时间再看Safari。这块的处理估计只能看WebKit了。

一些有趣的问题(1)——Edge/IE是如何处理history.pushState的?

在活动(http://www.freebuf.com/fevents/102205.html)中我收到了很多有意思的问题,比如:
B8E587C9-F5AB-4775-9237-C546B57A87AA.png

打开view-source:http://crashsafari.com可以看到该网页的代码如下:
826D8DA3-4C20-477C-9E69-14F6F894D0D0.png

实际有用的内容也就是红框一段了。代码逻辑很简单,不断地pushState,这个操作会不断向object History中添加信息并立刻改变地址栏(但不导航)。在树莓派中添加下列页面:
0FEC22E4-1F56-4DCC-89B4-317D1B0F4A0C.png

使用Safari访问,发现Safari疯狂吃内存和CPU,但是只是因为过于密集的代码执行使页面不响应用户输入,但是整体还是在运行的(我可以清晰地看到脚本计时器在连续读秒)。
2DFEB608-1D65-4705-80CB-794EEAB16319.jpg

但是我显然等不到让他执行完了,时间实在是太长了。让我们简单地计算一下,在执行完后,history对象的length应该是多少?答案是大于等于100002(命令行打开/直接打开等的情况下也有可能是100001)。

就这么简单吗?当然不是,length只是元素个数。每个history条目中还保存了url信息,因此,这是一个简单的等差数列的累计。假设1个字符占用2字节(wchar_t),我们的base url是http://192.168.2.113/crashsafari.htm?一共37字节。

不考虑Base url,从i=0起,
到i=9时,数字长度是 1,2,3,4……,10
到i=99时,数字长度是 12,14,16……,190
到i=999时,数字长度是 193,196,199,……,2890
到i=9999时,数字长度是 2894,2898,……,6494
到i=99999时,数字长度是 6499,6504,……,51499
到i=100000时,数字长度是 51505。

看起来不大吗?现在把它们累加起来。同时,把37字节也加进每个条目中,最终我们的到的数字是:

var s = 0; 
var t = 0; 
for(i = 0; i < 100000; ++i)
{
    t += i.toString().length; 
    s += t;
} 
console.log(s);

s == 23939749495

再加上10万条,每条37字节的baseurl,将结果再乘2,得到最终内存占用47880238990字节(当然,这还是偏少的,因为每条History纪录都要维护额外的类信息)。最终计算得到至少需要44GB内存支撑。

虽然44GB看起来很吓人,但是对64位系统来说并不算什么,所以在系统上就一直分配分配,吃着系统内存。而在32位系统上可能就有点问题了,不过我没有32位的Mac,所以暂时没法测试,喊朋友帮在iOS做了测试,结果也是类似:没有崩溃。iOS的内存管理机制提示占用内存过高,只要拒绝再次分配即可终止代码执行。

21E717CFC8B755ED058874EB34C73D36.png

说了这么多,不如直接试一试Windows上的浏览器——IE和Edge。由于我的测试机器Fuzz过久风扇烧了,只剩下一台Windows 7 32Bit的电脑可以试验,因此这次我先尝试使用IE 11来执行上述代码。看看IE下的表现如何。因为是32位系统,没开任何内存扩展,所以理论上,分配到2GB就差不多了。

在IE11上执行,一段时间后浏览器就出现了未响应的提示。但是内存占用却很少。这是为什么呢?从代码看,IE对历史条目做了限制。数量是500-1209(不同版本的IE/Edge有区别),但是对比Chrome(100)和Safari(50),IE的数量明显要多,但是内存占用却最少,这是为什么呢?看来得再仔细看下它的机制了。

IE中历史操作的接口为IOmHistory(对应类COmHistory),对应F12里面的history对象。可以在MSDN查到它的相关信息:https://msdn.microsoft.com/en-us/library/hh774261(v=vs.85).aspx

因为今天机器修好了,所以,我就以Edge为样本开始分析吧。jscript9.dll中调用pushState后,传给CFastDOM::CHistory::Trampoline_pushState(PVOID, CallInfo*,PPVOID)去处理。trampoline的代码十分简单,我简单人肉反编译一下,原始代码大致如下(为了避免各种vtguard代码,以下使用的是64位EdgeHTML.dll):

class CFastDOM
{
    public:
        static PVOID CFastDOM::CHistory::Trampoline_pushState(PVOID pv, CallInfo* pci,PPVOID ppv)
        {
            CBase* pBase = m_base;
            IActiveScriptDirect * pIASD = ValidateCallT<0>(pv, ppv, pci, 1, 0x10B2, &pBase);
            DWORD dwResult = COmHistory::SetStateHelper(pIASD, ppv, pci & 0xffffff, 1);
            if(!dwResult)
            {
               CFastDOM::ThrowDOMError(pIASD, pci, dwResult, pBase);
            }
            return 0;
        }
}

反观这段代码,唯一“有效”(我们能看得见)的操作就是SetStateHelper了。这个函数里面,如果一切正常,Edge会更新窗口数据,为文档添加浏览记录(CDoc::AddTravelEntery(pWindow, TRUE);)、更新后退状态(CDoc::UpdateBackForwardState(TRUE);)并更新了历史记录管理器中的URL信息(HRESULT hr = COmHistory::UpdateUrl(pWindow, (OLECHAR*)&pszNewURL);)。

但是事实确实如此吗?显然不是,当传入的URL大于INTERNET_MAX_PATH_LENGTH (2048)字节时,COmHistory::PrepareUrlForUpdate调用的MSHTML!CMarkup::ExpandUrl中拼接URL的API CoInternetCombineUrl会返回0x80004003。

#define INTERNET_MAX_HOST_NAME_LENGTH   256
#define INTERNET_MAX_USER_NAME_LENGTH   128
#define INTERNET_MAX_PASSWORD_LENGTH    128
#define INTERNET_MAX_PORT_NUMBER_LENGTH 5           // INTERNET_PORT is unsigned short
#define INTERNET_MAX_PORT_NUMBER_VALUE  65535       // maximum unsigned short value
#define INTERNET_MAX_PATH_LENGTH        2048
#define INTERNET_MAX_PROTOCOL_NAME      "gopher"    // longest protocol name
#define INTERNET_MAX_URL_LENGTH         ((sizeof(INTERNET_MAX_PROTOCOL_NAME) - 1) \
                                        + sizeof("://") \
                                        + INTERNET_MAX_PATH_LENGTH)

0x80004003的含义是:E_POINTER | pwzResult is NULL, or the buffer is too small.

但这是为什么呢,这个API只是拼接URL而已,为什么会管URL长度?回头看MSHTML!CMarkup::ExpandUrl调用该API时的代码,传入的BUFFER只有2048字节呀。

hr = CoInternetCombineUrl(pwzBaseUrl, pwzRelativeUrl, dwCombineFlags, pszResult, INTERNET_MAX_PATH_LENGTH, &pcchResult, 0);

所以后续插入、更新操作都没有了,顺便还调用了ThrowDOMError,导致后面的代码都无法执行了(在IE/Edge中的体现是SCRIPT16387:无效指针,就是0x80004003的解释)。这也就能解释为什么Edge为啥如此“高效”了。所以Edge实际上做的是:

var s = 0; 
var t = 0; 
for(i = 0; i < 100000; ++i)
{
    t += i.toString().length;   
    **if(t > 2048) break;**
    s += t;
} 
console.log(s);

占用702425字。Chrome和Safari的我之后有空再看吧。

ARM指令集是个好玩的东西

最近在看ARM相关的东西,一直就没更新博客……把以前几个设置为隐藏的文章拿出来就当是新文章好了。

作为刚刚开始接触ARM的菜鸟,ARM指令集和x86某些程度上很相似,却有有好多好玩的东西,等研究出个啥来再发相关文章好了=。=

IE的execCommand

先看IE大老爷的execCommand。跟踪可以发现,IE支持下列execCommand指令:

.text:63C0DB06 ; ---------------------------------------------------------------------------
.text:63C0DB08 ; public: static struct CBase::CMDINFOSTRUCT const * const CBase::cmdTable
.text:63C0DB08 ?cmdTable@CBase@@2QBUCMDINFOSTRUCT@1@B dd offset loc_63848E05+3
.text:63C0DB08                                         ; DATA XREF: CBase::CmdIDFromCmdName(ushort const *,ulong *)+21r
.text:63C0DB08                                         ; CBase::CmdIDFromCmdName(ushort const *,ulong *)+29o ...
.text:63C0DB0C ; int dword_63C0DB0C[]
.text:63C0DB0C dword_63C0DB0C  dd 919h                 ; DATA XREF: CBase::CmdIDFromCmdName(ushort const *,ulong *)+52r
.text:63C0DB10                 dd offset aAutourldetect ; "AutoUrlDetect"
.text:63C0DB14                 dd 960h
.text:63C0DB18                 dd offset aCreatebookmark ; "CreateBookmark"
.text:63C0DB1C                 dd 84Bh
.text:63C0DB20                 dd offset aCreatelink   ; "CreateLink"
.text:63C0DB24                 dd 84Ch
.text:63C0DB28                 dd offset aInsertimage  ; "InsertImage"
.text:63C0DB2C                 dd 878h
.text:63C0DB30                 dd offset aBold_0       ; "Bold"
.text:63C0DB34                 dd 34h
.text:63C0DB38                 dd offset aTristatebold ; "TristateBold"
.text:63C0DB3C                 dd 5Fh
.text:63C0DB40                 dd offset aBrowsemode   ; "BrowseMode"
.text:63C0DB44                 dd 84Eh
.text:63C0DB48                 dd offset aEditmode     ; "EditMode"
.text:63C0DB4C                 dd 84Fh
.text:63C0DB50                 dd offset aInsertbutton ; "InsertButton"
.text:63C0DB54                 dd 877h
.text:63C0DB58                 dd offset aInsertiframe ; "InsertIFrame"
.text:63C0DB5C                 dd 86Eh
.text:63C0DB60                 dd offset aInsertinputbut ; "InsertInputButton"
.text:63C0DB64                 dd 843h
.text:63C0DB68                 dd offset aInsertinputche ; "InsertInputCheckbox"
.text:63C0DB6C                 dd 873h
.text:63C0DB70                 dd offset aInsertinputima ; "InsertInputImage"
.text:63C0DB74                 dd 842h
.text:63C0DB78                 dd offset aInsertinputrad ; "InsertInputRadio"
.text:63C0DB7C                 dd 874h
.text:63C0DB80                 dd offset aInsertinputtex ; "InsertInputText"
.text:63C0DB84                 dd 871h
.text:63C0DB88                 dd offset aInsertselectdr ; "InsertSelectDropdown"
.text:63C0DB8C                 dd 875h
.text:63C0DB90                 dd offset aInsertselectli ; "InsertSelectListbox"
.text:63C0DB94                 dd 876h
.text:63C0DB98                 dd offset aInserttextarea ; "InsertTextArea"
.text:63C0DB9C                 dd 872h
.text:63C0DBA0                 dd offset aItalic       ; "Italic"
.text:63C0DBA4                 dd 38h
.text:63C0DBA8                 dd offset aTristateitalic ; "TristateItalic"
.text:63C0DBAC                 dd 60h
.text:63C0DBB0                 dd offset aSizetocontrol ; "SizeToControl"
.text:63C0DBB4                 dd 23h
.text:63C0DBB8                 dd offset aSizetocontrolh ; "SizeToControlHeight"
.text:63C0DBBC                 dd 24h
.text:63C0DBC0                 dd offset aSizetocontrolw ; "SizeToControlWidth"
.text:63C0DBC4                 dd 25h
.text:63C0DBC8                 dd offset aUnderline_1  ; "Underline"
.text:63C0DBCC                 dd 3Fh
.text:63C0DBD0                 dd offset aTristateunderl ; "TristateUnderline"
.text:63C0DBD4                 dd 61h
.text:63C0DBD8                 dd offset aCopy         ; "Copy"
.text:63C0DBDC                 dd 0Fh
.text:63C0DBE0                 dd offset aCut          ; "Cut"
.text:63C0DBE4                 dd 10h
.text:63C0DBE8                 dd offset psz2          ; "Delete"
.text:63C0DBEC                 dd 11h
.text:63C0DBF0                 dd offset aPrint        ; "Print"
.text:63C0DBF4                 dd 5Dh
.text:63C0DBF8                 dd offset aJustifycenter ; "JustifyCenter"
.text:63C0DBFC                 dd 39h
.text:63C0DC00                 dd offset aJustifyfull  ; "JustifyFull"
.text:63C0DC04                 dd 32h
.text:63C0DC08                 dd offset aJustifyleft  ; "JustifyLeft"
.text:63C0DC0C                 dd 3Bh
.text:63C0DC10                 dd offset aJustifyright ; "JustifyRight"
.text:63C0DC14                 dd 3Ch
.text:63C0DC18                 dd offset aJustifynone  ; "JustifyNone"
.text:63C0DC1C                 dd 5Eh
.text:63C0DC20                 dd offset aPaste_0      ; "Paste"
.text:63C0DC24                 dd 1Ah
.text:63C0DC28                 dd offset aMsPastecontent ; "ms-pasteContentOnly"
.text:63C0DC2C                 dd 9C4h
.text:63C0DC30                 dd offset aMsPastetextonl ; "ms-pasteTextOnly"
.text:63C0DC34                 dd 9C5h
.text:63C0DC38                 dd offset aPlayimage    ; "PlayImage"
.text:63C0DC3C                 dd 8DFh
.text:63C0DC40                 dd offset aStopimage    ; "StopImage"
.text:63C0DC44                 dd 8E0h
.text:63C0DC48                 dd offset aInsertinputres ; "InsertInputReset"
.text:63C0DC4C                 dd 844h
.text:63C0DC50                 dd offset aInsertinputsub ; "InsertInputSubmit"
.text:63C0DC54                 dd 845h
.text:63C0DC58                 dd offset aInsertinputfil ; "InsertInputFileUpload"
.text:63C0DC5C ; ---------------------------------------------------------------------------
.text:63C0DC5C                 inc     esi
.text:63C0DC5D                 or      [eax], al
.text:63C0DC5F                 add     [eax], bh
.text:63C0DC61                 retf
.text:63C0DC61 ; ---------------------------------------------------------------------------
.text:63C0DC62                 dw 63C0h
.text:63C0DC64                 dd 847h
.text:63C0DC68                 dd offset aUnselect     ; "Unselect"
.text:63C0DC6C                 dd 7D7h
.text:63C0DC70                 dd offset aBackcolor    ; "BackColor"
.text:63C0DC74                 dd 33h
.text:63C0DC78                 dd offset aForecolor    ; "ForeColor"
.text:63C0DC7C                 dd 37h
.text:63C0DC80                 dd offset aFontname     ; "FontName"
.text:63C0DC84                 dd 12h
.text:63C0DC88                 dd offset aFontsize_0   ; "FontSize"
.text:63C0DC8C                 dd 13h
.text:63C0DC90                 dd offset aGetblockformat ; "GetBlockFormats"
.text:63C0DC94 ; ---------------------------------------------------------------------------
.text:63C0DC94                 mov     ecx, 6C000008h
.text:63C0DC99                 retf
.text:63C0DC99 ; ---------------------------------------------------------------------------
.text:63C0DC9A                 dw 63C0h
.text:63C0DC9C ; ---------------------------------------------------------------------------
.text:63C0DC9C                 mov     edx, 84000008h
.text:63C0DCA1                 retf
.text:63C0DCA1 ; ---------------------------------------------------------------------------
.text:63C0DCA2                 dw 63C0h
.text:63C0DCA4                 dd 88Ah
.text:63C0DCA8                 dd offset aInsertmarquee ; "InsertMarquee"
.text:63C0DCAC                 dd 886h
.text:63C0DCB0                 dd offset aInsertorderedl ; "InsertOrderedList"
.text:63C0DCB4                 dd 888h
.text:63C0DCB8                 dd offset aInsertparagrap ; "InsertParagraph"
.text:63C0DCBC                 dd 884h
.text:63C0DCC0                 dd offset aInsertunordere ; "InsertUnorderedList"
.text:63C0DCC4                 dd 889h
.text:63C0DCC8                 dd offset aOutdent      ; "Outdent"
.text:63C0DCCC                 dd 88Bh
.text:63C0DCD0                 dd offset aUioutdent    ; "UIOutdent"
.text:63C0DCD4                 dd 967h
.text:63C0DCD8                 dd offset aRedo         ; "Redo"
.text:63C0DCDC                 dd 1Dh
.text:63C0DCE0                 dd offset aRefresh      ; "Refresh"
.text:63C0DCE4                 dd 8FCh
.text:63C0DCE8                 dd offset aRemoveparaform ; "RemoveParaFormat"
.text:63C0DCEC                 dd 8CDh
.text:63C0DCF0                 dd offset aRemoveformat ; "RemoveFormat"
.text:63C0DCF4                 dd 8B6h
.text:63C0DCF8                 dd offset aSelectall    ; "SelectAll"
.text:63C0DCFC                 dd 1Fh
.text:63C0DD00                 dd offset aStrikethrough ; "StrikeThrough"
.text:63C0DD04                 dd 5Bh
.text:63C0DD08                 dd offset aSubscript    ; "Subscript"
.text:63C0DD0C                 dd 8C7h
.text:63C0DD10                 dd offset aSuperscript  ; "Superscript"
.text:63C0DD14                 dd 8C8h
.text:63C0DD18                 dd offset aUndo         ; "Undo"
.text:63C0DD1C                 dd 2Bh
.text:63C0DD20                 dd offset aUnlink       ; "Unlink"
.text:63C0DD24                 dd 84Dh
.text:63C0DD28                 dd offset aInserthorizont ; "InsertHorizontalRule"
.text:63C0DD2C                 dd 866h
.text:63C0DD30                 dd offset aUnbookmark   ; "UnBookmark"
.text:63C0DD34                 dd 850h
.text:63C0DD38                 dd offset aOverwrite    ; "OverWrite"
.text:63C0DD3C                 dd 90Ah
.text:63C0DD40                 dd offset aInsertinputpas ; "InsertInputPassword"
.text:63C0DD44                 dd 909h
.text:63C0DD48                 dd offset aInsertinputhid ; "InsertInputHidden"
.text:63C0DD4C                 dd 908h
.text:63C0DD50                 dd offset aDirltr       ; "DirLTR"
.text:63C0DD54                 dd 92Eh
.text:63C0DD58                 dd offset aDirrtl       ; "DirRTL"
.text:63C0DD5C                 dd 92Fh
.text:63C0DD60                 dd offset aBlockdirltr  ; "BlockDirLTR"
.text:63C0DD64                 dd 930h
.text:63C0DD68                 dd offset aBlockdirrtl  ; "BlockDirRTL"
.text:63C0DD6C                 dd 931h
.text:63C0DD70                 dd offset aInlinedirltr ; "InlineDirLTR"
.text:63C0DD74                 dd 932h
.text:63C0DD78                 dd offset aInlinedirrtl ; "InlineDirRTL"
.text:63C0DD7C                 dd 933h
.text:63C0DD80                 dd offset aSaveas       ; "SaveAs"
.text:63C0DD84                 dd 47h
.text:63C0DD88                 dd offset aOpen         ; "Open"
.text:63C0DD8C                 dd 7D0h
.text:63C0DD90                 dd offset aStop         ; "Stop"
.text:63C0DD94                 dd 85Ah
.text:63C0DD98                 dd offset aMultipleselect ; "MultipleSelection"
.text:63C0DD9C                 dd 959h
.text:63C0DDA0                 dd offset a2dPosition   ; "2D-Position"
.text:63C0DDA4                 dd 95Ah
.text:63C0DDA8                 dd offset aAbsolutepositi ; "AbsolutePosition"
.text:63C0DDAC                 dd 95Dh
.text:63C0DDB0                 dd offset aLiveresize   ; "LiveResize"
.text:63C0DDB4                 dd 95Eh
.text:63C0DDB8                 dd offset aKeepselection ; "KeepSelection"
.text:63C0DDBC                 dd 96Ah
.text:63C0DDC0                 dd offset aRespectvisibil ; "RespectVisibilityInDesign"
.text:63C0DDC4                 dd 965h
.text:63C0DDC8                 dd offset aUnloaddocument ; "UnloadDocument"
.text:63C0DDCC                 dd 96Bh
.text:63C0DDD0                 dd offset aClearauthentic ; "ClearAuthenticationCache"
.text:63C0DDD4                 dd 3A9Bh
.text:63C0DDD8                 dd offset aBackgroundim_2 ; "BackgroundImageCache"
.text:63C0DDDC                 dd 97Eh
.text:63C0DDE0                 dd offset aDefaultparagra ; "DefaultParagraphSeparator"
.text:63C0DDE4                 dd 0F3Ch
.text:63C0DDE8                 dd offset aMsBeginundouni ; "ms-beginUndoUnit"
.text:63C0DDEC                 dd 0F3Dh
.text:63C0DDF0                 dd offset aMsEndundounit ; "ms-endUndoUnit"
.text:63C0DDF4                 dd 0F3Eh
.text:63C0DDF8                 dd offset aMsClearundosta ; "ms-clearUndoStack"
.text:63C0DDFC                 dd 0F3Fh
.text:63C0DE00                 dd offset aHilitecolor  ; "HiliteColor"
.text:63C0DE04                 dd 33h, 2 dup(0)
.text:63C0DE10                 db 5 dup(90h)

在网页中调用execCommand之后,在IE中分别经过了:

CFastDOM::CDocument::Trampoline_execCommand(void *,CallInfo,...)
CDocument::execCommand(ushort *,short,tagVARIANT,short *)
CBase::execCommand(ushort * const,short,tagVARIANT)

三步,CBase继承于CDocument嘛,其他细节不用在意。

想起来再写