2013年11月27日 星期三

[原創] LabVIEW code security: Password Protect VI v.s. Removing Block Diagram VI

LabVIEW 開發環境提供了兩種方式保護程式原始碼的安全性 , 一個是設定VI的密碼.另一個是移除VI的Block Diagram ,也就是VI的程式邏輯部分.
NI官方網站的說明也非常詳細  可以參考"Security of LabVIEW VI Password Protection vs. Removing VI Block Diagrams" , 雖然此篇文章並沒有明確指出Password Protect VI加密的演算法類型 , 但經實驗已確定是使用了MD5 (Wiki MD5)的加密 , MD5目前已被證實可經由碰撞而產生另一組解 , 因此並非完美的加密方式 , 官方文章也提到 Password Protect VI 的加密流程也可能因LabVIEW本身程式碼被逆向工程,跳過了判斷驗證密碼的程式導致即使加密,卻被輕鬆破解的結果.

因此NI提供了 Removing Block Diagram VI的方式 ,讓該VI 只保存人機介面與編譯過的Binary Code.此方式仍被破解的可能性只有對方能解讀該Binary Code在該平台(x86 ...etc) 對應的邏輯,因效益太低可能性微乎其微,因該不會有人會想做這種事情.此方式的缺點是該VI僅能提供同樣的LabVIEW版本與使用的平台環境,例如 abc.vi是在LabVIEW 2012,x86 Win7底下進行Removing Block Diagram後, abc.vi未來只能在LabVIEW 2012,x86 Win7的環境下被呼叫使用 , 因此若要支援多個版本,則須提供相對應的abc.vi.

Removing Block Diagram VI的流程如下:

1.建立一個vi,範例用一個簡單的加法運算,並加入專案.

2.在專案的Builed Specifications項目右鍵選擇New,然後再選擇Source Distribution.

3.在Source File選項中將目標abc.vi設為Always Included.

4.在source File Settings選項中點到abc.vi , 然後在右邊選單會有個Remove block diagram , 把這個選項打勾.
(後面會再多做一個連Remove front panel打勾的比較)

5. 之後點 Build 按鈕後就會生成不含Block Diagram的vi了. 由上圖可以比較一下
abc.vi為原始vi...... 13KB
abc_RemovingBlackDiagram.vi 為移除Block Diagram.......6KB
abc_RemovAnything.vi 為移除Front Panel與Block Diagram...... 3KB

6.將移除Block Diagram的vi拿到另外一台電腦上用LabVIEW 2013的環境打開就會跳出這個畫面 , 底下紅色框框裡顯說Block Diagram已被移除,因此LabVIEW 2013無法進行轉換.

接著用之前文章 提到的程式稍微修改一下,可以讀取儲存vi的每個部分,簡單說明幾個部分的名字代表的意義(猜的), 
VICD = VI compiled Data , 這是該vi程式碼compile 成machine code的資料.
BDPW = Block Diagram PassWord , 這是該vi被設 Password Protect VI後的密碼生成MD5存放的位置 , 前32 byte 就是該vi 被設密碼後的MD5值 , 若該值為"D41D8CD98F00B204E9800998ECF8427E"代表該vi並未被加密. 後面64 byte可能用來計算vi資料是否遭竄改或毀損(猜的)
FPHb , FPSE 為Front Panel 資料 , 詳細意義不明
BDHb , BDSE 為Block Diagram資料 , 詳細意義不明


上面兩張圖是 abc.vi與 abc_RemovingBlackDiagram.vi 的比較
可以看到VICD並未改變, BDHb , BDSE都被移除.

上面這張圖是 abc_RemovAnything.vi 與 abc_RemovingBlackDiagram.vi 的比較
可以看到VICD並未改變, FPHb , FPSE和其他一些東西都被移除.
結論:
1.Password Protect VI 用MD5加密程式碼.
2.Removing Block Diagram VI無法還原成可編輯的vi
3.就保護程式碼而言Removing Block Diagram VI比Password Protect VI來的安全 , 但使用此vi進行開發非常不方便.

2013年11月13日 星期三

[原創] Constant inside/outside loops

測試For Loop內若使用到常數時,將常數移出迴圈外,程式執行效能是否有所改變.

編譯環境為:
LabVIEW 2013 (32 - bit)
Windows 8 64-bit


第一個程式的比較非常單純 , 迴圈轉1000次,每次累加20,差別只有20放回圈外面或裡面


直接比較binary,可以看出LabVIEW compiler後的machine code是有差別的

看起來差在記憶體位置的位移,使用不同的register做某種資料的運算 , 至於是甚麼運算就去沒有深入了解.到此為止的運算上所需時間是一樣的.



真正的主角是在後面這段 , 迴圈轉1000次 , 每次累加20 (14H) , 比較是否少於1000次,若成立就繼續做
PS. jl (jump on less than, signed)


第二個程式的比較稍微 , 迴圈轉1000次,每次累加20,多做其他運算

Binary的比較,可以看出跑的code size沒變 , 執行運算時間看來是一樣的.


列出所有差異的部分


結論:為了程式碼美觀,常數就放在迴圈裡面吧


2013年11月11日 星期一

