使用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了异常,退出也是相对优雅的退出,不能经过在程序无处理异常时打开调试器的方法。

相关文章
相关标签/搜索