使用DNSpy 調試.net 服務

背景:web

某個.net 應用以windows服務的形式對外提供服務,而後經過IIs的web應用和該服務進行交互,經過web的方式向用戶提供業務。該項目有點年頭,目前至關於本身維護,沒廠家支持,並且自定義了不少開發在上面,可是有一個棘手的問題是該項目沒有提供全部源碼的,相似服務程序,使用的相關DLL還進行了混淆加密,因此其內部邏輯經過反編譯看源碼會很是繞,並且就算反混淆後,代碼的組織和命名也會差不少,看起來會很是的累。windows


症狀:app

1. 某次爲了和生產保持一致,就地升級了測試環境的操做系統(2012 r2 到2016),這個服務死活其不來,還好作了VM的快照。這個程序錯誤的時候就在日誌裏記了一條「輸入字符串格式不對」,沒頭沒腦,也不包含stack trace ,不知道哪裏報的錯。後來沒有辦法,拿快照回退,初步斷定爲操做系統兼容性問題(注意是.net 4.x 開發的,雖然有點懷疑這個結論)ide

2. 另外的同事爲了熟悉這個系統,在新的測試環境,從新部署了一套這個系統(操做系統使用的2019),系統能夠運行,沒有問題。(因此我很懷疑1 中的兼容性問題,但目前我找不到緣由)工具

3. 恢復快照的測試環境,後又經歷過兩次服務起不來的問題。有次打了補丁,有次是重啓就不行了。可是由於沒有太多思路去排錯(由於配置什麼的都沒有變過,關鍵是也沒有什麼有用的日誌協助排錯,並且沒技術支持,沒源碼),後面偷懶直接恢復了快照解決。測試

4.  測試環境由於須要更新補丁,重啓,結果服務又掛了。仍是同樣的報錯加密

嘗試解決:spa

實在忍不了,我必須得把這個緣由找出來,否則在生產上出現,真的無法處理,並且到時候確定手忙腳亂。初步的想法是經過調試器直接調試這個服務,而後找到報錯的位置,大體按邏輯找到可能出錯的位置。從而找出究竟是配置問題、仍是兼容性問題,仍是其餘的什麼。操作系統


因爲是個.net 應用,帶調試功能,且能反編譯的DNSpy 就屬於個人首選工具了。支持直接查看.net 語言編譯的exe、dll 文件,並且支持直接調試,並且能夠就地對dll、exe的代碼進行修改並保存。.net


image

結果調試一個windows 服務沒有你想的簡單。當使用DNSpy調試運行一個windows 服務時,因爲其不是經過net start 或者windows 服務管理器執行的,因此報這個錯誤。

C681F9BE

這可如何是好,這個服務起來就掛掉,我沒有經過attach 到進程的方式來調試啊。還好微軟的文章提供了一些思路。https://docs.microsoft.com/en-us/dotnet/framework/windows-services/how-to-debug-windows-service-applications#debugging-tips-for-windows-services

兩個選項:

1. 在服務的onstart或者main 裏增長sleep ,延長服務執行實際代碼的時間,在sleep 沒報錯的時候,使用調試器attch上去(加sleep 也就一兩行代碼的改動,應該能夠直接使用dnspy搞定,即便混淆過了,可是程序入口這些仍是很容易找到的,所以選擇了該方法)

2. 把服務的程序邏輯改寫,從新編譯成一個console 程序。(沒源碼,修改較多,看來不太適合個人狀況)


Dnspy 打開服務程序的exe文件,定位到onstart 方法,而後點擊右鍵,選擇」編輯方法(C#)「

image

增長一個sleep,我這裏大概5秒鐘,能夠手快的執行完attach,另外在onstart處下斷點。

image

保存修改。而後再DNspy的文件菜單中選擇保存模塊。這樣會把更改寫入到新的exe中(注意保留備份)


image

在服務管理器中啓動服務。


使用dnspy 調試--附加到進程

image

注意點: 這裏附加到進程能列出的是dnspy執行帳號下的進程,加入你的服務以其餘用戶身份執行,可能列舉不出來,因此可能要更換dnspy的運行帳號。

image


後續因爲涉及到應用內部的內容,就不寫出來了,大概定位到是程序加載某些自生成的配置文件(二進制),而後某些目錄的配置文件不知被誰拷貝了一份,名稱爲xx.yy-複製,致使程序加載報錯(多是文件名問題),可是報錯內容只有一條「輸入格式不正確」,由於程序自己對錯誤進行了catch ,而後沒有記錄下面stack信息,調試器裏其實能夠看到。雖然代碼進行了混淆,可是還能夠定位到大概的位置。

在 System.Version.VersionResult.SetFailure(ParseFailureKind failure, String argument)  
在 System.Version.TryParseComponent(String component, String componentName, VersionResult& result, Int32& parsedComponent)  
在 System.Version.TryParseVersion(String version, VersionResult& result)  
在 System.Version.Parse(String input)  
在 System.Version..ctor(String version)  
在 OO.Server.OOProcess.a(String A_0, Version& A_1)  
在 OO.Server.OOProcess.b(String A_0)  
在 l.c(String A_0)  
在 l.j()  
在 l.b(Boolean A_0)  
在 l.b(Boolean A_0)  
在 s4.a(Boolean A_0, Boolean A_1)  
在 aje.g()  
在 OO.Server.Server.Start()


總結分析:

綜合以上多個歷史症狀,能夠得出問題緣由是複製的自動生成的配置文件致使了應用crash,只是服務不重啓的時候,這個配置文件不會從新加載,因此不管是升級操做系統、打補丁、重啓都是因爲間接重啓了服務致使配置從新加載,而後觸發錯誤。

更多思考和問題:

這個程序是一個.net 反編譯和更改代碼相對簡單,可是若是服務是個C程序或者C++編譯的,如何進行調試呢?如何增長sleep 呢,或者有其餘更好的方法進行調試。並且程序自己catch了異常,退出也是相對優雅的退出,不能經過在程序無處理異常時打開調試器的方法。

相關文章
相關標籤/搜索