- A+
一:背景
1.讲故事
前几天有位朋友在微信上找到我,说他的一个程序上了生产之后,被运维监控定位到这个程序会向一个网址为: http://m.365ey.net
上不定期打数据,而且还是加密的格式,要他解释到底是怎么回事?朋友说根本不认识这个网址,最后在恐慌中排查到是项目中引用了 DeveloperSharp.RabbitMQ
组件所致,截图如下:
朋友反编译了下sdk源码,发现都是混淆过的没法看,聊的过程中朋友很恐慌,很焦虑,担心生产的数据被泄漏,让我帮忙看下是否有手段定位下到底都采集了什么数据?
说实话,这种事情听起来就很惊魂,情从肺腑出,方能入肺腑。。。只要是一个正能量的人,这个忙肯定是尽最大可能的帮一下。
二:WinDbg 分析
1. 前置工作准备
从 nuget 中把 DeveloperSharp.RabbitMQ
下载下来,写好一个测试案例。
internal class Program { static void Main(string[] args) { for (int i = 0; i < int.MaxValue; i++) { try { var queueName = "jk"; var content = $" hello world! {i}"; RabbitMQHelper.SendMessage(queueName, content); Console.WriteLine($"时间:{DateTime.Now}, i={i} 次处理!"); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { Thread.Sleep(1000); } } } }
为了安全,我把程序放到虚拟机中,同时在 hosts 下给它设置个错误的 ip 地址。
192.168.1.30 m.365ey.net
接下来打开 Fiddler,再用 WinDbg TTD
的方式把程序启动起来来记录程序的全部行为,方便我后续分析,
正如朋友所说,真的有采集行为:
http://m.365ey.net:13064/AssistLog.svc
http://m.365ey.net:13063/QueryLog.svc
2. 如何寻找请求代码块
截获请求的代码很简单,因为屏蔽了 IP,所以请求肯定会抛异常,我只需要用 sxe clr
拦截下 First Chance Exception
就能捕获到调用代码。
0:013> sxe clr 0:013> g (3398.3f7c): CLR exception - code e0434352 (first/second chance not available) First chance exceptions are reported before any exception handling. This exception may be expected and handled. Time Travel Position: 16D93B:0 eax=079befe8 ebx=00000005 ecx=00000005 edx=00000000 esi=079bf0a8 edi=00000001 eip=77207380 esp=079befe0 ebp=079bf040 iopl=0 nv up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 ntdll!RtlRaiseException: 77207380 55 push ebp 0:013> !clrstack OS Thread Id: 0x3f7c (13) Child SP IP Call Site 079bf0fc 77207380 [HelperMethodFrame: 079bf0fc] 079bf1ac 78861902 System.Net.HttpWebRequest.GetResponse() [f:ddNDPfxsrcnetSystemNetHttpWebRequest.cs @ 2254] 079bf1fc 0a5a2e89 System.ServiceModel.Channels.HttpChannelFactory`1+HttpRequestChannel+HttpChannelRequest[[System.__Canon, mscorlib]].WaitForReply(System.TimeSpan) 079bf24c 0a5a1199 System.ServiceModel.Channels.RequestChannel.Request(System.ServiceModel.Channels.Message, System.TimeSpan) 079bf2c0 0a5a1026 System.ServiceModel.Dispatcher.RequestChannelBinder.Request(System.ServiceModel.Channels.Message, System.TimeSpan) 079bf2d0 0a5703f3 System.ServiceModel.Channels.ServiceChannel.Call(System.String, Boolean, System.ServiceModel.Dispatcher.ProxyOperationRuntime, System.Object[], System.Object[], System.TimeSpan) 079bf3cc 0a57010d System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage, System.ServiceModel.Dispatcher.ProxyOperationRuntime) 079bf3f8 0a56f79e System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage) 079bf438 7b224e24 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData ByRef, Int32) [f:ddndpclrsrcBCLsystemruntimeremotingrealproxy.cs @ 823] 079bf5d0 7a5df148 [TPMethodFrame: 079bf5d0] wfP8f24Vyfpc8suOyj.bBZbSsm9FOwaYWDWVc.Record1(System.String, System.String, System.String, System.String, System.String, System.String, System.String) 079bf644 067c835f System.Base.ApplicationContext+c.b__19_0() 079bf6b0 7b1dd4bb System.Threading.Tasks.Task.InnerInvoke() [f:ddndpclrsrcBCLsystemthreadingTasksTask.cs @ 2884] ...
从线程栈看,请求 http://m.365ey.net:13064/AssistLog.svc
是由 Record1
方法发起的,一看就是个 WCF 方法,参数名称和个数都和 Fiddler 中保持一致, 截图如下:
3. 这些参数都是什么
要找到原参数信息,需要找到是谁调用了 Record1
方法,可以用 !U 067c835f
查看函数汇编代码,简化后如下:
0:013> !U 067c835f Normal JIT generated code System.Base.ApplicationContext+<>c.<HashObjectMap>b__19_0() Begin 067c7ed8, size 584 ... 067c8333 8b3d74361904 mov edi,dword ptr ds:[4193674h] (Object: System.Runtime.Remoting.Proxies.__TransparentProxy) 067c8339 ff75b0 push dword ptr [ebp-50h] 067c833c ff75ac push dword ptr [ebp-54h] 067c833f ff75a8 push dword ptr [ebp-58h] 067c8342 ff75a4 push dword ptr [ebp-5Ch] 067c8345 ff75a0 push dword ptr [ebp-60h] 067c8348 b94e080000 mov ecx,84Eh 067c834d ff15b05d7a06 call dword ptr ds:[67A5DB0h] (System.Base.ApplicationContext+<>c.zmMLEYhjSCTVEl2CxBD(Int32), mdToken: 0600009e) 067c8353 50 push eax 067c8354 8b55b4 mov edx,dword ptr [ebp-4Ch] 067c8357 8bcf mov ecx,edi 067c8359 ff15d8016d00 call dword ptr ds:[6D01D8h] ...
原来是 <HashObjectMap>b__19_0
方法做的调用,也就是 call dword ptr ds:[6D01D8h]
,不信的话可以截图看源码:
从混淆的代码看,有几个特征:
- aa 依赖于 n9UuXCvGC
- bb 依赖于 gY03KpyvZ
- cc 依赖于 GsvWjQg1p
- hh 依赖于 text
等等,那怎么提取呢? 这里只演示一个 aa 参数吧,可以在汇编代码的第一个 x4phG7d0qxdP1ZxlQa.pliOsRbOU
方法上下一个断点,即 067c820b
处观察方法参数,下断点后,让程序回流。
0:013> !U 067c8359 Normal JIT generated code System.Base.ApplicationContext+<>c.<HashObjectMap>b__19_0() ... 067c8206 50 push eax 067c8207 8bd3 mov edx,ebx 067c8209 8bcf mov ecx,edi 067c820b ff15e8667a06 call dword ptr ds:[67A66E8h] (System.Base.ApplicationContext+x4phG7d0qxdP1ZxlQa.pliOsRbOU(System.String, System.String, System.String), mdToken: 0600003e) 067c8211 8945b4 mov dword ptr [ebp-4Ch],eax ... 0:013> bp 067c820b 0:013> g- Breakpoint 1 hit Time Travel Position: 117A27:A80 eax=032c0ca4 ebx=032bf94c ecx=0329e558 edx=032bf94c esi=032bea78 edi=0329e558 eip=067c820b esp=079bf640 ebp=079bf6a8 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 067c820b ff15e8667a06 call dword ptr ds:[67A66E8h] ds:002b:067a66e8=067cdf00 0:013> ub 067c820b 067c81ef 8945b8 mov dword ptr [ebp-48h],eax 067c81f2 8b3d68361904 mov edi,dword ptr ds:[4193668h] 067c81f8 8b5e08 mov ebx,dword ptr [esi+8] 067c81fb b914040000 mov ecx,414h 067c8200 ff15b0647a06 call dword ptr ds:[67A64B0h] 067c8206 50 push eax 067c8207 8bd3 mov edx,ebx 067c8209 8bcf mov ecx,edi
上面输出的 ecx, edx, eax
分别就是 pliOsRbOU()
方法的三个参数。
0:013> !do ecx Name: System.String MethodTable: 7ad924e4 EEClass: 7ae97690 Size: 40(0x28) bytes File: C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll String: 192.168.0.106 Fields: MT Field Offset Type VT Attr Value Name 7ad942a8 4000283 4 System.Int32 1 instance 13 m_stringLength 7ad92c9c 4000284 8 System.Char 1 instance 31 m_firstChar 7ad924e4 4000288 70 System.String 0 shared static Empty >> Domain:Value 00b0bce8:NotInit << 0:013> !do edx Name: System.String MethodTable: 7ad924e4 EEClass: 7ae97690 Size: 46(0x2e) bytes File: C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll String: N8CDEFGH+JKLM..P Fields: MT Field Offset Type VT Attr Value Name 7ad942a8 4000283 4 System.Int32 1 instance 16 m_stringLength 7ad92c9c 4000284 8 System.Char 1 instance 4e m_firstChar 7ad924e4 4000288 70 System.String 0 shared static Empty >> Domain:Value 00b0bce8:NotInit << 0:013> !do eax Name: System.String MethodTable: 7ad924e4 EEClass: 7ae97690 Size: 32(0x20) bytes File: C:WindowsMicrosoft.NetassemblyGAC_32mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll String: TripleDES Fields: MT Field Offset Type VT Attr Value Name 7ad942a8 4000283 4 System.Int32 1 instance 9 m_stringLength 7ad92c9c 4000284 8 System.Char 1 instance 54 m_firstChar 7ad924e4 4000288 70 System.String 0 shared static Empty >> Domain:Value 00b0bce8:NotInit <<
从输出中可以看到,aa 参数原来提取了我的 ip 地址,用同样的方式可以提取出所有的方法参数,这里就不详述了,我做了一个整理,截图如下:
从图中可以看到: AssistLog.svc
请求大概会提取:
- IP地址
- 机器码
- 机器名
- 时间戳
- IP地址 + 机器码 + 时间戳
用同样的方式调查请求 http://m.365ey.net:13063/QueryLog.svc
,整理如下:
这个请求中主要统计了一些接口版本等信息,并没有发现有消息的外泄,分析到这里还是比较欣慰的,将信息反馈给朋友,让朋友不要担心,应该不会有问题。
三: 总结
总的来说我还是非常相信作者,写一个 SDK 已经够辛苦了,做一些违法犯罪的事情道理上说不通,作者也就是纯技术心,想收集下用户端的使用情况,不过在未经许可的情况下确实有所不妥,但初心不坏。
如果 SDK 的作者能够帮他的使用者解答疑惑,让他的粉丝群体可以安心使用,那就更好了,在此感谢作者的辛苦付出,祝组件越来越强大。