背景
战网安全令独立服关闭,导致之前没有存储密钥的安全令统统不好用了,经过一番研究发现可以通过反复绑定安全令到战网账号的方式获取到安全令的密钥,但是这个方式需要去战网账号上面将安全令进行解绑,暴雪没有提供解绑接口,一番研究发现战网的登录机制还是比较复杂的。时间比较紧张,考虑用selenium模拟网页操作的方式来解绑安全令。
方案
1.安装docker版本的模拟器
docker run -d -p 4444:4444 selenium/standalone-chrome
2.测试模拟器
#!/usr/bin/python # -*- coding: UTF-8 -*- import time from selenium import webdriver import requests import json from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException options = webdriver.ChromeOptions() options.add_argument('--headless') # example driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options) print("尝试打开官网") driver.get('https://account.battle.net/') print(driver.page_source.encode("utf-8")) driver.quit()
3.完整脚本
#!/usr/bin/python # -*- coding: UTF-8 -*- import time from selenium import webdriver import requests import json from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException ACCOUNT = '' PASSWORD = '' TOKEN = 'Bearer xx' def rollback(serial, restoreCode, deviceSecret): options = webdriver.ChromeOptions() options.add_argument('--headless') # example driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options) try: # 登录战网 print("尝试打开官网") driver.get('https://account.battle.net/') element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "accountName")) # is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element(By.ID, "someId").is_displayed()) print("官网已经打开....") accountName = driver.find_element_by_id('accountName') accountName.send_keys(ACCOUNT) password = driver.find_element_by_id('password') password.send_keys(PASSWORD) driver.find_element_by_id("submit").click() print("输入账号密码..") time.sleep(5) if check_exists_by_xpath(driver, '//button[@formaction="/login/en/authenticator/choose"]'): uasc = driver.find_element_by_xpath('//button[@formaction="/login/en/authenticator/choose"]') uasc.click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "authValue")) authValue = driver.find_element_by_id('authValue') a = getauth(serial, restoreCode, deviceSecret) authValue.send_keys(a['code']) #输入安全令密钥 print("输入安全令...") driver.find_element_by_id("submit").click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "app")) print("尝试解除安全令...") #解除安全令 driver.get('https://account.battle.net/security/authenticator/detach') element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "attach-detach-confirm")) print("点击解除安全令按钮...") driver.find_element_by_id("attach-detach-confirm").click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element_by_xpath('//input[@maxlength="8"]')) print("输入安全令密钥...") authValue = driver.find_element_by_xpath('//input[@maxlength="8"]') # print(getAllAttr(driver, authValue)) # print(authValue.get_attribute('innerHTML')) a = getauth(serial, restoreCode, deviceSecret) authValue.send_keys(a['code']) #输入安全令密钥 print("解除安全令.按钮查找中.......") btn1 = driver.find_elements_by_class_name("btn-primary")[0] time.sleep(5) # print(getAllAttr(driver, btn1)) print("解除安全令.按钮点击.......") btn1.click() time.sleep(10) check = hasAuth() if("restoreCode" in check): print("解除安全令.失败了.......") else: print("解除安全令.成功.......") except Exception as err: print(f"Unexpected {err}") time.sleep(10) driver.quit() def getAllAttr(driver, element): attr = driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element) attr['innerHTML'] = element.get_attribute('innerHTML') return attr def check_exists_by_xpath(driver, xpath): try: driver.find_element_by_xpath(xpath) except NoSuchElementException: return False return True def getauth(serial, restoreCode, deviceSecret): url = "http://192.168.1.99:8080/api/directGetCode" payload = { 'restore': restoreCode, 'serial': serial, 'secret': deviceSecret } headers = { 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', } response = requests.request("POST", url, headers=headers, data=payload) return response.json() def calc(serial, restoreCode): url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator/device" payload = json.dumps({ "serial": serial, "restoreCode": restoreCode }) headers = { 'authorization': TOKEN, 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload) return response.json() def hasAuth(): url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator" headers = { 'authorization': TOKEN, 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', 'Content-Type': 'application/json' } response = requests.request("GET", url, headers=headers) return response.json() def getSecret(serial, restoreCode): print("try to handel: " + serial + " : " + restoreCode) check = hasAuth() if("restoreCode" in check): print("account has auth with: " + check['serial'] + ", try to unlock..") doGetSecret(check['serial'], check['restoreCode']) return doGetSecret(serial, restoreCode) def doGetSecret(serial, restoreCode): c = calc(serial, restoreCode) if("deviceSecret" in c): a = getauth(serial, restoreCode, c['deviceSecret']) rollback(serial, restoreCode, c['deviceSecret']) return c else: raise Exception("doGetSecret failed: " + str(c)) if __name__ == '__main__': sec = getSecret("US230521228545", "W8M2HRA0EK") print(sec)
参考资料:
https://stackoverflow.com/questions/45323271/how-to-run-selenium-with-chrome-in-docker
https://authenticator-rest-api.bnet-identity.blizzard.net/swagger-ui/
这个网站已经打不开了该怎么办
重新研究暴雪api呗,自己研究。
老哥用密钥如何获取安全令验证码啊,这个代码在哪里能得到
已经会了,感谢你