摘要:

     
 由于雷电模拟器的命令行工具非常适合第三方代码控制,所以越来越多的人开始使用雷电模拟器进行脚本、逆向等软件的开发。但是也有很多人不清楚用程序控制雷电模拟器的原理和方法。所以我以比较容易入门的Python代码为例,从原理到方法讲解一下雷电模拟器的控制过程。

雷电模拟器的控制原理

     
 安装好雷电模拟器之后,大家可以在安装目录下面找到ldconsole.exe和ld.exe两个命令行程序。此外,启动雷电模拟器之后,可以在共享文件夹→高级选项里面找到一个Windows路径。这个路径和模拟系统里面/sdcard/Pictures路径,两者是想通的。即在模拟器中复制文件到/sdcard/Pictures路径下,对应的Windows路径下面也会多出一个这样的路径。这个对于识图脚本来说,是非常的友好——因为在模拟器中截图无需经过dump步骤,就可以直接在控制主机上读到图并进行处理。此外两边甚至能够动态、高效的传递共享文件,这个功能就完爆其他模拟器。

     
 下面说说ld和ldconsole两个命令。Ld命令用来执行adb命令,但是和普通adb不同的是,你无需知道模拟器设备的序列号,而是直接可以通过序号来发命令。而且目标不存在能够很快反馈。这个可以加快脚本的效率,并且提高脚本的稳定性——因为不存在也不会崩溃。此外ld命令的连接非常稳定,不会受到物理USB线的影响,不会时断时续不稳定。这个对一些要长期跑的脚本来说,简直就是致命吸引。

     
 ldconsole也是一个很不错的工具,可以控制模拟器参数的设定,包括imei,序列号,手机号分辨率等等参数,还可以检测和控制模拟器的启动和关闭。甚至能够模拟输入,按键,滑动等操作。而且这种模拟操作是模拟器本身的开发接口,比adb要更稳定和灵敏,非常适合开发人员使用。

       综上,雷电模拟器的控制原理,其实是通过控制命令行接口来控制模拟器的行为。