[原創] Consider constants for index array...

撰寫 LabVIEW 程式時,若用Index array時常常會拉一堆Index的常數, 這是否會對LabVIEW效能造成影響?

在編譯環境為:
LabVIEW 2013 (32 - bit)
Windows 8 64-bit

比較LabVIEW compiler以下6個程式碼編譯結果的差異


1,2的差別在連接線的常數

3,4的差別是計算和的順序

5,6的差別是,5含有沒用到的常數

比較compile完後各組的machine code差異,差異如下
1與2完全一樣 ,以後拉一堆常數也不用在意了
另外比較一下1和3,看看Index值有改變會怎麼樣,code size長度一樣所以計算時間應該是一樣的,下面有些零散的紅點只是address的變化
3與4改變計算和的順序,大概也是5~7行assembly指令的差別
5與6只差在5的Block Diagram多了不必要的常數加法乘法, LabVIEW compiler果然會無視掉,以後修改程式時可以把舊的算是留著不接,反正沒影響 ...
結論:
Index Array 的常數看來拉多拉少是沒有影響的,程式碼中沒有用到的常數運算看起來也會被編譯器無視掉.

PS.事實上也有另外做過常數擺在迴圈內外的比較,結果是有差異但是編譯後的機械碼長度記得也是一樣的.

2013年11月5日 星期二

[原創] Modified machine code inside .vi file , a proof of how LabVIEW runs in machine code

在了解.vi 檔案內帶有編譯完成machine code之後,可以做一個小小實驗來佐證LabVIEW執行時是直接跑machine code的.考慮簡單的加法運算 255 + 255 =510,透過Code Extractor.vi擷取machine code的部分存檔為"SimpleAdd.vi_255+255.bin";
接著修改為0+255=255,一樣透過Code Extractor.vi擷取machine code的部分存檔為"SimpleAdd.vi_0+255.bin"; 

比對一下"SimpleAdd.vi_255+255.bin"與"SimpleAdd.vi_0+255.bin", 可以發現只差在下圖紅字的地方,其實也可以發現LabVIEW編譯器會直接將兩個常數255+255視為一個常數510(0x01FE)
未來寫code如果遇到有代表意義的常數相加時可以不用客氣直接大辣辣地貼出來,LabVIEW編譯器會幫你自動最佳化成最簡的常數

修改Code Extractor.vi ,讓他可以將修改後的machine code的binary file經zlib加密後取代原始vi檔的machine code部分,並另存檔案修改黨名為Modified_XXX.

開啟修改後的"Modified_ SimpleAdd.vi", Block Diagram雖然顯示 0+255 但執行卻是510
證明了LabVIEW run-time 執行時確實是跑machine code的!
 執行時選用Highlight execution可以看到非常有趣的畫面...

點Ctrl+Shift後再點run button, LabVIEW就會重新編譯回0+255=255了

2013年11月4日 星期一

[原創] Convert LabVIEW code to x86 assembly code Introduction

從NI Community看了一篇有趣的文章
 LabVIEW code generation - behind the scenes...

這篇文章解釋了LabVIEW運行時會先把圖形化程式轉成x86 asm , 中間不會生成c code,不管是debug/release run都是直接由compiler編譯labview code轉成 x86 machine code , 因此不會受到c code compiler影響, NI只要專注在 Labview compiler的優化即可.

參考該文章範例進行實作,實作環境為 LabVIEW 2013 (32-bit) , Windows 8 (x 64) , i3-2100CPU@3.10GHz

考慮以下兩個程式碼,簡單的for loop 迴圈,shift register,加法運算, 僅改變部分初始值然後看看labview compiler 轉 machine code程式碼的差異




PS.為了簡化asm code長度 , vi的設定為取消debugging (如下圖所示)

轉為binary後 ,由hex檔直接比對,很容易就可以看出初始值差異的位置,
如左邊的F4 01即是0x1F4 , 迴圈跑500次的常數,shift register的初始值255,回圈內累加常數0x5F等





人眼看machine code絕對痛苦萬分,透過免費版IDA pro , deassembly machine code成asm code,可以幫助了解 labview compiler 執行效能 ,


 因為我也不熟 x86 asm , 上圖是我猜測的迴圈執行流程 , 中間穿插其他程式我猜可能數值型態的檢查

跑過以上流程後隱約可以感覺labview程式實際執行的流程,大概就可以解釋開發labview時遇到的以下幾個現象(不保證正確)

Q1. 不同版本 vi或相同版本vi轉到其他電腦上時,vi畫面右上角會出現 * 號要求存檔
A1. LabVIEW compiler 會檢查編譯環境,若發現需要重新編譯則會建議使用者執行前先存檔進行編譯.

Q2.新版LabVIEW跑舊版程式時,理論上新版經過優化執行速度會增加,執行時間會減少,但若沒有重新存檔則執行時間差異不大,存檔完才會有較大差異.
A2. 新版開舊版檔案時, .vi內的machine code若沒經過重新編譯,在machine code不變的情況下執行效能當然一樣.

未來幾篇文章會用這個方式將LabVIEW code轉 asm , 看看那些coding style可以獲得較佳的執行效率.