Fiddler抓包一键生成调用代码

首先我们的需求场景是

用Fiddler抓到某个接口调用后,用代码来模拟调用,一般我们写代码会有3个步骤:

  • 1设置http请求相关的参数:header,method,url,cookie等

  • 2设置post的body(如果是post的话需要)

  • 3拿到返回的body(一般我们需要拿到接口的返回体进行解析)

假如这3个步骤的代码全部都能一键生成那该多爽,我终于站在巨人的肩膀上搞定了!

搞定的效果如下图:

f9cd7c0025259fb9f9deb2d2d198d5d8.gif
image

上面是对于csharp 采用自带的 HttpClient的代码生成演示,还可以针对java kotlin,python,nodejs等

本篇的主要功能都是在FiddlerScript里面完成,主要包含3块扩展

  • 增加自定义右键菜单

  • 增加控制开关

  • 代码获取请求上下文并导出har

  • 使用脚本完成process的封装并调用

1. 增加右键菜单

点中某个Session然后点击右键菜单,选择生成指定语言的代码,这样使用起来最方便,如下图:ddd5b4bccd855cf582cb3512880deb10.png

新增右键菜单的扩展方式是 【一个ContextAction+一个function】

例如:

public static ContextAction("C#-httpclient", "生成代码")
 function do1(arrSess: Session[]) {  doStar(arrSess, "csharp","httpclient"); }

代表新增一个 一级菜单叫 生成代码,二级菜单叫 "C#-httpclient"

下面的function就是点击需要响应的方法实现,默认是Session数组,因为可以选择多个。

2. 控制开关

前面说有3个步骤,除了第一个步骤是核心的,其他2个步骤都是将json转为实体类定义,是辅助的。所以都设置开关可以人为控制要不要生成这2块的代码

如下图:

00bec3017ae94102f7a9ce8634dbb1a5.png
image

新增开关的方式是定义【一个RulesOption+一个对应接收的变量】

public static RulesOption("关闭请求体转代码", "生成代码")
 var m_DisableReuqest: boolean = false;

代表新增一个 以及菜单叫生成代码,二级菜单叫 "关闭请求体转代码",类型是bool,因为下面对应接收的变量是布尔类型!

3. 通过选中Session拿到整个请求的上下文

上下文包括,请求的各种参数,比如url,header,method,request,response等

a5d25ba1f6e2175e9dd72d1d4863aac7.png
image

Fillder有一个api可以导出har文件,这个har格式是谷歌提出来的一个用来描述一个请求的标准定义,如上图

关于har格式的详细文档:http://groups.google.com/group/http-archive-specification/

那如何在Fiddler里面将Session导出har呢

b244ed27b1604b3bfd27babc03bc2217.png
image
4e1343ee070e2fb2db4e439d029c992d.png
image
5062bcaf9d3afc04a91ead838b3a6986.png
image

那用代码如何导出呢?

//这种方式为导出到变量 注意是Fiddler 4.6.2.0版本之后支持的
var oExportOptions = FiddlerObject.createDictionary(); 
oExportOptions.Add(“ExportToString”, “true”);
FiddlerApplication.DoExport("HTTPArchive v1.2", oSessions,oExportOptions, null);
//这个就是了
var sOutput: String = oExportOptions[“OutputAsString”];
//这种方式为导出到指定路径
var oExportOptions = FiddlerObject.createDictionary(); 
oExportOptions.Add("Filename", "对应的路径"); 
FiddlerApplication.DoExport("HTTPArchive v1.2", oSessions,oExportOptions, null);

这里我采用了第二种方式,先把选中的Session导出一个har文件,然后将这个har文件作为下一个process的入参,得到我想要结果!

下面隆重介绍根据har来生成请求代码的工具:httpsnippet

开源地址:https://github.com/Kong/httpsnippet

Kong的话有个很有有名的网关想必大家都听说过!

这里我已经把这个程序包装成在windows系统可以独立运行的exe了,可以在文章末尾获取下载链接。

这里我稍微改造了一下代码,把har文件的requestBody和responseBody也提取出来,为了是生成对应的POJO代码做入参.

将json生成实体类POJO这里用了另外一个工具:quicktype

开源地址:https://github.com/quicktype/quicktype

也包装成在windows系统可以独立运行的exe了。

好了,组装一起:

  • 先通过代码生成har文件

  • 然后用httpsnippet生成指定语言的代码,并导出har中的requestBody和responseBody

  • 分别将requestBody和responseBody作为参数让quicktype生成实体类代码

整个的完整代码如下,按照如下步骤copy到fiddler的脚本编辑器中即可:

