首页 > 其他分享 >逆向WeChat(六)

逆向WeChat(六)

时间:2024-09-09 23:02:56浏览次数:8  
标签:逆向 mojom sniff WeChat bool mojo Browser

上篇回顾,逆向分析mojo,mmmojo.dll, wmpf_host_export.dll,还有如何通过mojoCore获取c++binding的remote或receiver,并调用它们的功能接口。   

本篇介绍如何对小程序mojoIPC进行嗅探Sniff,如何通过mojoIPC.sniffer抓包小程序https,如何打开小程序devtool,谁掌管了vconsole配置。

本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/18370679

如何sniff mojoIPC

借用Chrome.IPC.Sniffer。改造成WeChat.IPC.Sniffer。虽然Chrome.IPC.Sniffer是C#项目,但主要也是在使用Win32API,主逻辑改造成c++也不难。程序主要使用tdevmonc.sys进行sniff。程序扫描chrome.dll,找出mojom相关服务名称,以及它们的method hash name。然后跟代码仓的mojom文件进行映射生成映射表。Chrome.IPC.Sniffer提供了wireshark的lua分析脚本。知道这些就可以按需求进行重构。扫描mmmojo.dll, wmpf_host_export.dll, WeChatAppEx.exe,释出它家自身的服务接口。找出WeChatAppEx.exe对应的chromium版本,同步更新lua脚本。在lua脚本添加自己的分析。

如何trace mojoIPC,拦截IOCP。

除了sniff mojoIPC外,还可以通过对每一个mojo进程进行io拦截,并跟踪。

现在先来讲通过拦截io跟踪mojoIPC,后面再继续讲sniff mojoIPC。

windows平台的mojo使用IOCP进行overlapped操作。所以理论上trace IOCP基本上可以trace到channel的所有数据。对于mojo设定的NamedPipe,WriteFile一定会block pending,在IOCP得到完成事件。但是ReadFile不然,却既可能立即成功,也可能产生pending。这是我在实际trace中发现到的。换言之,只trace IOCP的话,只能得到部分的PendingRead,以及所有的Write。所以必须trace Read跟IOCP。CompletionKey就是ChannelWin,保存着临时的Pending Buffer。Overlapped分别有ReadContext或WriteContext,这样就可以区分完成的操作是读还是写。ReadContext使用CircularBuffer,取出有效Buffer则可。WriteContext使用Buffer Pointer List,取队列首Buffer。保存起来就可以录像,数据库可以使用一些类mongodb的轻量数据库。或者通过一条NamedPipe,将Buffer写成WireShark Frame,结合Chrome.IPC.Sniffer,输出到WireShark并保存。WireShark方便又有可视化工具,缺点是携带额外信息有限,并且不能支持DataPipe,因为没有办法sniff共享内存。而trace IOCP的话,可以使用类mongodb的文档记录,则可灵活携带各种信息。同时还可以自行读取出DataPipe的SharedBuffer内容。需要注意的一点是,一次读写操作,可能打包多于一个Message。所以必须拆解。这种方法,没有现成的代码或工具,所有一切都要自己去完成,工作量不少。另外tdevmonc.sys有一个不足,捕捉的操作其实是不能准确分辨数据读还是写操作。自己用tdevmonc.sys实现过主逻辑就清楚。Chrome.IPC.Sniffer只是巧,只将tdevmonc.sys标记为Read的数据包记录。但实际上,同一个数据包必须应该被捕捉两次操作,一次写入管道,一次读出管道,然而有时是会漏掉其中一个的,所以只记录Read或Write时,必然会漏掉一些数据包。说它分辨不清Read跟Write,是因为Read跟Write两次操作的源进程id往往是一样的。如果pidA_write, pidB_Read。这样是明确的。但是,pidA_write,pidA_Read,你说这数据是从A到B,还是从B到A。怎么也不可能是从A到A,因为这个pipe由A跟B连接的。这是一个很至命的问题,并且不能解决掉。

另外,通过拦截Io来跟踪mojoIPC,能够获得实时的mojoIPC信息,主要是PlatformHandle,例如SharedBuffer,DataPipe所包括的共享内存。拦截跟踪串行在所有的mojoIPC操作中了。毕竟sniff只捕捉到瞬时残象。PlatformHandle在搬运过程不断地DuplicateHandle并紧接着关闭。当到你分析sniff数据时,所有PlatformHandle都可能已经变换重新它用了。如果需要准确地捕捉所有SharedBuffer或DataPipe通信的内容,似乎除了拦截Io,并无它选。后面在讲解sniff mojoIPC捕捉https时,我会用实例分析sniff可以做到哪种程度。

