update root&xp检测提示;从外部群添加好友;修改好友信息;建群达到上限提示
This commit is contained in:
@@ -19,6 +19,7 @@ object Constant {
|
|||||||
val transformation = "AES/CBC/PKCS7Padding"
|
val transformation = "AES/CBC/PKCS7Padding"
|
||||||
var encryptType = SPUtils.getInstance().getInt("encryptType", 1)
|
var encryptType = SPUtils.getInstance().getInt("encryptType", 1)
|
||||||
var autoReply = SPUtils.getInstance().getInt("autoReply", 1)
|
var autoReply = SPUtils.getInstance().getInt("autoReply", 1)
|
||||||
|
var groupStrict = false
|
||||||
var host: String
|
var host: String
|
||||||
get() = SPUtils.getInstance().getString("host", DEFAULT_HOST)
|
get() = SPUtils.getInstance().getString("host", DEFAULT_HOST)
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import org.yameida.worktool.utils.HostTestHelper
|
import org.yameida.worktool.utils.HostTestHelper
|
||||||
import org.yameida.worktool.utils.PermissionHelper
|
import org.yameida.worktool.utils.PermissionHelper
|
||||||
import org.yameida.worktool.utils.PermissionPageManagement
|
import org.yameida.worktool.utils.PermissionPageManagement
|
||||||
|
import org.yameida.worktool.utils.envcheck.CheckHook
|
||||||
|
import org.yameida.worktool.utils.envcheck.CheckRoot
|
||||||
|
|
||||||
|
|
||||||
class ListenActivity : AppCompatActivity() {
|
class ListenActivity : AppCompatActivity() {
|
||||||
@@ -87,7 +89,15 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
val version = "${AppUtils.getAppVersionName()} Android ${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}"
|
val version = "${AppUtils.getAppVersionName()} Android ${DeviceUtils.getSDKVersionName()} ${DeviceUtils.getManufacturer()} ${DeviceUtils.getModel()}"
|
||||||
|
val deviceRooted = CheckRoot.isDeviceRooted()
|
||||||
|
val hook = CheckHook.isHook(applicationContext)
|
||||||
|
if (hook) {
|
||||||
|
tv_version.text = "当前设备存在侵入代码,请勿在本设备使用本程序!!!"
|
||||||
|
} else if (deviceRooted) {
|
||||||
|
tv_version.text = "$version\n本设备已Root,存在一定风险!"
|
||||||
|
} else {
|
||||||
tv_version.text = version
|
tv_version.text = version
|
||||||
|
}
|
||||||
val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName
|
val workVersionName = AppUtils.getAppInfo(Constant.PACKAGE_NAMES)?.versionName
|
||||||
when (workVersionName) {
|
when (workVersionName) {
|
||||||
null -> {
|
null -> {
|
||||||
@@ -107,6 +117,8 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
SPUtils.getInstance().put("appVersion", version)
|
SPUtils.getInstance().put("appVersion", version)
|
||||||
SPUtils.getInstance().put("workVersion", workVersionName)
|
SPUtils.getInstance().put("workVersion", workVersionName)
|
||||||
|
SPUtils.getInstance().put("deviceRooted", deviceRooted)
|
||||||
|
SPUtils.getInstance().put("hook", hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initAccessibility() {
|
private fun initAccessibility() {
|
||||||
@@ -215,7 +227,7 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
if (text.matches("ws{1,2}://[^/]+.*".toRegex())) {
|
if (text.matches("ws{1,2}://[^/]+.*".toRegex())) {
|
||||||
Constant.host = text
|
Constant.host = text
|
||||||
tv_host.text = text
|
tv_host.text = text
|
||||||
HostTestHelper.test()
|
HostTestHelper.testWs()
|
||||||
commentDialog.dismiss()
|
commentDialog.dismiss()
|
||||||
} else {
|
} else {
|
||||||
ToastUtils.showLong("格式异常!")
|
ToastUtils.showLong("格式异常!")
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ public class WeworkMessageBean {
|
|||||||
* 按手机号添加好友 ADD_FRIEND_BY_PHONE
|
* 按手机号添加好友 ADD_FRIEND_BY_PHONE
|
||||||
* 展示群信息 SHOW_GROUP_INFO
|
* 展示群信息 SHOW_GROUP_INFO
|
||||||
* 推送文件(网络图片视频和文件等) PUSH_FILE
|
* 推送文件(网络图片视频和文件等) PUSH_FILE
|
||||||
|
* 解散群聊 DISMISS_GROUP
|
||||||
|
* 从外部群添加好友 ADD_FRIEND_BY_GROUP
|
||||||
* <p>
|
* <p>
|
||||||
* 非操作类型 300
|
* 非操作类型 300
|
||||||
* 机器人普通日志记录 ROBOT_LOG
|
* 机器人普通日志记录 ROBOT_LOG
|
||||||
@@ -66,6 +68,7 @@ public class WeworkMessageBean {
|
|||||||
public static final int DELETED_ANTI_HARASSMENT_RULE = 217;
|
public static final int DELETED_ANTI_HARASSMENT_RULE = 217;
|
||||||
public static final int PUSH_FILE = 218;
|
public static final int PUSH_FILE = 218;
|
||||||
public static final int DISMISS_GROUP = 219;
|
public static final int DISMISS_GROUP = 219;
|
||||||
|
public static final int ADD_FRIEND_BY_GROUP = 220;
|
||||||
|
|
||||||
public static final int ROBOT_LOG = 301;
|
public static final int ROBOT_LOG = 301;
|
||||||
public static final int ROBOT_ERROR_LOG = 302;
|
public static final int ROBOT_ERROR_LOG = 302;
|
||||||
@@ -287,6 +290,20 @@ public class WeworkMessageBean {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(phone, name, markName, markCorp, markExtra, tagList, newFriend, leavingMsg);
|
return Objects.hash(phone, name, markName, markCorp, markExtra, tagList, newFriend, leavingMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Friend{" +
|
||||||
|
"phone='" + phone + '\'' +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", markName='" + markName + '\'' +
|
||||||
|
", markCorp='" + markCorp + '\'' +
|
||||||
|
", markExtra='" + markExtra + '\'' +
|
||||||
|
", tagList=" + tagList +
|
||||||
|
", newFriend=" + newFriend +
|
||||||
|
", leavingMsg='" + leavingMsg + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ fun getRoot(ignoreCheck: Boolean): AccessibilityNodeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sleep(1000)
|
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,16 +136,19 @@ object MyLooper {
|
|||||||
WeworkMessageBean.PUSH_OFFICE -> {
|
WeworkMessageBean.PUSH_OFFICE -> {
|
||||||
WeworkController.pushOffice(message)
|
WeworkController.pushOffice(message)
|
||||||
}
|
}
|
||||||
|
WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> {
|
||||||
|
}
|
||||||
|
WeworkMessageBean.ADD_FRIEND_BY_PHONE -> {
|
||||||
|
WeworkController.addFriendByPhone(message)
|
||||||
|
}
|
||||||
WeworkMessageBean.PUSH_FILE -> {
|
WeworkMessageBean.PUSH_FILE -> {
|
||||||
WeworkController.pushFile(message)
|
WeworkController.pushFile(message)
|
||||||
}
|
}
|
||||||
WeworkMessageBean.DISMISS_GROUP -> {
|
WeworkMessageBean.DISMISS_GROUP -> {
|
||||||
WeworkController.dismissGroup(message)
|
WeworkController.dismissGroup(message)
|
||||||
}
|
}
|
||||||
WeworkMessageBean.PASS_ALL_FRIEND_REQUEST -> {
|
WeworkMessageBean.ADD_FRIEND_BY_GROUP -> {
|
||||||
}
|
WeworkController.addFriendByGroup(message)
|
||||||
WeworkMessageBean.ADD_FRIEND_BY_PHONE -> {
|
|
||||||
WeworkController.addFriendByPhone(message)
|
|
||||||
}
|
}
|
||||||
WeworkMessageBean.SHOW_GROUP_INFO -> {
|
WeworkMessageBean.SHOW_GROUP_INFO -> {
|
||||||
WeworkController.showGroupInfo(message)
|
WeworkController.showGroupInfo(message)
|
||||||
|
|||||||
@@ -187,6 +187,22 @@ object WeworkController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从外部群添加好友
|
||||||
|
* @see WeworkMessageBean.ADD_FRIEND_BY_GROUP
|
||||||
|
* @param message#groupName 外部群
|
||||||
|
* @param message#friend 待添加用户
|
||||||
|
*/
|
||||||
|
@RequestMapping
|
||||||
|
fun addFriendByGroup(message: WeworkMessageBean): Boolean {
|
||||||
|
LogUtils.d("addFriendByGroup(): ${message.groupName} ${message.friend}")
|
||||||
|
return WeworkOperationImpl.addFriendByGroup(
|
||||||
|
message,
|
||||||
|
message.groupName,
|
||||||
|
message.friend
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 推送微盘图片
|
* 推送微盘图片
|
||||||
* @see WeworkMessageBean.PUSH_MICRO_DISK_IMAGE
|
* @see WeworkMessageBean.PUSH_MICRO_DISK_IMAGE
|
||||||
@@ -287,7 +303,7 @@ object WeworkController {
|
|||||||
/**
|
/**
|
||||||
* 按手机号添加好友
|
* 按手机号添加好友
|
||||||
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
|
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
|
||||||
* @param message#friend 待添加用户列表
|
* @param message#friend 待添加用户
|
||||||
*/
|
*/
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
fun addFriendByPhone(message: WeworkMessageBean): Boolean {
|
fun addFriendByPhone(message: WeworkMessageBean): Boolean {
|
||||||
|
|||||||
@@ -219,13 +219,19 @@ object WeworkOperationImpl {
|
|||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP, "创建群失败", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP, "创建群失败", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (createGroupLimit()) {
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_CREATE_GROUP_LIMIT, "建群达到上限", startTime)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
LogUtils.v("未发现建群达到上限")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!groupRename(groupName)) {
|
if (!groupRename(groupName)) {
|
||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_RENAME, "创建群成功 群改名失败", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_RENAME, "创建群成功 群改名失败", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!groupAddMember(selectList)) {
|
if (!groupAddMember(selectList)) {
|
||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "创建群成功 群改名成功 群拉人失败 ${selectList?.joinToString()}", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "创建群成功 群改名成功 群拉人失败: ${selectList?.joinToString()}", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!groupChangeAnnouncement(groupAnnouncement)) {
|
if (!groupChangeAnnouncement(groupAnnouncement)) {
|
||||||
@@ -278,11 +284,11 @@ object WeworkOperationImpl {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!groupAddMember(selectList, showMessageHistory == true)) {
|
if (!groupAddMember(selectList, showMessageHistory == true)) {
|
||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "进入房间成功 群改名成功 群拉人失败 ${selectList?.joinToString()}", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_ADD_MEMBER, "进入房间成功 群改名成功 群拉人失败: ${selectList?.joinToString()}", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!groupRemoveMember(removeList)) {
|
if (!groupRemoveMember(removeList)) {
|
||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_REMOVE_MEMBER, "进入房间成功 群改名成功 群拉人成功 群踢人失败 ${removeList?.joinToString()}", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_GROUP_REMOVE_MEMBER, "进入房间成功 群改名成功 群拉人成功 群踢人失败: ${removeList?.joinToString()}", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!groupChangeAnnouncement(newGroupAnnouncement)) {
|
if (!groupChangeAnnouncement(newGroupAnnouncement)) {
|
||||||
@@ -657,46 +663,21 @@ object WeworkOperationImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手机号添加好友
|
* 手机号添加好友或修改好友信息
|
||||||
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
|
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
|
||||||
* @param friend 待添加用户列表
|
* @param friend 待添加用户
|
||||||
*/
|
*/
|
||||||
fun addFriendByPhone(
|
fun addFriendByPhone(
|
||||||
message: WeworkMessageBean,
|
message: WeworkMessageBean,
|
||||||
friend: WeworkMessageBean.Friend
|
friend: WeworkMessageBean.Friend
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
goHome()
|
//如果已经是好友的可以传name修改好友信息
|
||||||
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
|
if (friend.phone == null && friend.name != null) {
|
||||||
if (list != null) {
|
if (getFriendInfo(friend.name)) {
|
||||||
val frontNode = AccessibilityUtil.findFrontNode(list)
|
if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话", "描述", "设置备注和描述", exact = true) != null) {
|
||||||
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
|
var markTv =
|
||||||
if (textViewList.size >= 2) {
|
AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
|
||||||
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
|
|
||||||
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
|
|
||||||
AccessibilityUtil.performClick(multiButton)
|
|
||||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
|
||||||
val list = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView).lastOrNull()
|
|
||||||
if (list != null) {
|
|
||||||
val button = AccessibilityUtil.findOneByText(list, "添加客户", "添加居民", "加微信", "加学员", exact = true)
|
|
||||||
if (button != null) {
|
|
||||||
AccessibilityUtil.performClick(button)
|
|
||||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
|
||||||
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
|
|
||||||
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
|
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
|
|
||||||
val bothUsedTv = AccessibilityUtil.findOneByText(getRoot(), "对方同时使用", "标签", "电话")
|
|
||||||
val bothUsedText = bothUsedTv?.text
|
|
||||||
if (bothUsedText != null && bothUsedText.contains("对方同时使用")) {
|
|
||||||
AccessibilityUtil.performClick(
|
|
||||||
AccessibilityUtil.findOnceByClazz(
|
|
||||||
AccessibilityUtil.findBackNode(bothUsedTv),
|
|
||||||
Views.ImageView
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话") != null) {
|
|
||||||
var markTv = AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
|
|
||||||
if (markTv == null) {
|
if (markTv == null) {
|
||||||
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true)
|
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true)
|
||||||
}
|
}
|
||||||
@@ -729,28 +710,60 @@ object WeworkOperationImpl {
|
|||||||
setFriendTags(friend.tagList)
|
setFriendTags(friend.tagList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//添加联系人
|
} else {
|
||||||
val imageView =
|
LogUtils.e("未找到标签")
|
||||||
AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到标签", startTime)
|
||||||
if (imageView != null) {
|
return false
|
||||||
val textViewList = AccessibilityUtil.findAllOnceByClazz(
|
}
|
||||||
imageView.parent,
|
LogUtils.d("修改好友信息成功: ${friend.name}")
|
||||||
Views.TextView
|
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
|
||||||
|
sleep(3000)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未找到用户: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_TARGET, "未找到用户: ${friend.name}", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//手机号添加好友
|
||||||
|
goHome()
|
||||||
|
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.RecyclerView, Views.ListView, Views.ViewGroup)
|
||||||
|
if (list != null) {
|
||||||
|
val frontNode = AccessibilityUtil.findFrontNode(list)
|
||||||
|
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
|
||||||
|
if (textViewList.size >= 2) {
|
||||||
|
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
|
||||||
|
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
|
||||||
|
AccessibilityUtil.performClick(multiButton)
|
||||||
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
|
val list = AccessibilityUtil.findAllByClazz(getRoot(), Views.ListView).lastOrNull()
|
||||||
|
if (list != null) {
|
||||||
|
val button = AccessibilityUtil.findOneByText(list, "添加客户", "添加居民", "加微信", "加学员", exact = true)
|
||||||
|
if (button != null) {
|
||||||
|
AccessibilityUtil.performClick(button)
|
||||||
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
|
AccessibilityUtil.findTextAndClick(getRoot(), "搜索手机号添加")
|
||||||
|
AccessibilityUtil.findTextInput(getRoot(), friend.phone.trim())
|
||||||
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "网络查找手机")) {
|
||||||
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
|
val bothUsedTv = AccessibilityUtil.findOneByTextRegex(getRoot(), "(对方同时使用.*?)|(标签)|(电话)|(描述)|(设置备注和描述)", timeout = 2000)
|
||||||
|
val bothUsedText = bothUsedTv?.text
|
||||||
|
if (bothUsedText != null && bothUsedText.contains("对方同时使用")) {
|
||||||
|
AccessibilityUtil.performClick(
|
||||||
|
AccessibilityUtil.findOnceByClazz(
|
||||||
|
AccessibilityUtil.findBackNode(bothUsedTv),
|
||||||
|
Views.ImageView
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val filter =
|
|
||||||
textViewList.filter { it.text != null && it.text.toString() != "微信" }
|
|
||||||
if (filter.isNotEmpty()) {
|
|
||||||
val tvNick = filter[0]
|
|
||||||
LogUtils.d("好友昵称或备注名: " + tvNick.text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (modifyFriendInfo(friend)) {
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
|
||||||
LogUtils.d("准备发送好友邀请: " + friend.phone)
|
LogUtils.d("准备发送好友邀请: ${friend.phone}")
|
||||||
if (!friend.leavingMsg.isNullOrEmpty()) {
|
if (!friend.leavingMsg.isNullOrEmpty()) {
|
||||||
AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg)
|
AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg)
|
||||||
}
|
}
|
||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
|
||||||
LogUtils.d("发送添加邀请成功: " + friend.phone)
|
LogUtils.d("发送添加邀请成功: ${friend.phone}")
|
||||||
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
|
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@@ -770,8 +783,8 @@ object WeworkOperationImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未找到标签")
|
LogUtils.e("修改用户信息失败: ${friend.phone}")
|
||||||
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到标签", startTime)
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "修改用户信息失败: ${friend.phone}", startTime)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -801,6 +814,119 @@ object WeworkOperationImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从外部群添加好友
|
||||||
|
* @see WeworkMessageBean.ADD_FRIEND_BY_PHONE
|
||||||
|
* @param groupName 外部群
|
||||||
|
* @param friend 待添加用户
|
||||||
|
*/
|
||||||
|
fun addFriendByGroup(
|
||||||
|
message: WeworkMessageBean,
|
||||||
|
groupName: String,
|
||||||
|
friend: WeworkMessageBean.Friend
|
||||||
|
): Boolean {
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
if (WeworkRoomUtil.intoRoom(groupName) && WeworkRoomUtil.intoGroupManager()) {
|
||||||
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "查看全部群成员")) {
|
||||||
|
val title = friend.name
|
||||||
|
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
||||||
|
if (list != null) {
|
||||||
|
val frontNode = AccessibilityUtil.findFrontNode(list)
|
||||||
|
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
|
||||||
|
.filter { it.text == null }
|
||||||
|
if (textViewList.size >= 2) {
|
||||||
|
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
|
||||||
|
AccessibilityUtil.performClick(searchButton)
|
||||||
|
val needTrim = title.contains(Constant.regTrimTitle)
|
||||||
|
val trimTitle = title.replace(Constant.regTrimTitle, "")
|
||||||
|
AccessibilityUtil.findTextInput(getRoot(), trimTitle)
|
||||||
|
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
||||||
|
//消息页搜索结果列表
|
||||||
|
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
||||||
|
val reverseRegexTitle = RegexHelper.reverseRegexTitle(trimTitle)
|
||||||
|
val regex1 = "^$reverseRegexTitle" + if (needTrim) ".*?" else "(-.*)?(…)?(\\(.*?\\))?$"
|
||||||
|
val regex2 = ".*?\\($reverseRegexTitle\\)$"
|
||||||
|
val regex = "($regex1)|($regex2)"
|
||||||
|
val matchSelect = AccessibilityUtil.findOneByTextRegex(
|
||||||
|
selectListView,
|
||||||
|
regex,
|
||||||
|
timeout = 2000,
|
||||||
|
root = false
|
||||||
|
)
|
||||||
|
if (selectListView != null && matchSelect != null) {
|
||||||
|
for (i in 0 until selectListView.childCount) {
|
||||||
|
val item = selectListView.getChild(i)
|
||||||
|
val searchResult = AccessibilityUtil.findOnceByTextRegex(item, regex)
|
||||||
|
//过滤异常好友
|
||||||
|
if (searchResult != null && searchResult.parent.childCount < 3) {
|
||||||
|
item.refresh()
|
||||||
|
val imageView =
|
||||||
|
AccessibilityUtil.findOneByClazz(item, Views.ImageView, root = false)
|
||||||
|
AccessibilityUtil.performClick(imageView)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modifyFriendInfo(friend)) {
|
||||||
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "添加为联系人")) {
|
||||||
|
LogUtils.d("准备发送好友邀请: ${friend.name}")
|
||||||
|
if (!friend.leavingMsg.isNullOrEmpty()) {
|
||||||
|
AccessibilityUtil.findTextInput(getRoot(), friend.leavingMsg)
|
||||||
|
}
|
||||||
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "发送添加邀请", "发送申请")) {
|
||||||
|
LogUtils.d("发送添加邀请成功: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
if (AccessibilityUtil.findOnceByText(getRoot(), "发消息", exact = true) != null) {
|
||||||
|
LogUtils.d("已经添加成功: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.SUCCESS, "", startTime)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
LogUtils.e("未找到发送邀请按钮")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到发送邀请按钮", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (AccessibilityUtil.findOnceByText(getRoot(), "发消息", exact = true) != null) {
|
||||||
|
LogUtils.e("已经添加联系人,请勿重复添加: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_REPEAT, "已经添加联系人,请勿重复添加 ${friend.name}", startTime)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未找到添加为联系人")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到添加为联系人", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogUtils.e("修改用户信息失败: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "修改用户信息失败: ${friend.name}", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未搜索到结果: ${friend.name}")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未搜索到结果: ${friend.name}", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未发现搜索按钮")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未发现搜索按钮", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未发现通讯录列表")
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未发现通讯录列表", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_BUTTON, "未找到查看全部群成员按钮 $groupName", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uploadCommandResult(message, ExecCallbackBean.ERROR_INTO_ROOM, "进入房间失败 $groupName", startTime)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 展示群信息
|
* 展示群信息
|
||||||
* @see WeworkMessageBean.SHOW_GROUP_INFO
|
* @see WeworkMessageBean.SHOW_GROUP_INFO
|
||||||
@@ -884,7 +1010,7 @@ object WeworkOperationImpl {
|
|||||||
if (matchSelect != null) {
|
if (matchSelect != null) {
|
||||||
LogUtils.d("找到搜索结果: $select")
|
LogUtils.d("找到搜索结果: $select")
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未搜索到结果")
|
LogUtils.e("未搜索到结果: $select")
|
||||||
}
|
}
|
||||||
sleep(Constant.POP_WINDOW_INTERVAL)
|
sleep(Constant.POP_WINDOW_INTERVAL)
|
||||||
}
|
}
|
||||||
@@ -902,18 +1028,18 @@ object WeworkOperationImpl {
|
|||||||
AccessibilityUtil.performClick(sendButton)
|
AccessibilityUtil.performClick(sendButton)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
LogUtils.e("未发现发送按钮: ")
|
LogUtils.e("未发现发送按钮")
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未发现确认按钮: ")
|
LogUtils.e("未发现确认按钮")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未发现搜索和多选按钮: ")
|
LogUtils.e("未发现搜索和多选按钮")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogUtils.e("未知错误: ")
|
LogUtils.e("未知错误")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,6 +1062,15 @@ object WeworkOperationImpl {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否达到当日建群上限
|
||||||
|
*/
|
||||||
|
private fun createGroupLimit(): Boolean {
|
||||||
|
val hasLimit =
|
||||||
|
AccessibilityUtil.findOneByText(getRoot(), "新建群聊功能暂时被限制", "未验证企业", timeout = 2000)
|
||||||
|
return hasLimit != null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改群名称
|
* 修改群名称
|
||||||
*/
|
*/
|
||||||
@@ -1047,8 +1182,8 @@ object WeworkOperationImpl {
|
|||||||
if (matchSelect != null) {
|
if (matchSelect != null) {
|
||||||
LogUtils.d("找到搜索结果: $select")
|
LogUtils.d("找到搜索结果: $select")
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未搜索到结果")
|
LogUtils.e("未搜索到结果: $select")
|
||||||
return false
|
if (Constant.groupStrict) return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showMessageHistory) {
|
if (showMessageHistory) {
|
||||||
@@ -1059,7 +1194,7 @@ object WeworkOperationImpl {
|
|||||||
if (confirmButton != null) {
|
if (confirmButton != null) {
|
||||||
AccessibilityUtil.performClick(confirmButton)
|
AccessibilityUtil.performClick(confirmButton)
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未发现确认按钮: ")
|
LogUtils.e("未发现确认按钮")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1138,8 +1273,8 @@ object WeworkOperationImpl {
|
|||||||
if (matchSelect != null) {
|
if (matchSelect != null) {
|
||||||
LogUtils.d("找到搜索结果: $select")
|
LogUtils.d("找到搜索结果: $select")
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未搜索到结果")
|
LogUtils.e("未搜索到结果: $select")
|
||||||
return false
|
if (Constant.groupStrict) return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val confirmButton =
|
val confirmButton =
|
||||||
@@ -1147,7 +1282,7 @@ object WeworkOperationImpl {
|
|||||||
if (confirmButton != null) {
|
if (confirmButton != null) {
|
||||||
AccessibilityUtil.performClick(confirmButton)
|
AccessibilityUtil.performClick(confirmButton)
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未发现移出按钮: ")
|
LogUtils.e("未发现移出按钮")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1193,10 +1328,10 @@ object WeworkOperationImpl {
|
|||||||
}
|
}
|
||||||
sleep(3000)
|
sleep(3000)
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("无法进行群公告发布: ")
|
LogUtils.e("无法进行群公告发布")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("无法进行群公告发布和编辑: ")
|
LogUtils.e("无法进行群公告发布和编辑")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1225,10 +1360,10 @@ object WeworkOperationImpl {
|
|||||||
if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) {
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "确定")) {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("无法进行群备注发布: ")
|
LogUtils.e("无法进行群备注发布")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("无法进行群备注修改: ")
|
LogUtils.e("无法进行群备注修改")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtils.e("未找到群公告按钮")
|
LogUtils.e("未找到群公告按钮")
|
||||||
@@ -1352,6 +1487,141 @@ object WeworkOperationImpl {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从通讯录查询好友信息
|
||||||
|
*/
|
||||||
|
private fun getFriendInfo(title: String): Boolean {
|
||||||
|
goHomeTab("通讯录")
|
||||||
|
val list = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
||||||
|
if (list != null) {
|
||||||
|
val frontNode = AccessibilityUtil.findFrontNode(list)
|
||||||
|
val textViewList = AccessibilityUtil.findAllOnceByClazz(frontNode, Views.TextView)
|
||||||
|
if (textViewList.size >= 2) {
|
||||||
|
val searchButton: AccessibilityNodeInfo = textViewList[textViewList.size - 2]
|
||||||
|
val multiButton: AccessibilityNodeInfo = textViewList[textViewList.size - 1]
|
||||||
|
AccessibilityUtil.performClick(searchButton)
|
||||||
|
val needTrim = title.contains(Constant.regTrimTitle)
|
||||||
|
val trimTitle = title.replace(Constant.regTrimTitle, "")
|
||||||
|
AccessibilityUtil.findTextInput(getRoot(), trimTitle)
|
||||||
|
sleep(Constant.CHANGE_PAGE_INTERVAL)
|
||||||
|
//消息页搜索结果列表
|
||||||
|
val selectListView = AccessibilityUtil.findOneByClazz(getRoot(), Views.ListView)
|
||||||
|
val reverseRegexTitle = RegexHelper.reverseRegexTitle(trimTitle)
|
||||||
|
val regex1 = "^$reverseRegexTitle" + if (needTrim) ".*?" else "(-.*)?(…)?(\\(.*?\\))?$"
|
||||||
|
val regex2 = ".*?\\($reverseRegexTitle\\)$"
|
||||||
|
val regex = "($regex1)|($regex2)"
|
||||||
|
val matchSelect = AccessibilityUtil.findOneByTextRegex(
|
||||||
|
selectListView,
|
||||||
|
regex,
|
||||||
|
timeout = 2000,
|
||||||
|
root = false
|
||||||
|
)
|
||||||
|
if (selectListView != null && matchSelect != null) {
|
||||||
|
for (i in 0 until selectListView.childCount) {
|
||||||
|
val item = selectListView.getChild(i)
|
||||||
|
val searchResult = AccessibilityUtil.findOnceByTextRegex(item, regex)
|
||||||
|
//过滤异常好友
|
||||||
|
if (searchResult != null && searchResult.parent.childCount < 3) {
|
||||||
|
item.refresh()
|
||||||
|
val imageView =
|
||||||
|
AccessibilityUtil.findOneByClazz(item, Views.ImageView, root = false)
|
||||||
|
AccessibilityUtil.performClick(imageView)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchSelect != null) {
|
||||||
|
LogUtils.d("找到搜索结果: $title")
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
LogUtils.e("未搜索到结果: $title")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改好友信息
|
||||||
|
*/
|
||||||
|
private fun modifyFriendInfo(friend: WeworkMessageBean.Friend): Boolean {
|
||||||
|
if (AccessibilityUtil.findOneByText(getRoot(), "标签", "电话", "描述", "设置备注和描述", exact = true) != null) {
|
||||||
|
var markTv = AccessibilityUtil.findOnceByText(getRoot(), "设置备注和描述", exact = true)
|
||||||
|
if (markTv == null) {
|
||||||
|
markTv = AccessibilityUtil.findOnceByText(getRoot(), "企业", exact = true)
|
||||||
|
}
|
||||||
|
if (markTv == null) {
|
||||||
|
markTv = AccessibilityUtil.findOnceByText(getRoot(), "描述", exact = true)
|
||||||
|
}
|
||||||
|
//设置备注
|
||||||
|
if (markTv != null && (friend.markName != null
|
||||||
|
|| friend.markCorp != null || friend.markExtra != null)
|
||||||
|
) {
|
||||||
|
AccessibilityUtil.performClick(markTv)
|
||||||
|
val etList =
|
||||||
|
AccessibilityUtil.findAllByClazz(getRoot(), Views.EditText, minSize = 2)
|
||||||
|
if (etList.size >= 5) {
|
||||||
|
//微信用户 备注/企业/电话/电话/描述
|
||||||
|
if (friend.markName != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[0], friend.markName)
|
||||||
|
}
|
||||||
|
if (friend.markCorp != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[1], friend.markCorp)
|
||||||
|
}
|
||||||
|
if (friend.markExtra != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[4], friend.markExtra)
|
||||||
|
}
|
||||||
|
} else if (etList.size == 2) {
|
||||||
|
//同企业内部用户 备注/描述
|
||||||
|
if (friend.markName != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[0], friend.markName)
|
||||||
|
}
|
||||||
|
if (friend.markExtra != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[1], friend.markExtra)
|
||||||
|
}
|
||||||
|
} else if (etList.size == 3) {
|
||||||
|
//外部企业用户 备注/电话/描述
|
||||||
|
if (friend.markName != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[0], friend.markName)
|
||||||
|
}
|
||||||
|
if (friend.markExtra != null) {
|
||||||
|
AccessibilityUtil.editTextInput(etList[2], friend.markExtra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AccessibilityUtil.findTextAndClick(getRoot(), "保存")
|
||||||
|
}
|
||||||
|
//设置标签
|
||||||
|
if (!friend.tagList.isNullOrEmpty()) {
|
||||||
|
if (AccessibilityUtil.findTextAndClick(getRoot(), "标签")) {
|
||||||
|
setFriendTags(friend.tagList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//添加联系人
|
||||||
|
val imageView =
|
||||||
|
AccessibilityUtil.findOneByClazz(getRoot(), Views.ImageView)
|
||||||
|
if (imageView != null) {
|
||||||
|
val textViewList = AccessibilityUtil.findAllOnceByClazz(
|
||||||
|
imageView.parent,
|
||||||
|
Views.TextView
|
||||||
|
)
|
||||||
|
val filter =
|
||||||
|
textViewList.filter { it.text != null && it.text.toString() != "微信" }
|
||||||
|
if (filter.isNotEmpty()) {
|
||||||
|
val tvNick = filter[0]
|
||||||
|
LogUtils.d("好友昵称或备注名: ${tvNick.text}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
if (AccessibilityUtil.findOnceByText(getRoot(), "无权查看") != null) {
|
||||||
|
LogUtils.e("无权查看该用户")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
LogUtils.e("未找到标签")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置好友标签
|
* 设置好友标签
|
||||||
*/
|
*/
|
||||||
@@ -1462,7 +1732,6 @@ object WeworkOperationImpl {
|
|||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
|
||||||
LogUtils.e(e)
|
LogUtils.e(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,8 +86,10 @@ class WeworkService : AccessibilityService() {
|
|||||||
Log.e(TAG, "链接建立")
|
Log.e(TAG, "链接建立")
|
||||||
val robotId = SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID, "")
|
val robotId = SPUtils.getInstance().getString(Constant.LISTEN_CHANNEL_ID, "")
|
||||||
val appVersion = SPUtils.getInstance().getString("appVersion", "")
|
val appVersion = SPUtils.getInstance().getString("appVersion", "")
|
||||||
val workVersion= SPUtils.getInstance().getString("workVersion", "")
|
val workVersion = SPUtils.getInstance().getString("workVersion", "")
|
||||||
log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion")
|
val deviceRooted = SPUtils.getInstance().getBoolean("deviceRooted", false)
|
||||||
|
val hook = SPUtils.getInstance().getBoolean("hook", false)
|
||||||
|
log("链接建立: $robotId appVersion: $appVersion workVersion: $workVersion deviceRooted: $deviceRooted hook: $hook")
|
||||||
LogUtils.i("设置自动跳转企业微信")
|
LogUtils.i("设置自动跳转企业微信")
|
||||||
sendBroadcast(true)
|
sendBroadcast(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package org.yameida.worktool.utils.envcheck;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class CheckHook {
|
||||||
|
|
||||||
|
public static boolean isHook(Context context) {
|
||||||
|
return isHookByPackageName(context) || isHookByStack(context) || isHookByJar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包名检测
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isHookByPackageName(Context context) {
|
||||||
|
boolean isHook = false;
|
||||||
|
PackageManager packageManager = context.getPackageManager();
|
||||||
|
List<ApplicationInfo> applicationInfoList = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||||
|
if (null == applicationInfoList) {
|
||||||
|
return isHook;
|
||||||
|
}
|
||||||
|
for (ApplicationInfo applicationInfo : applicationInfoList) {
|
||||||
|
if (applicationInfo.packageName.equals("de.robv.android.xposed.installer")) {
|
||||||
|
Log.wtf("HookDetection", "Xposed found on the system.");
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
if (applicationInfo.packageName.equals("com.saurik.substrate")) {
|
||||||
|
isHook = true;
|
||||||
|
Log.wtf("HookDetection", "Substrate found on the system.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isHookByStack(Context context) {
|
||||||
|
boolean isHook = false;
|
||||||
|
try {
|
||||||
|
throw new Exception("blah");
|
||||||
|
} catch (Exception e) {
|
||||||
|
int zygoteInitCallCount = 0;
|
||||||
|
for (StackTraceElement stackTraceElement : e.getStackTrace()) {
|
||||||
|
if (stackTraceElement.getClassName().equals("com.android.internal.os.ZygoteInit")) {
|
||||||
|
zygoteInitCallCount++;
|
||||||
|
if (zygoteInitCallCount == 2) {
|
||||||
|
Log.wtf("HookDetection", "Substrate is active on the device.");
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stackTraceElement.getClassName().equals("com.saurik.substrate.MS$2") &&
|
||||||
|
stackTraceElement.getMethodName().equals("invoked")) {
|
||||||
|
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Substrate.");
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
|
||||||
|
stackTraceElement.getMethodName().equals("main")) {
|
||||||
|
Log.wtf("HookDetection", "Xposed is active on the device.");
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
if (stackTraceElement.getClassName().equals("de.robv.android.xposed.XposedBridge") &&
|
||||||
|
stackTraceElement.getMethodName().equals("handleHookedMethod")) {
|
||||||
|
Log.wtf("HookDetection", "A method on the stack trace has been hooked using Xposed.");
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isHookByJar() {
|
||||||
|
boolean isHook = false;
|
||||||
|
try {
|
||||||
|
Set<String> libraries = new HashSet();
|
||||||
|
String mapsFilename = "/proc/" + android.os.Process.myPid() + "/maps";
|
||||||
|
BufferedReader reader = new BufferedReader(new FileReader(mapsFilename));
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.endsWith(".so") || line.endsWith(".jar")) {
|
||||||
|
int n = line.lastIndexOf(" ");
|
||||||
|
libraries.add(line.substring(n + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String library : libraries) {
|
||||||
|
if (library.contains("com.saurik.substrate")) {
|
||||||
|
Log.wtf("HookDetection", "Substrate shared object found: " + library);
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
if (library.contains("XposedBridge.jar")) {
|
||||||
|
Log.wtf("HookDetection", "Xposed JAR found: " + library);
|
||||||
|
isHook = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.wtf("HookDetection", e.toString());
|
||||||
|
}
|
||||||
|
return isHook;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
package org.yameida.worktool.utils.envcheck;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class CheckRoot {
|
||||||
|
private static String LOG_TAG = CheckRoot.class.getName();
|
||||||
|
|
||||||
|
public static boolean isDeviceRooted() {
|
||||||
|
if (checkDeviceDebuggable()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkSuperuserApk()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkRootPathSU()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkRootWhichSU()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkAccessRootData()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkDeviceDebuggable() {
|
||||||
|
String buildTags = android.os.Build.TAGS;
|
||||||
|
if (buildTags != null && buildTags.contains("test-keys")) {
|
||||||
|
Log.i(LOG_TAG, "buildTags=" + buildTags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkSuperuserApk() {
|
||||||
|
try {
|
||||||
|
File file = new File("/system/app/Superuser.apk");
|
||||||
|
if (file.exists()) {
|
||||||
|
Log.i(LOG_TAG, "/system/app/Superuser.apk exist");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkRootPathSU() {
|
||||||
|
File f = null;
|
||||||
|
final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < kSuSearchPaths.length; i++) {
|
||||||
|
f = new File(kSuSearchPaths[i] + "su");
|
||||||
|
if (f != null && f.exists()) {
|
||||||
|
Log.i(LOG_TAG, "find su in : " + kSuSearchPaths[i]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkRootWhichSU() {
|
||||||
|
String[] strCmd = new String[]{"/system/xbin/which", "su"};
|
||||||
|
ArrayList<String> execResult = executeCommand(strCmd);
|
||||||
|
if (execResult != null) {
|
||||||
|
Log.i(LOG_TAG, "execResult=" + execResult.toString());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.i(LOG_TAG, "execResult=null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> executeCommand(String[] shellCmd) {
|
||||||
|
String line = null;
|
||||||
|
ArrayList<String> fullResponse = new ArrayList<String>();
|
||||||
|
Process localProcess = null;
|
||||||
|
try {
|
||||||
|
Log.i(LOG_TAG, "to shell exec which for find su :");
|
||||||
|
localProcess = Runtime.getRuntime().exec(shellCmd);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
|
||||||
|
try {
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
Log.i(LOG_TAG, "–> Line received: " + line);
|
||||||
|
fullResponse.add(line);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Log.i(LOG_TAG, "–> Full response was: " + fullResponse);
|
||||||
|
return fullResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized boolean checkGetRootAuth() {
|
||||||
|
Process process = null;
|
||||||
|
DataOutputStream os = null;
|
||||||
|
try {
|
||||||
|
Log.i(LOG_TAG, "to exec su");
|
||||||
|
process = Runtime.getRuntime().exec("su");
|
||||||
|
os = new DataOutputStream(process.getOutputStream());
|
||||||
|
os.writeBytes("exit\n");
|
||||||
|
os.flush();
|
||||||
|
int exitValue = process.waitFor();
|
||||||
|
Log.i(LOG_TAG, "exitValue=" + exitValue);
|
||||||
|
if (exitValue == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
|
||||||
|
+ e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (os != null) {
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
process.destroy();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized boolean checkBusybox() {
|
||||||
|
try {
|
||||||
|
Log.i(LOG_TAG, "to exec busybox df");
|
||||||
|
String[] strCmd = new String[]{"busybox", "df"};
|
||||||
|
ArrayList<String> execResult = executeCommand(strCmd);
|
||||||
|
if (execResult != null) {
|
||||||
|
Log.i(LOG_TAG, "execResult=" + execResult.toString());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.i(LOG_TAG, "execResult=null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
|
||||||
|
+ e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized boolean checkAccessRootData() {
|
||||||
|
try {
|
||||||
|
Log.i(LOG_TAG, "to write /data");
|
||||||
|
String fileContent = "test_ok";
|
||||||
|
Boolean writeFlag = writeFile("/data/su_test", fileContent);
|
||||||
|
if (writeFlag) {
|
||||||
|
Log.i(LOG_TAG, "write ok");
|
||||||
|
} else {
|
||||||
|
Log.i(LOG_TAG, "write failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(LOG_TAG, "to read /data");
|
||||||
|
String strRead = readFile("/data/su_test");
|
||||||
|
Log.i(LOG_TAG, "strRead=" + strRead);
|
||||||
|
if (fileContent.equals(strRead)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
|
||||||
|
+ e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean writeFile(String fileName, String message) {
|
||||||
|
try {
|
||||||
|
FileOutputStream fout = new FileOutputStream(fileName);
|
||||||
|
byte[] bytes = message.getBytes();
|
||||||
|
fout.write(bytes);
|
||||||
|
fout.close();
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readFile(String fileName) {
|
||||||
|
File file = new File(fileName);
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
byte[] bytes = new byte[1024];
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
int len;
|
||||||
|
while ((len = fis.read(bytes)) > 0) {
|
||||||
|
bos.write(bytes, 0, len);
|
||||||
|
}
|
||||||
|
String result = new String(bos.toByteArray());
|
||||||
|
Log.i(LOG_TAG, result);
|
||||||
|
return result;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user