2009年5月7日 星期四

布達佩斯忙裡偷閒

這個禮拜被派去匈牙利首都布達佩斯參加會議,第一次一個人跑到地球的另外一邊,出發前心情有點忐忑。台北沒有直達布達佩斯的班機,所以必須先飛香港、轉機到德國、再轉小飛機到布達佩斯。
在慕尼黑的時候小緊張了一下,被海關當做偷渡客 ,不過因為匈牙利簽證天衣無縫(廢話本來就是真的) 也有回程機票,就讓我走了。哈哈!
 P1020483
Budapest給我的感覺有點介於台南與花蓮市之間 ,建築挺漂亮,但是現代化的程度還ok而已。物
價在歐洲應該算偏低,比台北略高吧!一份麥當勞大約快兩百台幣。想要用少少的經費體驗歐洲風情,這裡算是自助不錯的選擇。

抵達之後,立刻到information center換福林以及買Budapest card。
我買的Budapest card是三日票,可以免費達乘大眾交通工具,多處景點也有折扣。
不過如果只需要免費坐捷運跟公車,推薦更划算的One-day travel card,每人1500福林。
如果算的更精一點,捷運及公車的單程票是290福林,制定好計畫是有可能把一天的車費降的更低一些。建議至少第一天買張One-day travel card,來回坐公車捷運熟悉一下街道以及方向。

隨後做200E公車前往捷運站Kobanya-Kispest,路上遇到馬來西亞華僑,閒聊了一下.。
到捷運站之後,坐了12站抵達旅館附近的站點Nyugati pu。
P1020447

說是捷運,其實只不過是我們的台鐵普通車在地底下跑,挺有歷史的。
出了捷運站找了一下路,最後順利抵達旅館check-in。
發了報平安的訊息之後,衣服也沒換澡也沒洗,立刻出門找market買水。
Budapest沒有看到國際連鎖知名的超商,為了找買水的地方,第一天就破了自助旅行的大忌,一個人在小巷子裡子來走去。最後眼尖看見一間店鋪有賣水,買了水之後出門想認 一下標誌,原來布達佩斯的超商外面掛的牌子是可口可樂的標誌,還會有一個0-24的牌子。

回旅館休息,洗了澡小憩片刻,安排了幾個景點之後又出門了。
第一站坐捷運來到Opera,這座Opera有超過百年的歷史,本來想買張票,但是5/1剛好公休。只好前往下一站位於Kossuth ter站的國會大廈。之前就聽說布達佩斯的國會大廈是歐洲最美,哈,就來見識一下有多美。
P1020457
結果一出站,映入眼簾的國會大廈果然美的讓我停止呼吸... 不過!!5/1似乎也沒有開放內部導覽,扼腕阿!! 好吧,那只好去國會對面的博物館繞繞,這間博物館應該是以前王宮貴族的家改造的。內部雖然只有三層,卻大到讓我迷路。不過展出的東西實在是沒什麼,有騙錢的嫌疑。(門票600福林)

參觀完以後,坐上國會門口的2路電車,沿著多瑙河欣賞一下風光.當電車開到Erzsebet橋
附近時,決定上橋走走.這一走就開啟了兩小時的走路旅程...
倒也不是橋太長走不完,而是走到另外一端時發現許多觀光客朝一個小丘的階梯拾級而上.
我也就跟上瞧瞧有什麼好玩的. 爬到一半竟然看到St.Gellert的雕像!
這是原本想要來的景點,但因為沒有大眾交通工具直達而放棄.想不到竟然莫名其妙的就到了...XDXD
P1020475

繼續向上爬,爬到飢腸轆轆氣喘如牛,但是沿途卻賞了不少配斯以及多瑙河的好風景.最後終於到了丘頂,看到了Liberation Monument,趕快買了支雪糕補充體力.XD
註:後來才知道在M2的Astoria站搭乘47,49路面電車過多瑙河在蓋雷特酒店前下車,那站是蓋雷特山比較正統的登高起點.還可以洗溫泉喔!山上看夜景也是很美的!