不得不吐一下,python因为GIL,多线程变得有够不(鸡)给(肋)力。原本为了方便,用ctypes代替JIT-C来使用。但是用在patch代码,尤其是多线程时,真是一个大坑。性能不说,死锁才气死人。cpython中GIL无处不在。python适合多进程大于多线程。如果只是执行python脚本,问题可能不大,因为仍然在系统闭环内。一但用ctypes进行跨越语言边界调用,并且多线程并发并行使用ctypes的时候,问题就会大条起身。就算只用两个线程并发用ctypes调用DebugLogStr打印固定内容,都会死锁。我用在patch密集的IO线程池代码时,即刻摔(仆)跤(街)。我又不得不将代码迁移回c/c++代码。毕竟python有太多轮子可以用,要不是问题大条到死锁不能用,我还是会忍受着运行效率。幸好我还开发了JIT-C相类似的功能。

下图演示,通过我的JustInTimeC生成执行代码,对Iocp进行patch,跟踪mojo数据。

下图演示,将跟踪到的数据进行分析。

下图演示,将跟踪到的数据,写到WireShark,让WireShark的lua脚本去分析。

 

如何通过sniff mojoIPC,抓包HTTPS。

截获https请求,网上有分享《基于微信PC端小程序抓包方法》通过搭建代理服务器,由代理服务器Burp做手脚,中间人攻击(MITM)或SSL拆分等的其它同类工具。

现在回到我的本篇,并不使用http(s)代理工具,chrominum存在另一溪径。在https通信之上,还有一层mojom服务。这些服务将用户的URL请求,转化成网络请求,并将网络响应数据,通过服务通知用户。所以我就可以绕开网络,直接sniff到明文请求,明文数据。对吧。

如果只是sniff请求的话,通过mojo也可以。只要理解了chrome的架构,就可以知道network也是一个子服务,要有一个宿主去运行。就像COM要有一个dllhost进程。只要sniff到network服务的mojo,不就可以截获https请求了吗,这是可以确定的。所有URL请求都通过URLLoaderFactory.CreateLoaderAndStart()方法调用network服务。URL响应由URLLoaderClient.OnReceiveResponse进行通知。响应一般都会采用DataPipe进行数据传递,所以sniff mojoIPC无能为力获得响应内容。这就跟CEF的处理流程是一致的。在CEF中,CefClient的CefRequestHandler,当Renderer处理脚本时需要加载href或url请求时,就会触发Browser的CefRequesthandler。同理地,chrominum中,Renderer向Browser请求处理URL请求,如果scheme需要网络请求,Browser就会转给network服务处理,否则自己处理。Browser再将响应结果传送给Renderer。

囗讲无凭,Now I show you how。

