DIY 書套

 DSC05765_rescale DSC05764_rescale

不喜歡書本高高低低的,尤其還是同一家出版社的,這樣買書套很麻煩。不過離開小學後,好像只剩漫畫書比較容易塞進書套裡頭。

工具

先簡單知道一下什麼是:(參考這裡這裡

  1. 封面
  2. 封底
  3. 書背 

請先準備:prepare

  1. 書套:不知道有沒有比較正式的名稱,平時我都去重慶南路上的亞典書局買的,不過這分店已經結束營業了,唉!他們店內販售的外文書籍也是用這種書套包起來。  
  2. 切割尺:它有一面鑲有鐵片,不怕切割時刮到,上頭也有許多方格幫忙定位,價錢有點高。  
  3. 長尺:切割尺比較沉,有時候切割上不方便,準備一把短些、靈活的長尺比較方便,當然也要找上頭有方格的。
  4. 刀片:好的刀片很重要~
  5. 紙膠帶:很多膠帶會脫膠,會讓書本黑黑、黏黏的,紙膠帶是不錯的選擇。
  6. 電池:不一定要是電池,只要能用來壓線、方便靈活就可以。

動手吧~

 Step1Step2

Step1. 把書放在書套偏右,上下約莫留 8cm (視喜好而定),然後裁割。

Step3

Step2. 以書背為基準,把書本“橋”到書套中間

Step3-b Step3-a

Step3. 這邊,我們從封面做起,書背朝自己,大約捏出一條折線

Step4-a Step4-b

Step4-c Step4-d

Step4. 把封面翻過來朝桌面,再壓一次折線,這折線很重要,等會密不密實都靠它了。電池這邊派上用場了,用來壓實,這可是用手指無法比擬的。

 Step5-a Step5-b

Step5. 稍微把書本後退,然後再用電池壓實折線,最後也可以把書本退開,從頭用電池再壓一次。用這麼多次,知道這折線有多重要了吧?!

Step6

Step6. 封底也是同樣作法。這邊忘了拍一張照片作為參考,不過可以看上圖:把藍線以右假裝沒看到,就是封面、封底跑完 Step4 ~ 5 後的樣子。

Step7

Step7. 準備裁掉過長的部份,這邊用長尺書底做出一個直角,然後用刀片切出一條線。總共有四條線要切

Step8-a 

Step8-a. 四條線切完後,把書本拿開,但不要把兩旁折頁(紅色區域)打開,就會像上圖一樣,隱隱約約還可以看到切線(藍色表示)。

Step8-b 
Step8-b. 將書套壓平,基於兩點成一直線的原理,我們已經可以知道書的上下緣線。沿著藍線頂點構成的紅線下刀。但別急著把尺移開。

Step9-a 

Step9-a. 尺夠長的話,可以看到它橫跨過書套的折頁,也就是書的緣線,輕輕劃過,做個劃線記號就好,除非刀功一流,保證不會割到隔一層的書套去。

Step9-b

Step9-b. 四個角落都輕劃過完,可以看到 Step9-a 的劃線和 Step5 的折線形成一個四邊形,就裁掉它們吧~

Step9-c Step9-d

Step9-c. 看到這個像雙十字的形狀了嗎?這就是書套的形狀了。這時有個修正的機會,以紅線為準,去切掉不直的藍線。這誤差通常來自書套沒壓平,所以往往尾端會有些許多出來。

 Step10-a Step10-b

Step10-a. 先把書套折頁翻過去,再把上下兩邊的折頁蓋上去。

 Step10-cStep10-d

Step10-b. 一樣用電池密實。封面、封點都要做過一遍

Step11-a  Step11-bStep11-c

Step11. 把書本拿回來,部份放入書套,套過書本的幫忙,可以方便地用紙膠帶將折頁固定,之後再把封底完整放入書套。已經完成一邊了!

Step12-aStep12-bStep12-c   

Step12. 把書本翻到另一面,同時把書套折頁放入,利用書的重量一次整理一邊的上下折頁。過程中,手指記得把上下折頁的外緣用力拉往中間。

Step13

成果!不放心可以拿東西壓一壓,讓書套定型。

DSC05760

我都這樣用 Windows Internals 的~

Interrupt Handling in Windows —— part 1: Using WinDbg

透過 WinDbg 的 !idt 指令,我們可以看到 IDT 的內容:

0: kd> !idt

Dumping IDT:

37:    806e6864 hal!PicSpuriousService37
3d:    806e7e2c hal!HalpApcInterrupt
41:    806e7c88 hal!HalpDispatchInterrupt
50:    806e693c hal!HalpApicRebootService
62:    81bbe6f4 atapi!IdePortInterrupt (KINTERRUPT 81bbe6b8)
63:    8191a974 USBPORT!USBPORT_InterruptService (KINTERRUPT 8191a938)
             portcls!CKsShellRequestor::`scalar deleting destructor'+0x26 (KINTERRUPT 8197cbb0)
73:    81962bec USBPORT!USBPORT_InterruptService (KINTERRUPT 81962bb0)
82:    81bd4bec atapi!IdePortInterrupt (KINTERRUPT 81bd4bb0)
83:    81be1ae4 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 81be1aa8)
93:    81965204 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 819651c8)
a3:    81964044 i8042prt!I8042MouseInterruptService (KINTERRUPT 81964008)
b1:    81be423c ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 81be4200)
b2:    81795bec serial!SerialCIsrSw (KINTERRUPT 81795bb0)
b4:    818765d4 NDIS!ndisMIsr (KINTERRUPT 81876598)
c1:    806e6ac0 hal!HalpBroadcastCallService
d1:    806e5e54 hal!HalpClockInterrupt
e1:    806e7048 hal!HalpIpiHandler
e3:    806e6dac hal!HalpLocalApicErrorService
fd:    806e75a8 hal!HalpProfileInterrupt
fe:    806e7748 hal!HalpPerfInterrupt

單純的 !idt 只會輸出 ntoskrnl.exe 之外的 interrupt handler ,若想看到全部 IDT 項目,則須加上 -a:

!idt -a

More

0: kd> !idt

Dumping IDT:

37:    806e6864 hal!PicSpuriousService37
3d:    806e7e2c hal!HalpApcInterrupt
41:    806e7c88 hal!HalpDispatchInterrupt
50:    806e693c hal!HalpApicRebootService
62:    81bbe6f4 atapi!IdePortInterrupt (KINTERRUPT 81bbe6b8)
63:    8191a974 USBPORT!USBPORT_InterruptService (KINTERRUPT 8191a938)
             portcls!CKsShellRequestor::`scalar deleting destructor'+0x26 (KINTERRUPT 8197cbb0)
73:    81962bec USBPORT!USBPORT_InterruptService (KINTERRUPT 81962bb0)

一般的情況下,!idt 已經給予足夠的資訊了,以上面的 IDE 介面來說:

  1. 它的 interrupt number 是 62
  2. ISR 是位於 atapi module 的 IdePortInterrupt

不過好奇心驅使,可以發現:當去查詢 atapi!IdePortInterrupt 的位置時,卻不是 0x81bbe6f4 、也不是 0x81bbe6b8

0: kd> x atapi!IdePortInterrupt
f980467e atapi!IdePortInterrupt = 

而是 0xf980467e ,加上一個 IDT entry 竟然有這麼多 addresses ,真是詭異。所以我們可以懷疑一下,!idt 這個指令的輸出是經過加工的。不過必須找到一些更基礎的資訊來支持這個懷疑。在 x86 處理器的 protected mode 中, IDT 是由 IDTR (Interrupt Descriptor Table Register)所紀錄,所以來看看 registers 的狀況如何:

0: kd> r
eax=00000001 ebx=ffdff980 ecx=80554780 edx=000003f8 esi=00000003 edi=19d73a7c
eip=8052c5ec esp=805523b0 ebp=805523c0 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202
nt!RtlpBreakWithStatusInstruction:
8052c5ec cc              int     3

什麼?沒有 IDTR ,原來要打 r idtr

0: kd> r idtr
idtr=8003f400

知道 IDT 的位置後,必須算出適當的位移量來找到 atapi!IdePortInterrupt ,參考一下 Intel 的文件:Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programming Guide 5.10 可以知道 IDT entry —— interrupt gate 的格式:

InterruptObject06

所以,atapi!IdePortInterrupt 等於:

image

0: kd> dd 8003f400+0x8*0x62
8003f710  0008e6f4 81bb8e00 0008a974 81918e00
8003f720  00081d28 80548e00 00081d32 80548e00
8003f730  00081d3c 80548e00 00081d46 80548e00

跟 IDT Entry 第一個 address 是符合的,所以可以確定它是 interrupt handler 的 entry point:

62:    81bbe6f4 atapi!IdePortInterrupt (KINTERRUPT 81bbe6b8)

那麼該怎麼從這 0x81bbe6f4 entry point 到 atapi!IdePortInterrupt  去執行呢?線索就在 KINTERRUPT 81bbe6b8 上, Windows 透過 interrupt object 來管理系統上的 interrupts ,driver programmers 必須向 interrrupt object 註冊自己的 ISR ,那麼 interrupt handling flow 才會有機會執行到這個 ISR 。Windows Internals chapter 3 有針對 interrupt 作詳細的說明。透過 dt 指令可以看到存放在 0x81bbe6b8 的 kinterrupt 變數。

0: kd> dt nt!_kinterrupt 81bbe6b8
   +0x000 Type             : 22
   +0x002 Size             : 484
   +0x004 InterruptListEntry : _LIST_ENTRY [ 0x81bbe6bc - 0x81bbe6bc ]
   +0x00c ServiceRoutine   : 0xf980467e     unsigned char  atapi!IdePortInterrupt+0
   +0x010 ServiceContext   : 0x81bbd030 
   +0x014 SpinLock         : 0
   +0x018 TickCount        : 0xffffffff
   +0x01c ActualLock       : 0x81bbe91c  -> 0
   +0x020 DispatchAddress  : 0x80546660     void  nt!KiInterruptDispatch+0
   +0x024 Vector           : 0x162
   +0x028 Irql             : 0x5 ''
   +0x029 SynchronizeIrql  : 0x5 ''
   +0x02a FloatingSave     : 0 ''
   +0x02b Connected        : 0x1 ''
   +0x02c Number           : 0 ''
   +0x02d ShareVector      : 0 ''
   +0x030 Mode             : 1 ( Latched )
   +0x034 ServiceCount     : 0
   +0x038 DispatchCount    : 0xffffffff
   +0x03c DispatchCode     : [106] 0x56535554

注意到沒有?放在 nt!_kinterrupt+0x03c 位置的 DispatchCode 其實就是 IDT Eetry。

0: kd> .formats 0x81bbe6b8+0x03c
Evaluate expression:
  Hex:     81bbe6f4
  Decimal: -2118392076
  Octal:   20156763364
  Binary:  10000001 10111011 11100110 11110100
  Chars:   ....
  Time:    ***** Invalid
  Float:   low -6.90244e-038 high -1.#QNAN
  Double:  -1.#QNAN

既然確定了 0x56535554 就是 entry point ,那麼可以反組譯一下 DispatchCode 來確定是否會執行到 atapi!IdePortInterrupt

0: kd> u 81bbe6f4 81bbe6f4+0x106*0x4
; Setup Trap Frame
81bbe6f4 54              push    esp
81bbe6f5 55              push    ebp
81bbe6f6 53              push    ebx
81bbe6f7 56              push    esi
81bbe6f8 57              push    edi
81bbe6f9 83ec54          sub     esp,54h
81bbe6fc 8bec            mov     ebp,esp
81bbe6fe 89442444        mov     dword ptr [esp+44h],eax
81bbe702 894c2440        mov     dword ptr [esp+40h],ecx
81bbe706 8954243c        mov     dword ptr [esp+3Ch],edx
81bbe70a f744247000000200 test    dword ptr [esp+70h],20000h
81bbe712 0f852b010000    jne     81bbe843
81bbe718 66837c246c08    cmp     word ptr [esp+6Ch],8
81bbe71e 7423            je      81bbe743
81bbe720 8c642450        mov     word ptr [esp+50h],fs
81bbe724 8c5c2438        mov     word ptr [esp+38h],ds
81bbe728 8c442434        mov     word ptr [esp+34h],es
81bbe72c 8c6c2430        mov     word ptr [esp+30h],gs
81bbe730 bb30000000      mov     ebx,30h
81bbe735 b823000000      mov     eax,23h
81bbe73a 668ee3          mov     fs,bx
81bbe73d 668ed8          mov     ds,ax
81bbe740 668ec0          mov     es,ax
81bbe743 648b1d00000000  mov     ebx,dword ptr fs:[0]
81bbe74a 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh
81bbe755 895c244c        mov     dword ptr [esp+4Ch],ebx
81bbe759 81fc00000100    cmp     esp,10000h
81bbe75f 0f82b6000000    jb      81bbe81b
81bbe765 c744246400000000 mov     dword ptr [esp+64h],0
81bbe76d fc              cld
81bbe76e 8b5d60          mov     ebx,dword ptr [ebp+60h]
81bbe771 8b7d68          mov     edi,dword ptr [ebp+68h]
81bbe774 89550c          mov     dword ptr [ebp+0Ch],edx
81bbe777 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
81bbe77e 895d00          mov     dword ptr [ebp],ebx
81bbe781 897d04          mov     dword ptr [ebp+4],edi
81bbe784 64f60550000000ff test    byte ptr fs:[50h],0FFh
81bbe78c 750d            jne     81bbe79b
; Pass KInterrupt object to KiInterruptDispatch
81bbe78e bfb8e6bb81      mov     edi,81BBE6B8h ; 0x81bbe6b8 是我們的 interrupt object
81bbe793 e9c87e98fe      jmp     nt!KiInterruptDispatch (80546660); 0x80546660 interrupt object 中的 DispatchAddress 一欄

如果我們再追進 nt!KiInterruptDispatch :

0: kd> u nt!KiInterruptDispatch L50
nt!KiInterruptDispatch:
80546660 64ff05c4050000  inc     dword ptr fs:[5C4h]
80546667 8bec            mov     ebp,esp
80546669 8b4724          mov     eax,dword ptr [edi+24h]
8054666c 8b4f29          mov     ecx,dword ptr [edi+29h]
8054666f 50              push    eax
80546670 83ec04          sub     esp,4
80546673 54              push    esp
80546674 50              push    eax
80546675 51              push    ecx
80546676 ff1590904d80    call    dword ptr [nt!_imp__HalBeginSystemInterrupt (804d9090)]
8054667c 0bc0            or      eax,eax
8054667e 7440            je      nt!KiInterruptDispatch+0x60 (805466c0)
80546680 83ec0c          sub     esp,0Ch
80546683 833d6c56568000  cmp     dword ptr [nt!PPerfGlobalGroupMask (8056566c)],0
8054668a c745f400000000  mov     dword ptr [ebp-0Ch],0
80546691 7541            jne     nt!KiInterruptDispatch+0x74 (805466d4)
80546693 8b771c          mov     esi,dword ptr [edi+1Ch]
80546696 f00fba2e00      lock bts dword ptr [esi],0
8054669b 722b            jb      nt!KiInterruptDispatch+0x68 (805466c8)
8054669d 8b4710          mov     eax,dword ptr [edi+10h]
805466a0 50              push    eax
805466a1 57              push    edi
; 注意到了嗎?di 是 interrupt ojbect; edi+0Ch 是欄位 ServiceRoutine 也就是 0xf980467e atapi!IdePortInterrupt+0
805466a2 ff570c          call    dword ptr [edi+0Ch] 

終於,水落石出了!最後用 interrupt object 做一個總結:

image

  1. 當 interrupt 發生時,CPU 根據 interrupt number 到 IDT 描述找到對應的 DispatchCode
  2. DispatchCode 產生 trap frame ,然後呼叫對應的 kernel interrupt handler (nt!KiInterruptDispatch),並將 interrupt object 當作參數傳入
  3. Kernel interrupt handler 轉而呼叫對應的 ISR

那 !idt 輸出的格式就是:

Interrupt Number: IDT Entry Code Kernel/User Defined ISR (Corresponding interrupt object address)

是吧!再看一次:

0: kd> !idt

Dumping IDT:

37:    806e6864 hal!PicSpuriousService37
3d:    806e7e2c hal!HalpApcInterrupt
41:    806e7c88 hal!HalpDispatchInterrupt
50:    806e693c hal!HalpApicRebootService
62:    81bbe6f4 atapi!IdePortInterrupt (KINTERRUPT 81bbe6b8)
63:    8191a974 USBPORT!USBPORT_InterruptService (KINTERRUPT 8191a938)
             portcls!CKsShellRequestor::`scalar deleting destructor'+0x26 (KINTERRUPT 8197cbb0)
73:    81962bec USBPORT!USBPORT_InterruptService (KINTERRUPT 81962bb0)
82:    81bd4bec atapi!IdePortInterrupt (KINTERRUPT 81bd4bb0)
83:    81be1ae4 SCSIPORT!ScsiPortInterrupt (KINTERRUPT 81be1aa8)
93:    81965204 i8042prt!I8042KeyboardInterruptService (KINTERRUPT 819651c8)
a3:    81964044 i8042prt!I8042MouseInterruptService (KINTERRUPT 81964008)
b1:    81be423c ACPI!ACPIInterruptServiceRoutine (KINTERRUPT 81be4200)
b2:    81795bec serial!SerialCIsrSw (KINTERRUPT 81795bb0)
b4:    818765d4 NDIS!ndisMIsr (KINTERRUPT 81876598)
c1:    806e6ac0 hal!HalpBroadcastCallService
d1:    806e5e54 hal!HalpClockInterrupt
e1:    806e7048 hal!HalpIpiHandler
e3:    806e6dac hal!HalpLocalApicErrorService
fd:    806e75a8 hal!HalpProfileInterrupt
fe:    806e7748 hal!HalpPerfInterrupt

: )

相關閱讀

  1. Windows Debugging – Kernel Debugging with WinDbg and VMWare

Reference

  1. Windows Internals chapter 3
  2. Intel® 64 and IA-32 Architectures Software Developer's Manuals: http://www.intel.com/products/processor/manuals/

Windows + Visual Studio + VSCode + CMake 的疑難雜症

Environment Windows 10 Visual Studio 2019 CMake 3.27.7 VSCode VSCode CMake Tools 1. CMAKE_BUILD_TYPE 是空的 參考一下 這篇 的處理。 大致上因為 Visual...