雷电模拟器控制的示例
class Dnconsole: # 请根据自己电脑配置 console = 'D:\\Changzhi\\dnplayer2\\ldconsole.exe
' ld = 'D:\\Changzhi\\dnplayer2\\ld.exe ' share_path =
'C:/Users/zerglurker/Documents/雷电模拟器/Pictures' @staticmethod def get_list():
cmd = os.popen(Dnconsole.console + 'list2') text = cmd.read() cmd.close() info
= text.split('\n') result = list() for line in info: if len(line) > 1: dnplayer
= line.split(',') result.append(DnPlayer(dnplayer)) return result @staticmethod
def list_running() -> list: result = list() all = Dnconsole.get_list() for dn
in all: if dn.is_running() is True: result.append(dn) return result
@staticmethod def is_running(index: int) -> bool: all = Dnconsole.get_list() if
index >= len(all): raise IndexError('%d is not exist' % index) return
all[index].is_running() @staticmethod def dnld(index: int, command: str,
silence: bool = True): cmd = Dnconsole.ld + '-s %d "%s"' % (index, command) #
print(cmd) if silence: os.system(cmd) return '' process = os.popen(cmd) result
= process.read() process.close() return result @staticmethod def adb(index:
int, command: str, silence: bool = False) -> str: cmd = Dnconsole.console +
'adb --index %d --command "%s"' % (index, command) if silence: os.system(cmd)
return '' process = os.popen(cmd) result = process.read() process.close()
return result @staticmethod def install(index: int, path: str):
shutil.copy(path, Dnconsole.share_path + str(index) + '/update.apk')
time.sleep(1) Dnconsole.dnld(index, 'pm install /sdcard/Pictures/update.apk')
@staticmethod def uninstall(index: int, package: str): cmd = Dnconsole.console
+ 'uninstallapp --index %d --packagename %s' % (index, package) process =
os.popen(cmd) result = process.read() process.close() return result
@staticmethod def invokeapp(index: int, package: str): cmd = Dnconsole.console
+ 'runapp --index %d --packagename %s' % (index, package) process =
os.popen(cmd) result = process.read() process.close() print(result) return
result @staticmethod def stopapp(index: int, package: str): cmd =
Dnconsole.console + 'killapp --index %d --packagename %s' % (index, package)
process = os.popen(cmd) result = process.read() process.close() return result
@staticmethod def input_text(index: int, text: str): cmd = Dnconsole.console +
'action --index %d --key call.input --value %s' % (index, text) process =
os.popen(cmd) result = process.read() process.close() return result
@staticmethod def get_package_list(index: int) -> list: result = list() text =
Dnconsole.dnld(index, 'pm list packages') info = text.split('\n') for i in
info: if len(i) > 1: result.append(i[8:]) return result @staticmethod def
has_install(index: int, package: str): if Dnconsole.is_running(index) is False:
return False return package in Dnconsole.get_package_list(index) @staticmethod
def launch(index: int): cmd = Dnconsole.console + 'launch --index ' +
str(index) process = os.popen(cmd) result = process.read() process.close()
return result @staticmethod def quit(index: int): cmd = Dnconsole.console +
'quit --index ' + str(index) process = os.popen(cmd) result = process.read()
process.close() return result # 设置屏幕分辨率为1080×1920 @staticmethod def
set_screen_size(index: int): cmd = Dnconsole.console + 'modify --index %d
--resolution 1080,1920,480' % index process = os.popen(cmd) result =
process.read() process.close() return result @staticmethod def touch(index:
int, x: int, y: int, delay: int = 0): if delay == 0: Dnconsole.dnld(index,
'input tap %d %d' % (x, y)) else: Dnconsole.dnld(index, 'input swipe %d %d %d
%d %d' % (x, y, x, y, delay)) @staticmethod def press_key(index: int, key:
int): Dnconsole.dnld(index, 'input keyevent %d' % key) @staticmethod def
swipe(index, coordinate_leftup: tuple, coordinate_rightdown: tuple, delay: int
= 0): x0 = coordinate_leftup[0] y0 = coordinate_leftup[1] x1 =
coordinate_rightdown[0] y1 = coordinate_rightdown[1] if delay == 0:
Dnconsole.dnld(index, 'input swipe %d %d %d %d' % (x0, y0, x1, y1)) else:
Dnconsole.dnld(index, 'input swipe %d %d %d %d %d' % (x0, y0, x1, y1, delay))
@staticmethod def copy(name: str, index: int = 0): cmd = Dnconsole.console +
'copy --name %s --from %d' % (name, index) process = os.popen(cmd) result =
process.read() process.close() return result @staticmethod def add(name: str):
cmd = Dnconsole.console + 'add --name %s' % name process = os.popen(cmd) result
= process.read() process.close() return result @staticmethod def
auto_rate(index: int, auto_rate: bool = False): rate = 1 if auto_rate else 0
cmd = Dnconsole.console + 'modify --index %d --autorotate %d' % (index, rate)
process = os.popen(cmd) result = process.read() process.close() return result
@staticmethod def change_device_data(index: int): # 改变设备信息 cmd =
Dnconsole.console + 'modify --index %d --imei auto --imsi auto --simserial auto
--androidid auto --mac auto' % index process = os.popen(cmd) result =
process.read() process.close() return result @staticmethod def
change_cpu_count(index: int, number: int): # 修改cpu数量 cmd = Dnconsole.console +
'modify --index %d --cpu %d' % (index, number) process = os.popen(cmd) result =
process.read() process.close() return result @staticmethod def
get_cur_activity_xml(index: int): # 获取activity的xml信息 Dnconsole.dnld(index,
'uiautomator dump /sdcard/Pictures/activity.xml') time.sleep(1) f =
open(Dnconsole.share_path + '%d/activity.xml' % index, 'r', encoding='utf-8')
result = f.read() f.close() return result @staticmethod def
get_user_info(index: int) -> UserInfo: xml =
Dnconsole.get_cur_activity_xml(index) usr = UserInfo(xml) if 'id' not in
usr.info: return UserInfo() return usr @staticmethod def
get_activity_name(index: int): text = Dnconsole.dnld(index, 'dumpsys activity
top | grep ACTIVITY', False) text = text.split(' ') for i, s in
enumerate(text): if len(s) == 0: continue if s == 'ACTIVITY': return text[i +
1] return '' @staticmethod def wait_activity(index: int, activity: str,
timeout: int) -> bool: for i in range(timeout): if
Dnconsole.get_activity_name(index) == activity: return True time.sleep(1)
return False @staticmethod def find_pic(screen: str, template: str, threshold:
float): try: scr = cv.imread(screen) tp = cv.imread(template) result =
cv.matchTemplate(scr, tp, cv.TM_SQDIFF_NORMED) except cv.error: print('文件错误:',
screen, template) time.sleep(1) try: scr = cv.imread(screen) tp =
cv.imread(template) result = cv.matchTemplate(scr, tp, cv.TM_SQDIFF_NORMED)
except cv.error: return False, None min_val, max_val, min_loc, max_loc =
cv.minMaxLoc(result) if min_val > threshold: print(template, min_val, max_val,
min_loc, max_loc) return False, None print(template, min_val, min_loc) return
True, min_loc @staticmethod def wait_picture(index: int, timeout: int,
template: str) -> bool: count = 0 while count < timeout: Dnconsole.dnld(index,
'screencap -p /sdcard/Pictures/apk_scr.png') time.sleep(2) ret, loc =
Dnconsole.find_pic(Dnconsole.share_path + '%d/apk_scr.png' % index, template,
0.001) if ret is False: print(loc) time.sleep(2) count += 2 continue print(loc)
return True return False # 在当前屏幕查看模板列表是否存在,是返回存在的模板,如果多个存在,返回找到的第一个模板
@staticmethod def check_picture(index: int, templates: list):
Dnconsole.dnld(index, 'screencap -p /sdcard/Pictures/apk_scr.png')
time.sleep(1) for i, t in enumerate(templates): ret, loc =
Dnconsole.find_pic(Dnconsole.share_path + '%d/apk_scr.png' % index, t, 0.001)
if ret is True: return i, loc return -1, None
下面是调用代码
#注意,请自行导入上面的类代码,否则无法使用 import dnconsole.Dnconsole import time if __name__ ==
'__main__': Dnconsole.launch(0)#打开模拟器 time.sleep(10)#等待启动 #TODO: 其他的控制(touch)
Dnconsole.quit(0)#退出模拟器
 

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信