"network.mojom.URLRequest": {
    "definition": "struct URLRequest { string method; url.mojom.Url url; SiteForCookies site_for_cookies; bool update_first_party_url_on_redirect; url.mojom.Origin? request_initiator; array<url.mojom.Url> navigation_redirect_chain; url.mojom.Origin? isolated_world_origin; url.mojom.Url referrer; URLRequestReferrerPolicy referrer_policy; HttpRequestHeaders headers; HttpRequestHeaders cors_exempt_headers; int32 load_flags; int32 resource_type; RequestPriority priority; CorsPreflightPolicy cors_preflight_policy; bool originated_from_service_worker; bool skip_service_worker; bool corb_detachable = false; RequestMode mode; CredentialsMode credentials_mode; RedirectMode redirect_mode; string fetch_integrity; RequestDestination destination; URLRequestBody? request_body; bool keepalive; bool has_user_gesture; bool enable_load_timing; bool enable_upload_progress; bool do_not_prompt_for_login; bool is_outermost_main_frame; int32 transition_type; int32 previews_state; bool upgrade_if_insecure; bool is_revalidating; mojo_base.mojom.UnguessableToken? throttling_profile_id; mojo_base.mojom.UnguessableToken? fetch_window_id; string? devtools_request_id; string? devtools_stack_id; bool is_fetch_like_api; bool is_favicon; RequestDestination original_destination; TrustedUrlRequestParams? trusted_params; mojo_base.mojom.UnguessableToken? recursive_prefetch_token; TrustTokenParams? trust_token_params; WebBundleTokenParams? web_bundle_token_params; array<SourceType>? devtools_accepted_stream_types; NetLogSource? net_log_create_info; NetLogSource? net_log_reference_info; IPAddressSpace target_ip_address_space;};",
"network.mojom.URLLoaderFactory.CreateLoaderAndStart": {
    "definition": "CreateLoaderAndStart(pending_receiver<URLLoader> loader, int32 request_id, uint32 options, URLRequest request, pending_remote<URLLoaderClient> client, MutableNetworkTrafficAnnotationTag traffic_annotation);",
"network.mojom.URLLoaderClient.OnReceiveResponse": {
    "definition": "OnReceiveResponse(URLResponseHead head, handle<data_pipe_consumer>? body, mojo_base.mojom.BigBuffer? cached_metadata);",

URLLoaderFactory.CreateLoaderAndStart(),接受参数URLRequest跟remote<URLLoaderClient>。URLRequest可以分析,URLLoaderClient用于异步回调传送响应数据。URLLoaderClient.OnReceiveResponse将响应数据的body用DataPipeConsumer作为载体。

下图是URLLoaderFactory.CreateLoaderAndStart()抓包,参数分析。 xweb_xhr, URL,header-fields,URL-argurments,Cookie。

现在我再将Chrome.IPC.Sniffer,升级成支持DataPipe.Sniffer。抓包URLLoaderClient.OnReceiveResponse()。抓包js,json数据,PNG文件,视频流等。

 

从wireshark不难看出,有大于一半的DataPipe没有能够成功sniff出来。我在上面过滤出来的OnReceiveResponse(...)[+1 native handle],就意味着有一个DataPipe。能够成功sniff的,我都将其内容生成一个新包RawBuffer。而sniff失败的原因,是因为DataPipeDispatcher的序列化同时包含一个sharedMemory的句柄的序列化,我必须要将这个句柄DuplicateHandle出来备用,才能在每次监测到写控制时读得内容。但是mojo也在DuplicateHandle,并且同时关闭源句柄。这就是在跟它比速度啊。但是这个DuplicateHandle,mojo是十分频繁地在做的。如果一秒内只有几个还好,实际上却是几十,100地频繁做。

下图展示WeChatAppEx--type=Browser进程的DuplicateHandle次数统计。

 在开发过程发现,MergePort可以将自身以外的两个node之间建立MessagePipe连接。DataPipeConsumer还可以由中间人转发给其它node。试想一下,有这么一条数据流,A->me->B。如果me只是将数据转发,并且两两之间是用DataPipe进行数据传送,那么me将DataPipeConsumer传递给B使用,那就高效得多了。

下图展示,Utility--type=network原本将网络数据发送给Browser,由Browser转发送到Render-4的。变成Browser为Utility跟Render-4牵手,把DataPipeConsumer交给Render-4,之后Utility跟Render-4直接联谊。

 

 

如何打开小程序的devtools。

github有开源项目WeChatOpenDevTools。原理是,拦截WeChatWin.dll调用CreateProcessW,追加命令行选项--enable-xweb-inspect。拦截WeChatAppEx启动小程序的函数入口,修改Json选项,添加enable_vconsole:true。还是修改WeChatAppEx一处菜单项相关的字符串为DevTools。要部署新的东西太麻烦,用WinDbg代行所有流程后,发现是可行的。然后在逆向分析,我又发现这些都由WeChat进程配置控制的。enable-xweb-inspect由wmpf_host_export.dll一个全局json控制,vconsole由AppletPkgDownLoadMgr的属性控制。只要修改这两处,就可以顺利开启vconcole,并且不需要修改菜单项字符串以及其它奇奇怪怪的东西。Mini-Program,这个名字有点chinglish。在它们的代码命名也好像不用Mini-Program。Applet才是它的本名。凡是Game跟Applet相关的类,服务,函数等,基本都是跟小程序相关的。就连公众号也是在使用WMPF,不过就没有vconsole可用。不管怎么,vconsole这是一个它家做出来让人用的功能,我们做的事情只是调它出来用,应该不违反什么。

Wmpf是一个完整的Chrome。WeChat.exe将Browser进程实现成它的一个子服务进程,XPlugin WMPF就是通过wmpf_host_export.dll的MojoEmbedder启动Wmpf的Browser进程,并建立Ipc关系。WeChat作为WmpfHost角色。Browser如何打开小程序,明白了交互的过程,就清楚谁控制了vconsole。当Browser的小程序图标被点击时,onclick事件在Render处理js脚本,需要打开小程序,就会调用Browser的服务,Browser调用WeChat的身为WmpfHost的服务。这就十分明显,是由WeChat控制了vconsole的权限。

下图是85xx版的WMPF-Applet,有vconsole,没有devtool

下图是112xx版的WMPF-Applet,不但有vconsole,还有devtool。只能看到请求的url,看不到返回数据,以及请求相关的头部字段。但是Applet的DevTool还是被阉割限制了不少功能。

下图是WMPF的主页,它的DevTool是最完整的,但只允许调试主页。

 

本篇到这里,下一篇再见。

 

逆向WeChat(六,通过嗅探mojo抓包小程序https,打开小程序devtool)

逆向WeChat(五,mmmojo, wmpfmojo)

逆向通达信 x 逆向微信 x 逆向Qt (趣味逆向,你未曾见过的signal-slot用法)

逆向WeChat(四,mars, 网络模块)

逆向WeChat(三, EventCenter, 所有功能模块的事件中心)

逆向WeChat (二, WeUIEngine, UI引擎)

逆向wechat(一, 计划热身)

我还有逆向通达信系列

我还有一个K线技术工具项目KTL可以用C++14进行公式,QT,数据分析等开发。

标签:逆向,mojom,sniff,WeChat,bool,mojo,Browser
From: https://www.cnblogs.com/bbqzsl/p/18370679

相关文章

  • CTF逆向:将数组作为函数代码执行
    例题攻防世界BABYREflag判定条件为这个if(v5==14&&(*judge)(s))注意judge本质上是一个数组,(*judge)(s)则说明judge数组中的内容被当做代码执行但前面又有for(i=0;i<=181;++i)judge[i]^=0xCu;judge数组中的内容进行加密所以需要进行patch......
  • js逆向基础14异步编程3
    上节课遗留.finally.finally()方法不管Promise对象最后的状态如何都会执行.finally()方法的回调函数不接收任何的参数,也就是你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected的它最终返回的默认会是一个上一次的Promise对象值,不过抛出的是一个异常......
  • 京东h5st参数js逆向
    扣代码的环节挺简单的就不讲了直接到重点发现许多包都会有一个h5st的加密参数那么我们就要看这个参数是怎么生成的我们可以根据请求堆栈找到h5st的入口当然还有一种更简单的方法就是直接全局搜索h5st这里采用后者这里sign函数就是h5st生成的地方但是这是个promi......
  • js逆向--cai招网
    js逆向--cai招网一、寻找加密入口1、抓包2、关键字搜索寻找加密入口二、调试js三、python代码实现一、寻找加密入口1、抓包目标数据来源网址为:https://search.bidcenter.com.cn/search?keywords=%E5%85%AC%E5%85%B1%E4%BD%8F%E5%AE%85,打开开发者工具进行......
  • 吉林长春产权交易中心JS逆向:魔改AES请求加密与解密
    吉林长春产权交易中心JS逆向:魔改AES请求加密与解密......
  • CI3在路由规则中使用回调函数来处理逆向引用
      创建yy类classyyextendsCI_Controller{publicfunction__construct(){parent::__construct();}publicfunctionfy(){echo'home/yy';}publicfunctionfy2(){echo'home/yy2';}} $route[......
  • DAY87 APP 攻防-安卓逆向篇&Smail 语法&反编译签名重打包&Activity 周期&Hook 模块
    1、APK逆向-数据修改-结构&格式2、APK逆向-逻辑修改-Smail语法3、APK逆向-视图修改-Activity&Xml#章节点:1、APP资产-内在提取&外在抓包2、APP逆向-反编译&删验证&重打包3、APP安全-存储&服务&组件&注册等演示案例:ØAPK逆向-数据修改-结构&格式ØA......
  • 【js逆向专题】6.对称加密
    本笔记仅供学习交流使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,请各位自觉遵守相关法律法规。小节目标:了解对称加密算法种类熟悉加密算法工作模式掌握加密算法的实现方式对称加密(加密解密密钥相同):DES、3DES、AES、RC4简介对称式加密就是加......
  • 某税务app登录接口逆向--获取长效ck
    某税务app登录接口逆向–获取长效ck前言:之前分析了web版本的登录接口,各参数都很简单。有朋友私信我说app登录有意外惊喜,俺们做学术研究的不就是喜欢发掘惊喜么,鉴于此有了此文分析一、目的​1.1、实现完整登录接口(账号登录、短信登录)​1.2、实现免短信登录接口​......
  • 【验证码逆向专栏】某某邮政滑块逆向分析
    声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作......