下了小丘又沿著橋走回配斯,途中順著人群到達一個戶外演唱會的地點,人很多很high,可惜聽不懂.看地圖決定去著名的Vaci大街瞧瞧.這條街是以餐館跟紀念品聞名,相當漂亮的歐洲大街.話說歐洲人好像很喜歡在戶外吃,就連漢堡王也要在店外面圍一圈讓客人坐. Vaci street的另外一端是Central Marcket,最大的傳統市場.但是今天竟然休市! 這時候我才發覺這城市怎麼了,沿街的商店幾乎都是休市,遊客似乎比居民還多XD 可能5/1是什麼重大節日吧.那只後之後有緣再來拜訪了.

走了一下午腳也酸了.乾脆坐上49路電車,來來回回的欣賞布達佩斯的城市風光.最後時差發作,竟然在電車上睡著了哈哈.買了麥當勞跟水之後就回飯店休息了.

(Day 2)
P1020487 
今天早上出門,特地先去參觀了旅館附近的火車站.整體感覺挺有歷史的,沒有剪票口,要上車是滿容易的,哈哈。 出了火車站搭上四路公車,前往Moszcva廣場.途中經過Margit橋,可以到達號稱多瑙河綠寶石的Margit island,如果星期日有時間的話再來參觀.(六路也可以到)

到了Moszcva廣場滿滿的都是人,這裡不只是布達城捷運與公車的交會點。也是前往城堡山的搭車起點.上坡之後搭上16路公車,行駛一小段之後我就後悔了.因為城堡山其實算個小丘.不陡路途也不長,其實讓公車開一站進入城堡山之後,就可以下車步行.當機立斷,馬上在城堡山城門口下車,開始了一整天的暴走 XDXD
P1020502

城門口附近是數間博物館,有的展覽兵器,也有的是歷史文物.布達佩斯身為一個遊客比居民多的城市,廣設博物館是很聰明的事情.整個城市博物館總數應該有五十座,不知道要逛哪邊的話,就逛逛博物館吧! 第二聰明的事情就是公共廁所要收錢.博物館沒興趣,廁所總是要上的吧XD 在門口收錢的可不是凶神惡煞的地頭蛇,是文質彬彬的政府公職,旁邊還擺一台收銀機可以找錢XD.

扯遠了,入了城堡山門口之後變開始步行,印入眼簾的是漂亮的建築物以及壯觀的教堂.參觀了漁夫堡,馬提亞斯教堂,最令我印象深刻的還是Royal Palace.匈牙利皇室似乎喜歡以威猛的雄獅為誌,到處可見栩栩如生的獅像.
P1020527

城堡山的景色實在是很美,不論是建築物的壯觀,或是向山下眺望多瑙河,都讓人流連忘返.
P1020537

中午在皇宮附近用餐之後,在附近看好了一條下山小徑,沿著小徑行走,不一會兒就抵達百年歷史的Szechenyi chain bridge.
P1020550
過了橋之後買了支雪糕在公園裝悠閒,這裡剛好位於 M1捷運的起點Vorosmarty,那就去昨天無緣的Oprea吧! 今天果然有開張!晚上歌劇最便宜有500福林的座位,不過要聽到九點半. 一個小時的參訪要價3000福林...好詭異的比例,讓我考慮一下吧...

買了一些紀念品之後,繼續沿著歐洲最古老的地鐵M1 前往Szechenyi furdo站.這一站可以去泡溫泉,聽說容易遇到同性戀 XDXD