首先打开脚本编辑器:

0250739bc98655f64db755c8090d1f12.png
image

随便找到一个空白的地方,然后把下面的代码复制进去:

public static RulesOption("关闭请求体转代码", "生成代码")
 var m_DisableReuqest: boolean = false;

 public static RulesOption("关闭返回体转代码", "生成代码")
 var m_DisableResponse: boolean = false;
   
 public static ContextAction("C#-httpclient", "生成代码")
 function do1(arrSess: Session[]) {  doStar(arrSess, "csharp","httpclient"); }
 public static ContextAction("C#-restsharp", "生成代码")
 function do2(arrSess: Session[]) { doStar(arrSess, "csharp","restsharp"); }

 public static ContextAction("Java-okhttp", "生成代码")
 function do3(arrSess: Session[]) {  doStar(arrSess, "java","okhttp"); }
 public static ContextAction("Java-asynchttp", "生成代码")
 function do4(arrSess: Session[]) {  doStar(arrSess, "java","asynchttp"); }
 public static ContextAction("Java-nethttp", "生成代码")
 function do5(arrSess: Session[]) {  doStar(arrSess, "java","nethttp"); }
 public static ContextAction("Java-unirest", "生成代码")
 function do6(arrSess: Session[]) {  doStar(arrSess, "java","unirest"); }

 public static ContextAction("Kotlin-okhttp", "生成代码")
 function do7(arrSess: Session[]) {  doStar(arrSess, "kotlin","okhttp"); }
   
 public static ContextAction("JavaScript-xhr", "生成代码")
 function do8(arrSess: Session[]) {  doStar(arrSess, "javascript","xhr"); }
 public static ContextAction("JavaScript-jquery", "生成代码")
 function do9(arrSess: Session[]) {  doStar(arrSess, "javascript","jquery"); }
 public static ContextAction("JavaScript-fetch", "生成代码")
 function do10(arrSess: Session[]) {  doStar(arrSess, "javascript","fetch"); }
 public static ContextAction("JavaScript-axios", "生成代码")
 function do11(arrSess: Session[]) {  doStar(arrSess, "javascript","axios"); }
  
 public static ContextAction("Node-native", "生成代码")
 function do12(arrSess: Session[]) {  doStar(arrSess, "node","native"); }
 public static ContextAction("Node-request", "生成代码")
 function do13(arrSess: Session[]) {  doStar(arrSess, "node","request"); }
 public static ContextAction("Node-fetch", "生成代码")
 function do14(arrSess: Session[]) {  doStar(arrSess, "node","fetch"); }
 public static ContextAction("Node-axios", "生成代码")
 function do15(arrSess: Session[]) {  doStar(arrSess, "node","axios"); }   
 public static ContextAction("Node-unirest", "生成代码")
 function do16(arrSess: Session[]) {  doStar(arrSess, "node","unirest"); } 
 
 public static ContextAction("Python3-http.client", "生成代码")
 function do17(arrSess: Session[]) {  doStar(arrSess, "python","python3"); }
 public static ContextAction("Python-requests", "生成代码")
 function do18(arrSess: Session[]) {  doStar(arrSess, "python","requests"); }
   
 public static ContextAction("ObjectiveC-nsurlsession", "生成代码")
 function do19(arrSess: Session[]) {  doStar(arrSess, "objc","nsurlsession"); }

 public static ContextAction("Ruby-net::http", "生成代码")
 function do20(arrSess: Session[]) {  doStar(arrSess, "ruby","native"); }

 public static ContextAction("Swift-nsurlsession", "生成代码")
 function do21(arrSess: Session[]) {  doStar(arrSess, "swift","nsurlsession"); }
   
 public static ContextAction("powershell-webrequest", "生成代码")
 function do22(arrSess: Session[]) {  doStar(arrSess, "powershell","webrequest"); }
 public static ContextAction("powershell-restmethod", "生成代码")
 function do23(arrSess: Session[]) {  doStar(arrSess, "powershell","restmethod"); }

 public static ContextAction("Shell-curl", "生成代码")
 function do24(arrSess: Session[]) {  doStar(arrSess, "shell","curl"); }
 public static ContextAction("Shell-httpie", "生成代码")
 function do25(arrSess: Session[]) {  doStar(arrSess, "shell","httpie"); }
 public static ContextAction("Shell-wget", "生成代码")
 function do26(arrSess: Session[]) {  doStar(arrSess, "shell","wget"); }
  
 public static ContextAction("Go-NewRequest", "生成代码")
 function do27(arrSess: Session[]) { doStar(arrSess, "go","native"); }
   
 public static ContextAction("Clojure-clj_http", "生成代码")
 function do28(arrSess: Session[]) { doStar(arrSess, "clojure","clj_http"); }

 public static ContextAction("C-Libcurl", "生成代码")
 function do29(arrSess: Session[]) { doStar(arrSess, "c","libcurl"); }
 
 public static ContextAction("PHP-curl", "生成代码")
 function do30(arrSess: Session[]) {  doStar(arrSess, "php","curl"); }
 public static ContextAction("PHP-http1", "生成代码")
 function do31(arrSess: Session[]) {  doStar(arrSess, "php","http1"); }
 public static ContextAction("PHP-http2", "生成代码")
 function do32(arrSess: Session[]) {  doStar(arrSess, "php","http2"); }  
  
 public static function doStar(oSessions: Session[], target: String,client:String) {
     //注意看这里,请下载我给的这2个exe并替换成你电脑中正确的目录
  var httpsnippet = "E:\\workspace\\github\\test\\httpsnippet.exe";
  var quicktype = "E:\\workspace\\github\\test\\quicktype.exe";
  var oExportOptions = FiddlerObject.createDictionary(); 
  var tempPath2 = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler.har");
  if(System.IO.File.Exists(tempPath2)){
   System.IO.File.Delete(tempPath2); 
  }
  var tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler.json");
  if(System.IO.File.Exists(tempPath)){
   System.IO.File.Delete(tempPath); 
  }
  var tempRequestBodyPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler_requestBody.json");
  if(System.IO.File.Exists(tempRequestBodyPath)){
   System.IO.File.Delete(tempRequestBodyPath); 
  }
  var tempResponseBodyPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler_responseBody.json");
  if(System.IO.File.Exists(tempResponseBodyPath)){
   System.IO.File.Delete(tempResponseBodyPath); 
  }
  oExportOptions.Add("Filename", tempPath2); 
  FiddlerApplication.DoExport("HTTPArchive v1.2", oSessions,oExportOptions, null);  
  System.IO.File.Move(tempPath2, tempPath);
  if(!System.IO.File.Exists(tempPath)){
   MessageBox.Show("生成代码失败", "No action");
   return;  
  }
  var rtPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler_rt");
  if(System.IO.Directory.Exists(rtPath))System.IO.Directory.Delete(rtPath,true);
  if(!doProcess(httpsnippet, "\""+tempPath+"\" -t "+target+" -c "+client+" -o " + "\""+rtPath+"\"")){
   MessageBox.Show("生成代码错误", "No action");
   return;  
  }
  var file = System.IO.Directory.GetFiles(rtPath);
  if(file.Length!=1){
   MessageBox.Show("生成代码错误", "No action");
   return; 
  }
  var json = System.IO.File.ReadAllText(file[0]);
  System.IO.File.Delete(file[0]);
  var rtPath1 = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler_request_body");
  if(System.IO.File.Exists(rtPath1))System.IO.File.Delete(rtPath1);
  if(!m_DisableReuqest && System.IO.File.Exists(tempRequestBodyPath)){
  
   json += getJsonCode(quicktype,tempRequestBodyPath,rtPath,rtPath1,target,"FiddlerRequest");
  }
  rtPath1 = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "fiddler_response_body");
  if(System.IO.File.Exists(rtPath1))System.IO.File.Delete(rtPath1);
  if(!m_DisableResponse && System.IO.File.Exists(tempResponseBodyPath)){
   json += getJsonCode(quicktype,tempResponseBodyPath,rtPath,rtPath1,target, "FiddlerReponse"); 
  } 
  
  Clipboard.SetText(json);
  MessageBox.Show("代码生成成功,已复制到剪贴板"); 
 }
  
 static function getJsonCode(file: String,tempRequestBodyPath:String,rtPath:String,rtPath1:String,target:String,type:String): String {
  var json = "";
  var tmp1 = "";
  if(target == 'csharp'){
   tmp1 = "--quiet --telemetry disable --features just-types --array-type list --no-check-required --namespace \"Fiddlers\" --lang \"" + target + "\" --top-level \""+type+"Model\" \"" + tempRequestBodyPath + "\"" +" -o " + "\""+rtPath1+"\"";
  }
  else if(target == 'kotlin'){
   tmp1 = "--quiet --telemetry disable --framework just-types --lang \"" + target + "\" --top-level \""+type+"Model\" \"" + tempRequestBodyPath + "\"" +" -o " + "\""+rtPath1+"\"";
  }
  else if(target == 'java'){
   tmp1 = "--quiet --telemetry disable --array-type list --just-types --package \"Fiddlers\" --lang \"" + target + "\" --top-level \""+type+"Model\" \"" + tempRequestBodyPath + "\"" +" -o " + "\""+rtPath+"\\test"+"\"";
    
  }
  else {
   tmp1 = "--telemetry disable --just-types  --lang \"" + target + "\" --top-level \""+type+"Models\" \"" + tempRequestBodyPath + "\"" +" -o " + "\""+rtPath1+"\""; 
  }
   
  doProcess(file, tmp1)
  if(System.IO.File.Exists(rtPath1)){
   json += "\r\n//"+type+"-POJO\r\n" + System.IO.File.ReadAllText(rtPath1).Replace("package quicktype","");
  }
   
  if(target == 'java'){
   var javaFiles = System.IO.Directory.GetFiles(rtPath,"*.java"); 
   if(javaFiles.Length>0){
    json += "\r\n//"+type+"-POJO\r\n" ;
    for (var i:int = 0; i<javaFiles.Length; i++)
    {
     json += System.IO.File.ReadAllText(javaFiles[i]).Replace("package Fiddlers;","")
     System.IO.File.Delete(javaFiles[i]);
    }
   }
  }
  return json;
 }
   
 static function doProcess(file: String,paramsList:String): Boolean {
  var process = new System.Diagnostics.Process();
  process.StartInfo.FileName = file;
  process.StartInfo.Arguments = paramsList;
  process.StartInfo.CreateNoWindow = true;
  process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
  process.StartInfo.UseShellExecute = false;
  process.StartInfo.Verb = "runas";
  process.StartInfo.RedirectStandardError = true;
  process.StartInfo.RedirectStandardOutput = true;
  process.Start();
  process.WaitForExit();
  process.Dispose(); 
  return true;
 }

