Why
最近对模拟登录很感兴趣,因为很多事情都需要登录了才能做,利用程序实现自动化能做到许多十分有趣的事情。
我已经打手头上这台 TP-LINK 的 WDR5600 主意很久了,趁着最近在学习 HTTP ,正好实现以前的一些想法。
GitHub Repository
Library
- Requests :HTTP for Humans ,涉及到 HTTP 怎能少了它
- json:JSON 数据格式的编码和解码
- urllib.parse:对 URL 编码进行解码
- Chrome开发者工具:调试 HTTP 请求、JavaScript 的利器
How
既然无线路由器是通过 Web 页面登录的,那直接打开登录页面用Chrome开发者工具获取网络通信过程。
输入 http://192.168.1.1 打开登录页面,现在有些无线路由器只需要密码就可以登录了,我手头上的就是这种:
输入密码
administrator点登录开始抓包:
在 Request Payload 可以很明显看到
{"password":"WaQ7xPr3L41vMwK"},Response 返回{ "stok": "b686b22c7a3f1c1fb983b0e30aa98fae", "error_code": 0 },password应该就是我们要找密码字段,返回的stok相当于后续通信的token口令,error_code的值为0表示认证通过。但是请求的
{"password":"WaQ7xPr3L41vMwK"}和真正的密码administrator完全不同,应该是对密码进行了加密处理,打开登录界面时加载了许多 JavaScript 文件,猜测可能是通过 JavaScript 来加密,切换到 Chrome 开发者工具的 Soures 标签页,尝试搜索password、pwd等密码相关的关键字,多试几次就可以找到加密代码,实在不行就设置断点单步查看登录涉及的代码。设置断点后在
orgAuthPwd函数发现传入的pwd参数值为真实的密码administrator,最后返回一个带参数的securityEncode函数,继续运行可以看到变量output的值在一步步变为登录请求的{"password":"WaQ7xPr3L41vMwK"}
既然找到了加密代码,那就马上用 Python 重写:
1234567891011121314151617181920212223242526272829def encrypt_pwd(password):input1 = "RDpbLfCPsJZ7fiv"input3 = "yLwVl0zKqws7LgKPRQ84Mdt708T1qQ3Ha7xv3H7NyU84p21BriUWBU43odz3iP4rBL3cD02KZciXTysVXiV8ngg6vL48rPJyAUw0HurW20xqxv9aYb4M9wK1Ae0wlro510qXeU07kV57fQMc8L6aLgMLwygtc0F10a0Dg70TOoouyFhdysuRMO51yY5ZlOZZLEal1h0t9YQW0Ko7oBwmCAHoic4HYbUyVeU3sfQ1xtXcPcf1aT303wAQhv66qzW"len1 = len(input1)len2 = len(password)dictionary = input3lenDict = len(dictionary)output = ''if len1 > len2:length = len1else:length = len2index = 0while index < length:# 十六进制数 0xBB 的十进制为 187cl = 187cr = 187if index >= len1:# ord() 函数返回字符的整数表示cr = ord(password[index])elif index >= len2:cl = ord(input1[index])else:cl = ord(input1[index])cr = ord(password[index])index += 1# chr() 函数返回整数对应的字符output = output + chr(ord(dictionary[cl ^ cr]) % lenDict)return output有了加密后的密码,顺理成章地将它作为 Payload 发送请求,返回的响应为 JSON 格式数据,通过 json 库解析获得 token:
123456encryptPwd = '加密后的密码'url = 'http://192.168.1.1/'headers = {'Content-Type': 'application/json; charset=UTF-8'}payload = '{"method":"do","login":{"password":"%s"}}' % encryptPwdresponse = requests.post(url, data=payload, headers=headers)stok = json.loads(response.text)['stok']获得 stok 后就可以将它加入请求 URL 中获取数据了
例如获取用户当前的上传速度、下载速度、限速信息等;返回的信息为 JSON 格式数据,可以解析做格式化处理增强可读性。
获取数据代码:
12345stok = json.loads(response.text)['stok']headers = {'Content-Type': 'application/json; charset=UTF-8'}url = '%sstok=%s/ds' % ('http://192.168.1.1/',stok)payload = '{"hosts_info":{"table":"host_info"},"method":"get"}'response = requests.post(url, data=payload, headers=headers)
返回的 JSON 数据:
123456789101112131415161718192021222324252627282930313233343536373839404142{"hosts_info": {"host_info": [{"host_info_1": {"mac": "D4-45-9D-62-8E-2D","up_speed": "45773","ssid": "","plan_rule": [],"ip": "192.168.1.100","type": "0","is_cur_host": "0","cfg_valid": "1","blocked": "0","down_limit": "0","down_speed": "808","hostname": "hehe","up_limit": "0"}},{"host_info_2": {"mac": "5A-54-33-0C-92-2D","up_speed": "59","ssid": "aaa","plan_rule": [],"wifi_mode": "0","ip": "192.168.1.98","type": "1","is_cur_host": "1","cfg_valid": "1","blocked": "0","down_limit": "0","down_speed": "70","hostname": "HaHa","up_limit": "0"}}]},"error_code": 0}对 JSON 数据进一步可视化处理后:
More
根据上面的分析,发现整个过程的关键点有几个:
- 请求参数
- 密码加密函数
- 获取 stok
获取到 stok 可以做的事情就很多了,可以更改请求参数(具体参数要提前抓取)获取数据、操作路由器,有兴趣的可以根据我的代码自行拓展。