一出站就是中央公園,許多匈牙利人在這裡遊玩,很熱鬧. 往Andrassy大道的方向走(可以感受到地鐵在腳下震動@"@),不一會兒就到達英雄廣場.這座廣場是百年前為了紀念匈牙利建國一千年而造,有許多匈牙利英雄的雕像,非常漂亮,還遇到新人來這裡拍婚紗,實在是很幸福呢  ^^Y
P1020567 
出了英雄廣場之後,又沿著Andrassy這條很舒服的大道走了一站,Bajza u.我遇到了11路公車的極限,一整天的行走讓我左腳膝關節不舒服.只好去超市買了水,牛奶跟麵包。然後回到旅館休息.

傍晚六點半,躺在床上還在想晚餐要吃什麼,疲倦感襲來,就這樣睡了11個小時. XDXD

2009年4月3日 星期五

Windows學習地圖

Slide2

1.“Windows via C/C++”, “The old new thing”
2.“Windows NT 2000 Native API Reference”
3. “Windows Internals ”, “Memory management algorithm and implementation in c/c++ ”
4.“Windows Internals ”
5.“Programming the Windows Driver Model””Windows Internals ”, OSR Nt Insider  6.Winddk\src\filesys\filter\sfilter,filespy ,OSR
7.Winddk\src\filesys\filter\miniFilter\passThrough, minispy, ctx , MS file system , OSR
8.Winddk\src\filesys\fastfat,“ Windows NT File System Internals ”, OSR
9.“天書夜讀”“Undocumented Windows 2000 Secrets”, Sysinternals forum
10.“Intel® 64 and IA-32 Architectures Software Developer’s Manual”,”自己動手寫作業系統”

Debugging and trouble shooting
1.”軟件調適”
2. ”Advanced Windows Debugging”
3. “Memory Dump Analysis Anthology”
4. Windbg

註:這是一份Windows系統的學習地圖。所記錄的是Win32 API以及更底層的系統相關資料。不包含硬體及網路相關。學海無涯,僅取最具代表性之參考資料。

2009年3月25日 星期三

百聽不膩

    有人古典音樂可以百聽不膩,可惜我沒有那個福氣與慧根,不論如何用心欣賞都會想睡。爵士樂就好多了,尤其是Bill EvansThelonious Monk這兩位大師的曲子,可以一邊聽一邊思考問題、撰寫程式。

    腦海中的爵士樂手的名字非常之少,能說出名字的只有這兩位。(Thelonious Monk的first name還不會念,常常以專輯名稱Solo Monk來稱呼之。)介紹這兩位大師的音樂給我的正是恩師,路德維希先生

明天是老師重要的日子,這裡以一曲輕快的I hadn't anyone till you,預祝老師一切順利Smile

 

2009年3月15日 星期日

Nt Insider : 常用的Windbg技巧(3)

相關知識:Windows kernel driver,x86 knowledge, Windbg command
原文連結

技巧4::幫助發現記憶體洩漏的中斷點:記憶體標籤(Tag)

    在開發Windows Kernel Driver的時候,最常使用的記憶體配置的函式之一是ExAllocatePoolWithTag。此函式可以指定一個四個字的標籤(Tag),而配置出來的記憶體便會"貼上"這個標籤。在記憶體洩漏(memory leak)的情況,此標籤就成為解決問題的重要工具!
    不過在使用記憶體標籤功能之前,如果系統的不是Windows 2003、Vista或是更新的版本,那我們需要調整一下系統設定。下載Windbg並且安裝完成之後,此時會發現同一包裡面除了Windbg還有另一個小工具:GFlags。這個小工具讓我們可以藉由UI對系統的各項設定進行調整,好處就是不用去死記這些設定對應的Registry。繼續偵錯之前,先讓我們把"Enable Pool Tagging"打開:GFlag
    題外話,我覺得周大師這篇"Live Debugging環境設定"寫的很棒。
    回到本文,現在我們覺得有Memory leak發生,想中斷在ExAllocatePoolWithTag偵錯。除了前一篇所提利用IAT位置來中斷,也可以使用條件中斷,檢查ExAllocatePoolWithTag的第三個參數,如果是我們的標籤就中斷,其餘放行。
    假設標籤是"BaPd",轉成ACSII碼 B = 42、a = 61、P = 50、d = 64。因為Intel-CPU處理資料的時候遵守Little endian(小頭派)的運作原則,所以開頭的資料會放在記憶體的低位(B在記憶體的地址比a少一個位元組,a又比P少一個位元組),但是在Windbg顯示32bits的時候是由高位(也就是d)到低位(最低的是開頭的B)顯示,所以會看到d(64)、P(50)、a(61)、B(42),串起來就是64506142。依照本系列第一篇所提及的條件中斷語法:

   1: bp nt!ExAllocatePoolWithTag ".if(poi(@esp+0xC) == 64506142){da @esp+0xC L4;k}.else{g}"

    這樣就會在每次(呼叫ExAllocatePoolWithTag) + (Tag = "BaPd")的時候,列印出標籤、堆疊的內容並且中斷。但缺點是條件中斷會讓系統效能明顯的降低X10。由於標籤實在是太重要了,Windows提供了另外一種方法:用Windbg以ed(enter value)指令把想要中斷的標籤輸入到nt!poolhittag,如此一來每當屬於該標籤的記憶體被配置,系統會中斷。記得Intel的小頭政策,輸入標籤時要反向輸入CPU才看的懂。
    下面這個例子我們先清掉原來的條件中斷點,然後輸入'dPaB'到nt!poolhittag,待中斷發生時檢查堆疊與標籤內容是否是我們想要的:

   1: kd> bl
   2:  0 e 82734b10     0001 (0001) nt!ExAllocatePoolWithTag ".if(poi(@esp+0xC) == 64506142){da @esp+0xC l4;kvn}.else{g}"
   3: kd> bc 0
   4: kd> ed nt!PoolHitTag 'dPaB'
   5: d> g
   6: Break instruction exception - code 80000003 (first chance)
   7: nt!DbgBreakPoint:
   8: 8269ef0c cc              int     3
   9: kd> kvn
  10:  # ChildEBP RetAddr  Args to Child              
  11: 00 8273ca88 82735401 80809f70 83d816f8 80829ae0 nt!DbgBreakPoint (FPO: [0,0,0])
  12: 01 8273cadc 829a66c6 00000000 00000038 64506142 nt!ExAllocatePoolWithTag+0x8eb
  13: 02 8273cafc 829acc8f 80831cb8 82743100 00000000 nt!BootApplicationPersistentDataInitialize+0x50
  14: 03 8273cca0 8292ab03 8080a648 39332b42 83540c00 nt!InitBootProcessor+0x412
  15: 04 8273cd0c 82733804 827433c0 82743100 8273d000 nt!KiInitializeKernel+0x770
  16: 05 00000000 00000000 00000000 00000000 00000000 nt!KiSystemStartup+0x32c
  17: kd> da 8273cadc+0x10 L4
  18: 8273caec  "BaPd"

技巧5:手動重建堆疊

    分析dump的時候,如果遇到沒有symbol的module,又恰巧它用了Frame pointer omission的最佳化,在函式的開頭跟結尾省去了對ebp的操作,那麼stack的分析很可能就落的沒有結果,譬如下面的這個例子,Windbg分析堆疊最後斷在savrt,無法得知堆疊更早的資訊,對於偵錯幫助有限:

   1: bd74e438 8045163d nt!IopParseDevice+0xa04
   2: bd74e4ac 804a4e8a nt!ObpLookupObjectName+0x4d5
   3: bd74e5bc 80496b85 nt!ObOpenObjectByName+0xc5
   4: bd74e690 be7728c6 nt!IoCreateFile+0x3ec
   5: bd74e6e0 be77382b savrt+0x458c6
   6: bd74e6ec be7752a5 savrt+0x4682b
   7: bd74e768 be744773 savrt+0x482a5
   8: bd74e77c be742e4b savrt+0x17773
   9: bd74e7a4 be745e75 savrt+0x15e4b
  10: 00000000 00000000 savrt+0x18e75

    怎棒?幸好k指令(Display stack backtrace)可以指定參數,讓Windbg重新分析堆疊,進而逼近原始的真實資料。

k = ebp esp eip

    大致流程是這樣的,當發生先前所舉的那個情況,偵錯者便印出堆疊上的所有資訊,然後依照x86堆疊運作規則,以及邏輯推理,判斷"斷掉的"堆疊所記錄的ebp、esp以及eip(我覺得,應該看成ReturnAddress),然後餵給k指令。其實Call stack的整個追蹤就是靠這三個資訊,Windbg也是使用這三個訊息,反推出整個Call Stack的"長相"。Nt Insider:Stacking the deck是一篇很不錯的參考。
    怎麼起頭呢?我們先在堆疊上找一個"可能是正確"的ebp。ebp會有什麼特徵?ebp會存放前一個stack frame的ebp之位置,而ebp+4則是存放了return address。首先注意到的是第21行:

   1: kd> dps bd74e7a4
   2: bd74e7a4  00000000
   3: bd74e7a8  be745e75 savrt+0x18e75
   4: bd74e7ac  e5d590b0
   5: bd74e7b0  e369d7a8
   6: bd74e7b4  be74355a savrt+0x1655a
   7: bd74e7b8  bd74e7c8
   8: bd74e7bc  e369d7a8
   9: bd74e7c0  e369d7a8
  10: bd74e7c4  be741b0f savrt+0x14b0f
  11: bd74e7c8  00000000
  12: bd74e7cc  e5da1e68
  13: bd74e7d0  e5da1e70
  14: bd74e7d4  be741c26 savrt+0x14c26
  15: bd74e7d8  00000000
  16: bd74e7dc  e5da1e70
  17: bd74e7e0  e1bffb48
  18: bd74e7e4  be8329bc SYMEVENT+0xd9bc
  19: bd74e7e8  00000000
  20: bd74e7ec  e5da1e70
  21: bd74e7f0  bd74e810
  22: bd74e7f4  be834e74 SYMEVENT+0xfe74
  23: bd74e7f8  e5da1e70
  24: bd74e7fc  e1374c28
  25: bd74e800  e5da1e68
  26: bd74e804  bd74e870
  27: bd74e808  e1374c30
  28: bd74e80c  e1374c28
  29: bd74e810  bd74e824
  30: bd74e814  be835023 SYMEVENT+0x10023
  31: bd74e818  e5da1e68
  32: bd74e81c  bd74e870
  33: bd74e820  e1374c48
  34: bd74e824  bd74e838
  35: bd74e828  be82b07f SYMEVENT+0x607f
  36: bd74e82c  bd74e870
     接著發生的事情就要靠一點強記了。取21行(第一個frame)的下一行之值為eip,取29行(第二個frame)之值作為ebp,ebp之值-8為esp。根據我的假設,原文esp的取法是一個隨意,按照Intel CPU的規則esp<ebp,所以這裡指定ebp-8。
   1: kd> k = bd74e810 bd74e810-8 be834e74
   2: ChildEBP RetAddr  
   3: WARNING: Stack unwind information not available. Following frames may be wrong.
   4: bd74e810 be835023 SYMEVENT+0xfe74
   5: bd74e824 be82b07f SYMEVENT+0x10023
   6: bd74e838 be833d28 SYMEVENT+0x607f
   7: bd74e854 be82c7b9 SYMEVENT+0xed28
   8: bd74e894 bfd7fd2a SYMEVENT+0x77b9
   9: bd74e8fc 8041fb8b <EDIT - removed module name>+0xcd2a
  10: bd74e910 8049c945 nt!IopfCallDriver+0x35
  11: bd74ea98 8045163d nt!IopParseDevice+0xa04
  12: bd74eb0c 804a4e8a nt!ObpLookupObjectName+0x4d5
  13: bd74ec1c 80496b85 nt!ObOpenObjectByName+0xc5
  14: bd74ecf0 80497f27 nt!IoCreateFile+0x3ec
  15: bd74ed30 80465691 nt!NtCreateFile+0x2e
  16: bd74ed30 77f8f9c5 nt!KiSystemService+0xc4
  17: 0006e808 00000000 ntdll!NtCreateFile+0xb
    個人覺得這是最難說明的技巧,一方面是在下的文筆與翻譯不好,二方面是因為需要的相關知識太多,很難言傳。如果覺得難以了解,不妨記得K可以指定三個參數,如果遇到Windbg顯示堆疊不對勁,嘗試餵參數給k指令是一個方法。

2009年3月11日 星期三

除錯經驗分享(1):EIP = 0所造成的Crash

相關知識:x86架構、Compiler、Windbg指令

今天遇到一個值得分享的問題,先看一下Crash發生時候的Stack:

   1:   
   2:  STACK_TEXT:  
   3:  WARNING: Frame IP not in any known module. Following frames may be wrong.
   4:  f7a22c60 f7944029 0000ffff f7a22c7c f794407a 0x0
   5:  f7a22c6c f794407a 0000ffff bb40e64e f7a22d4c BasicDriver!CheckParameterAndCallHelperFunction+0x19 [d:\XXX\basicdriver.c @ 25]
   6:  f7a22c7c 805777ff 824e5cc0 82328000 00000000 BasicDriver!DriverEntry+0x1a [d:\XXX\basicdriver.c @ 36]
   7:  f7a22d4c 8057790f 800009a8 00000001 00000000 nt!IopLoadDriver+0x66d
   8:  f7a22d74 80535c12 800009a8 00000000 827ba388 nt!IopLoadUnloadDriver+0x45
   9:  f7a22dac 805c71ec f7a76cf4 00000000 00000000 nt!ExpWorkerThread+0x100
  10:  f7a22ddc 80542de2 80535b12 00000001 00000000 nt!PspSystemThreadStartup+0x34
  11:  00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

請看奇怪的第四行,Symbol Path都設定了,怎麼會有0x0出現呢?再來看看當時的暫存器的值:

   1:  kd> .cxr 0xfffffffff7a22898
   2:  eax=0000ffff ebx=00000000 ecx=bb40e64e edx=8054be20 esi=e1f8b780 edi=824e5cc0
   3:  eip=00000000 esp=f7a22c64 ebp=f7a22c6c iopl=0         nv up ei pl nz na pe nc
   4:  cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00210206
   5:  00000000 ??              ???

    第三行的eip竟然顯示為0x00000000,難道今天忘記放乖乖在電腦上,所以硬體產生問題嗎?來檢查Crash發生時的x86指令(!analyze -v):

   1:  FOLLOWUP_IP: 
   2:  BasicDriver!CheckParameterAndCallHelperFunction+19 [d:\XXX\basicdriver.c @ 25]
   3:  f7944029 5d              pop     ebp
   4:   
   5:  FAILED_INSTRUCTION_ADDRESS: 
   6:  +689e952f01edd960
   7:  00000000 ??              ???

    由第7行可知是位於0x00000000的無意義資料,可見得機器的確是因為執行了EIP=0x00000000這個地方的東西而掛掉。第2、3行給了我們一些線索,呼叫0x00000000的犯人位於0xf7944029附近,讓我們來看一下CheckParameterAndCallHelperFunction所包含的指令:

   1:  kd> uf BasicDriver!CheckParameterAndCallHelperFunction
   2:  BasicDriver!CheckParameterAndCallHelperFunction [d:\XXX\basicdriver.c @ 17]:
   3:     17 f7944010 8bff            mov     edi,edi
   4:     17 f7944012 55              push    ebp
   5:     17 f7944013 8bec            mov     ebp,esp
   6:     19 f7944015 837d0800        cmp     dword ptr [ebp+8],0
   7:     19 f7944019 7d04            jge     BasicDriver!CheckParameterAndCallHelperFunction+0xf (f794401f)
   8:     21 f794401b 32c0            xor     al,al
   9:     21 f794401d eb0a            jmp     BasicDriver!CheckParameterAndCallHelperFunction+0x19 (f7944029)
  10:     24 f794401f 8b4508          mov     eax,dword ptr [ebp+8]
  11:     24 f7944022 50              push    eax
  12:     24 f7944023 ff15186094f7    call    dword ptr [BasicDriver!LittleHelper+0x8 (f7946018)]
  13:     25 f7944029 5d              pop     ebp
  14:     25 f794402a c20400          ret     4

    有人說每寫一行組語,就會嚇跑一半的讀者Tongue out我們只要簡單看幾個地方,首先是找到第13行的0xf7944029,跟!analyze -v說的一樣,是pop ebp。前一個指令,也就是第12行的地方會呼叫存放於f7946018的函式,讓我們來檢查一下:

   1:  kd> dd f7946018 L1
   2:  f7946018  00000000

    真是乾淨的清潔溜溜。原來錯誤是發生在程式使用了一個沒有初始化的函式指標(function pointer),而call指令會把新指令的位置設定給eip,所以就造成了eip=0x00000000的現象摟!

BOOLEAN CheckParameterAndCallHelperFunction(int iValue){
 if (iValue <0)
         return FALSE;

 return (fpHelperFunction)(iValue);
}


另外,Compiler可能會把function開頭的push ebp以及尾端的三個指令最佳化:

   1:     ......
   2:     17 f7944012 55              push    ebp
   3:     ......
   4:     24 f7944023 ff15186094f7    call    dword ptr [BasicDriver!LittleHelper+0x8 (f7946018)]
   5:     25 f7944029 5d              pop     ebp
   6:     25 f794402a c20400          ret     4
   7:                                最佳化,以jmp取代上述四個指令:
   8:                                 jmp [f7946018]

    也就是說,速度上可以節省掉push/pop ebp兩個指令。以jmp取代call的好處是jmp不會退回return address,那執行完會到哪邊呢?會回到上一次用call的地方,也就是本例中的BasicDriver!DriverEntry。這也是一個加速(反正都在尾巴了,乾脆直接的返回更上一層的function)這樣最佳化對程式很好,但是分析dump的時候負擔也就會增加了。