Android Studio 新版Logcat 从惊艳到放弃
AS总算更新了这个logcat了,原来的logcat真是使用起来贼难受,动不动过滤就失效,或者日志不打印,新版的logcat初步使用下来,那是贼舒服,先上一张界面图
- 一眼看去,可以看到主要改变有三点:
一个是多窗口支持,可以同时打开多个logcat窗口,这对于分析繁多复杂的日志十分有帮助,并且每个窗口又可以复制出多个子窗口,对于分析对比当前窗口不同位置的日志是非常有帮助的;
一个是过滤方式,改成了类似于Github里的搜索过滤语法,可以多个语法组合进行过滤,自定义更强更灵活了;
一个是打印格式,优化了多行日志的打印,日志排版也更清新,也支持了颜色高亮。并且打印信息也可以自定义是否显示
接下来就详细介绍一下这个过滤语法
过滤语法
package:mine 默认语法,官方原版解释:PIDs for the local app project,不太理解这个解释,不过根据使用,应该就是当前logcat窗口所在项目的log
package:my-package-ID 也就是根据包名过滤
tag:my-tag 根据tag进行过滤
level:ERROR 根据日志等级进行过滤
-tag:exclude-this-tag 在前面加了个'-',排除指定的TAG
tag~:regex-tag 在后面加了个'~',支持正则过滤
-tag~:exclude-this-regex-tag 这个自然就是支持正则过滤排除指定TAG
-package:exclude-this-package 同上
package~:regex-package 同上
-package~:exclude-this-regex-package 同上
总结
更新的内容挺好的,不过该有的问题依然存在,还是时不时的不显示logcat,需要重新插拔一下手机才行
后续
放弃了,日志更新不及时,删除也不及时,有些日志还没捕获到,比原来的logcat还垃圾,基本问题没解决,上层功能做的再花俏有什么卵用
退回旧版
Settings-> Experimental -> [x] Enable new Logcat tool window 取消选择 -> 重启 Android Studio
Android P以上隐藏API(非SDK接口)反射限制解决方案
相关限制说明参考:针对非 SDK 接口的限制
FreeReflection github
- Java层双重反射 (Android P)
- ClassLoader置空 (Android P+)(将被修复)
- native层通过偏移获取关键结构体然后修改其中的检查Flag (Android P+)
RestrictionBypass github
- Java层双重反射 (Android P)
- native层双重反射 (通过AttachCurrentThread隐藏未知调用者) (Android Q+) (将被修复)
AndroidHiddenApiBypass github
- 借用Unsafe重写反射代码(Android P+)
总结
目前本人仅使用了FreeReflection,不过由于时常报native层闪退异常已弃用(猜测是ClassLoader置空方案造成的,双重反射应该只在Android P生效,native层修改Flag未用到),其他暂未使用,不做评价,不过理论上比较看好AndroidHiddenApiBypass,纯Java,兼容性肯定是最好的,由于Unsafe涉及到native层访问,恐怕不太好封,限制访问部分内存区域?还是直接限制访问反射相关结构体?RestrictionBypass的native层双重反射应该也容易被检测修复
Android 10以上无法隐藏应用图标问题探究及解决方案
Android 10之前的隐藏方式:
java
con.getPackageManager().setComponentEnabledSetting(new ComponentName(con,activityAliasName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
Android 10之后,该隐藏图标方式失效,隐藏成功后依然会在桌面上保留其图标,点击后会跳转到应用详情页。具体查看官方API更新说明:getActivityList 。
那这就很难受了,在我看来,目前设计的并不太合理,能不能隐藏应该由用户决定,而不是强制不允许隐藏,不过目前官方也设置了控制开关,可以修改系统设置来去掉该限制,只不过需要adb或能修改系统设置的APP或ROOT权限才能修改。接下来就是具体分析了。
具体分析
- getActivityList
java
//frameworks/base/core/java/android/content/pm/LauncherApps.java
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
packageName, user), user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
- getLauncherActivities
java
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
@Override
public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
String callingPackage, String packageName, UserHandle user) throws RemoteException {
//查询包含ACTION_MAIN和CATEGORY_LAUNCHER的activity
ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
queryActivitiesForUser(callingPackage,
new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(packageName),
user);
//检测系统设置(系统设置控制开关控制是否走新版限制逻辑)
if (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
return launcherActivities;
}
//没有找到入口activity
if (launcherActivities == null) {
// Cannot access profile, so we don't even return any hidden apps.
return null;
}
final int callingUid = injectBinderCallingUid();
final long ident = injectClearCallingIdentity();
try {
//托管配置文件可以隐藏APP
if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
// Managed profile should not show hidden apps
return launcherActivities;
}
//设备管理员可以隐藏APP
if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {
// Device owner devices should not show hidden apps
return launcherActivities;
}
final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
launcherActivities.getList());
if (packageName != null) {
// If this hidden app should not be shown, return the original list.
// Otherwise, inject hidden activity that forwards user to app details page.
//可能是如果大于0,表示有有效的启动入口,则不用注入隐藏的APP详情页入口
if (result.size() > 0) {
return launcherActivities;
}
final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(
packageName, /* flags= */ 0, callingUid, user.getIdentifier());
//根据APP信息进行判断是否注入隐藏APP详情页入口
if (shouldShowSyntheticActivity(user, appInfo)) {
LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
callingUid, user);
if (info != null) {
result.add(info);
}
}
return new ParceledListSlice<>(result);
}
final HashSet<String> visiblePackages = new HashSet<>();
for (LauncherActivityInfoInternal info : result) {
visiblePackages.add(info.getActivityInfo().packageName);
}
final List<ApplicationInfo> installedPackages =
mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
user.getIdentifier(), callingUid);
for (ApplicationInfo applicationInfo : installedPackages) {
if (!visiblePackages.contains(applicationInfo.packageName)) {
if (!shouldShowSyntheticActivity(user, applicationInfo)) {
continue;
}
LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
applicationInfo.packageName, callingUid, user);
if (info != null) {
result.add(info);
}
}
}
return new ParceledListSlice<>(result);
} finally {
injectRestoreCallingIdentity(ident);
}
}
- shouldShowSyntheticActivity
java
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
private boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) {
//没有app信息,是系统APP或者更新的系统APP,则不添加隐藏详情页入口
if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
return false;
}
//是管理员APP也不用添加该入口
if (isManagedProfileAdmin(user, appInfo.packageName)) {
return false;
}
final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);
if (pkg == null) {
// Should not happen, but we shouldn't be failing if it does
return false;
}
// If app does not have any default enabled launcher activity or any permissions,
// the app can legitimately have no icon so we do not show the synthetic activity.
return requestsPermissions(pkg) && hasDefaultEnableLauncherActivity(
appInfo.packageName);
}
根据以上源码,可以发现该机制有个全局设置开关,并且有多种情况可以不受该机制限制。因此总结出如下解决方案:
- 若是你的APP有adb权限或ROOT权限,可以直接修改这个全局开关,修改命令如下:
bash
adb shell settings put global show_hidden_icon_apps_enabled 0
- 或者你的APP有设备管理员权限
- 或者你的APP是系统APP
- 或者你的APP没有任何默认启动入口
- 或者你的APP没有请求任何权限
- 或者你的APP涉及到Managed profile(这个具体不太清楚怎么用)
- 或者你的APP是Xposed模块,可以直接注入系统服务拦截相关API,或者注入可以修改系统设置的APP(比如:设置)直接修改这个开关
- 或者你是系统源码开发者,可以直接修改该处代码即可
- 其它更多还未探索出的方式(比如利用某些逻辑漏洞)...
参考
Android Studio gradle 没依赖成功 但不报错 只有调用时会报红
可以单独运行一下依赖任务,就会出现错误信息了
Empty SMPTE 2094-40 data 报错问题
搜索源码定位到打印日志位置:SharedMetadata#151
临时解决方案
借用AS 新版Logcat 过滤语法进行过滤即可,过滤语法如下
-tag:gralloc4
即排除掉tag为gralloc4的日志
【远程USB】VirtualHere 指定IP(Android 远程开发优雅连接调试设备)
一般我们Android远程调试一般直接是adb connect ip:port,但是呢,这个很不方便,你还需要配置wifi USB调试,但是远程USB就不一样了,连接远程设备就像连接本地设备一样操作,直接adb device就可以看见设备了,不用什么adb connect和wifi USB调试。
而远程USB工具,当然就推荐VirtualHere了,VirtualHere使用起来真香,局域网内,直接安装运行就行了,不需要任何操作(当然了,试用版同一时间仅限一台设备,不过对于个人使用,这足够了,需要时再切换即可,反正又不绑定设备),但互联网的话就不能这么操作了,毕竟互联网这么多IP,它怎么知道是哪个对不对,所以需要我们手动指定IP,正文开始...
服务端若是在服务器运行的,那就不用动了,若是在本地运行的话,就需要做内网穿透了,这里推荐FRP,简单方便开源,具体这里就不展开讲了;
然后在客户端中,在USB Hubs上右键菜单选择Specify Hubs...
然后点击add添加,在新窗口输入你的ip和port即可
之后等待USB hubs下出现新设备即可
若是出现问题,可以通过
bash
telnet ip port
命令,看看是否可以正常连接,若不能正常连接,检查防火墙是否拦截了
Frida使用技巧
命令行
Frida命令
- 指定IP和Port:-H 127.0.0.1:27042
- 强制重启应用:-f
- 重启后直接运行应用:--no-pause
- 注入指定包名(必须):com.xxxxx.xxx
- 注入指定进程ID(与上面二选一):pid
- 调试js代码:--debug --runtime=v8
具体参考Here,其中代码方式开启调试测试不太管用,具体待分析
- 指定要加载的js文件路径(必须):-l path_to_js
接入Google Admob
官方Demo
填坑
- 1.The ad request was successful, but no ad was returned due to lack of ad inventory
参考Here
1.未关联应用商店应用,在应用设置里添加应用ID,也就是包名,且在google play,或者其支持的应用商店上架
2.填充率低,采用中介模式,具体参考Here
2.1.查看国内支持的广告源,参考Here
- 2.需要上车才能显示Ad
参考
App支付功能实现及踩坑
支付宝支付实现
1.正规支付实现
前提条件:
1. 企业或个体工商户可申请,且账号通过支付宝实名认证审核
2. 需提供真实有效的营业执照,且支付宝账户名称需与营业执照主体一致;
3. 提供APP名称或产品说明文档,开发者与支付宝账户名称不一致需提供开发合作协议。
(您可以提供产品介绍文档或者手机demo .apk .doc .docx .pdf格式,内容要求包含产品说明、接口使用场景、资费说明等(文件大小不超过5M)如果已经在appstore、googleplay、豌豆荚上架,可不提供文档demo,直接提供应用名称)
现在企业和个体工商户的办理,某宝应该都可以搜到,我咨询了卖家,说是需要对公账户,
对公账户具体意思呢,卖家就说是上面第二个条件.
还是不太明白,我又上网百度了下:
看来百度,也百度不到什么,试试用谷歌吧
终于让我找到了,看来还是谷歌大法好,百度真是越做越垃圾了!
看来要对公账号,就等于要申请一个公司啊,那还是算了,想起那一大堆文件和流程就害怕!只能等待以后有一定资金了直接某宝找人代办吧
2.非正规实现
1.还是使用个人支付账号,然后服务端后台(或个人电脑端)不断监听付款记录,当匹配到特定备注的支付记录时就进行相应处理!
2.使用第三方代理支付,就是把支付交给已经得到支付宝认证的第三方支付商,这样的话的确是最方便的,不做任何处理就可以得到支付宝的正规支付功能,缺点就是肯定会收手续费了,当然收手续费很正常,毕竟人家提供了服务嘛,另一个缺点就是这个服务商是否可信,毕竟人家后台怎么处理你根本不知道,同样的这种服务商都一个共同的最大的缺点---不稳定,就是一个开发者的App支付若是被投诉,肯定会影响到这个服务商,在支付宝那边就相当于这个服务商被投诉,若投诉多了,那这个服务商也就挂了!
微信支付实现
1.正规实现
同样需要企业认证,其开发者认证也不是很容易
仅认证个开发者就要300元,还仅是一年的!
还是需要企业,算了,放弃!
2.非正规实现
还在研究中...
参考
1.支付宝收款流程
DcseTBS获取设备ID原理
这款工具的就不说了,这不是文章的重点,今天我们要研究一下他的获取设备唯一ID的原理。
其实它获取设备唯一ID的方法跟别人的没啥区别,
实现代码:
TelephoneManager tm=TelephoneManager.getSystemService(Context.TELEPHONE_SERVICE);
tm.getDeviceId();
代码非常的简单,所以破解它也非常的简单,用xposed模块hook即可修改,或者直接反编译修改也可以。
但重点不在这里,而是破解之后的情况,它的每个设备ID不充值是仅能使用一天的,所以我破解它的方法是让他每次返回随机设备ID,这样不就可以破解了,可是事实不是这样的,并没有破解(其实后来才知道已经破解了),每次打开显示错误,真是让我纠结了半天,以为他是用了反射或者通过其他方式获取了设备ID,或者把设备ID存储在了内存卡,通过我的验证,最后发现都没有,这就让我懵逼了,难道使用了我不知到的方法,就在我准备放弃的时候,我想到了一个被我忽略的地方,那就是我的网络IP地址,并且发现有时候错误的信息并不一样,到最后我终于发现了问题的关键点
原来是当它获取到新的设备ID时,并不会第一时间就出现试用信息,而是出现错误信息,并让你重启,正常在没破解的情况下,在第二次进入时就会出现试用信息了,但是当我们把它破解之后,每次获取的都是随机的设备ID,也就是当第二次重启时,又获取的是新的设备ID,这是又需要再重启一次,可是再获取一次又是新的设备ID,这样造成的结果就是永远不会出现试用信息!
既然明白了问题,那破解也就简单了,只要在每次过了试用期后,再返回一个新的设备ID即可!
真是一个非常巧妙地防止被破解的方法!
破解后的截图
不要频繁重置ID,一天一次即可,设置了新的ID后直接点击"明天再提示",就会等到明天这个时候会自动提示更换新ID,这时候再设置一个新ID即可。
破解后的APK
看有这多人要,我就直接发出来吧!
下载地址:https://download.csdn.net/download/qq_26914291/10383983
【注】该Apk仅供研究测试用,用作它用造成的一切损失一概与本人无关 !
总结
1.破解遇到问题,一定要仔细观察错误的信息,或许原因就藏在那些之中!
2.其实往往看似复杂的东西其实很简单!
代码混淆
初级混淆
使用proGuard进行代码混淆
1.参考教程:Android开发者指南(9) ------ ProGuard
2.混淆模板:Android proguard-rules.pro 混淆模板
3.[Android 代码混淆 选项说明(https://blog.csdn.net/jjwwmlp456/article/details/44977721)
几个可以增加混淆强度的命令:
#将所有类放到跟目录下
-flattenpackagehierarchy
#使用唯一的类成员名称
-useuniqueclassmembernames
几个可以增加混淆强度的字典
1.https://github.com/ihbing/AndroidObfuseDictionary
几个使用时需要注意的命令:
1.-assumenosideeffects使用时一定不要加-dontoptimize才起作用
# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
[注]
1.eclipse只有将项目导出时才会进行混淆
2.1 proguard.config=proguard-project.txt//采用相对路径配置,直接读取项目根目录下proguard-project.txt文件
2.2 proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt//采用绝对路径
使用proGuard-GUI进行手动混淆
使用AndResGuard进行资源混淆
参考教程:AndResGuard集成笔记
增加7z压缩
参考教程:Android项目集成AndResGuard资源混淆两种方式
参考教程:https://github.com/shwenzhang/AndResGuard/blob/master/doc/how_to_work.zh-cn.md
注意点
1.如何打开Gradle中的Task任务?
进阶混淆
使用StringFog对字符串Base64加密
参考教程:https://github.com/MegatronKing/StringFog
将字符串混淆成byte字节码
如下图
参考教程:https://github.com/ihbing/obfuseSmaliText
高阶混淆
修改proGuard工具的混淆字符
[注]AndroidStudio的自身proGuard混淆工具目录D:\Program Files\Android\Android Studio\gradle\m2repository\net\sf\proguard\proguard-base\
或D:\Program Files\Android\Android Studio\gradle\m2repository\net\proguard-base\
用无效代码对抗jadx&jd-gui
//添加无效代码,对抗jadx & jd-gui
boolean qwerty21345hjdnjd = false;
while (qwerty21345hjdnjd) {
switch (1) {
case 1:
while (qwerty21345hjdnjd) {
try {
Throwable throwable=new Throwable();
Throwable cause = throwable.getCause();
} catch (NullPointerException e) {
} finally {
}
}
break;
}
}
参考
4.apk在线反混淆