然后下载:httpsnippet和quicktype这2个可执行文件。获取下载地址的方法:关注本公众号后发送文本 :Fiddler ,会告诉你百度网盘链接!

下载zip包后然后把这2个文件解压到你的电脑的某个目录。

在回到脚本中找到 doStar 方法中修改成正确的目录(别忘记了)

Enjoy!!!

其实在FiddlerScript里面还是能玩出很多花样的,只不过写扩展得用JScript.net(js+csharp的语法),什么语言不重要,重要的是实现了我想要的效果。


我是正东,学的越多不知道也越多。如果决定去深究一个东西, 一定要完全搞懂, 并认真总结一篇博客让以后能在短时间拾起来 ( 因为不搞懂你很难写一篇半年后还能理解的博客 )

为了使其更容易理解FD 易语言 版原理组成,可以参考2张原理图(有点丑): 图 1-1 图1-2 图1-1就是  易语言和Fiddler接口的整体交互实现流程, FD是一个HTTP代理服务器,我们知道,一旦开启了HTTP代理,所有浏览器的数据会先通过FD,FD接着把数据发给易语言,易语言对他们进行处理,处理后在返回到FD。FD在把处理后的数据正常放通,这样就实现了修改和拦截的功能。当然了,如果会C#语言开发,完全可以在C#完成,本次源码主要就是封装成了易语言,达到便捷快速的作用。 图1-2为动态填表技术部分,我们知道 精易模块 大名鼎鼎的功能就是 普通填表,它可以在浏览器加载完成页面的情况下,在随意去修改浏览器上的内容,而Fiddler我们知道,它的原理是通过代理来修改数据,然而它只能在数据返回的时候修改它,待它返回完成后,就无法进行二次修改了,而图1-2即是解决这个问题,能达到和精易模块 普通填表 的功能一样,可以在网页加载完成的情况下,还是可以二次修改网页上的内容,比如修改输入框内容,点击某个按钮等。 其原理比较另类,需要鸡助的借助一个服务器,我们还得自己搭建一个HTTP服务器,里面就是专门负责存放要填表的代码,FD会往网页注入一行100毫秒循环1次的一段代码,这段代码就会不断的向HTTP服务器索取最新的填表代码,一旦易语言需要填表某个地方,将会把代码先传到HTTP服务器,那么HTML就能取出最新代码进行填表,进而达到动态填表效果! 那么,本次开源一共分为2个部分 1.易语言处理端的源码            这块源码其实不算是核心部分,它只不过是处理数据的一段小程序,比如进行修改,替换等操作而已。 真正拦截,和实现HTTP代理是在C#完成的。 2.C#源码                                这块源码才是核心源码,它是加载了Fiddler接口,实现建立起HTTP代理服务器,所有的网页数据将会通过它,它在把数据发送给易语言进行处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值