sleepasandroid
㈠ 安卓 13侧载 App 权限将被进一步限制
安卓 13侧载 App 权限将被进一步限制
安卓 13侧载 App 权限将被进一步限制,Android 13 Beta 1 更新,到了下半年,我们就能陆续看到第一批更新 Android 13 系统的设备了。安卓 13侧载 App 权限将被进一步限制。
安卓 13侧载 App 权限将被进一步限制1
据 Android Police 报道,Android 的无障碍服务旨在为残障用户提供帮助,但这套工具非常强大,以至于其他应用程序经常使用它来启用引人注目的功能。不幸的是,无障碍服务通常也是恶意软件的门户,以此来控制电话或获取个人数据。在 Android 13 中,谷歌正在进一步打击对无障碍服务的访问,使侧载应用更难获得访问权限。
Android 13 对侧载 App 引入了新的限制,阻止用户授予他们使用无障碍服务的权利。鉴于许多网络钓鱼和恶意软件攻击是通过诱骗用户从应用商店外部安装 APK 来进行的,这可能会使不良行为者更难劫持毫无戒心的用户手机。
不过,谷歌并没有完全阻止侧载应用程序使用无障碍服务。一旦进入对话框,说明相关应用程序的辅助功能服务受到限制,你可以通过“允许受限设置”菜单条目在右上角的应用程序信息屏幕下激活访问权限,因此,如果你是有兴趣使用合法应用程序增强手机的高级用户,仍然可以这样做。
这似乎是一个漏洞,恶意应用程序可以通过指示用户启用受限设置来规避。因此,谷歌仍有可能在稳定的 Android 13 上线之前改变这种行为。
新规则对用户在 Play 商店中也有的应用程序有影响。当我们从 APK Mirror 侧载旧版本的 Sleep as Android 时,它使用辅助功能服务来防止在尝试关闭闹钟时关闭手机,无法启用辅助功能服务,即使将其更新到通过 Play 商店提供的最新版本。
虽然用户仍然可以使用前面描述的解决方法访问 Android 13 Beta 1 中的辅助功能服务,但对于那些在 Play 商店推出之前将应用程序侧载到最新状态的用户来说,这是一个额外的步骤。
还需要注意的是,谷歌只限制侧载应用程序。如果你使用 F-Droid 或亚马逊应用商店等替代应用分发平台,将不会遇到无障碍服务限制,谷歌可能会认为应用商店中的应用至少在一定程度上是经过筛选的。
同时,默认情况下,在 Google Play 商店中分发的应用程序根本无法使用无障碍服务,除非它们是专门为无障碍而创建的。当其他应用程序开发人员可以通过漫长的过程向谷歌证明他们的应用通过无障碍服务得到了极大的增强时,他们仍然可以要求豁免,但一般来说,谷歌强烈反对使用无障碍服务。
事实上,通话记录应用程序是最新感受到这些限制的应用程序,谷歌不再允许它们使用无障碍服务来记录电话通话。
安卓 13侧载 App 权限将被进一步限制2
到了下半年,我们就能陆续看到第一批更新 Android 13 系统的设备了。
距离去年 Android 12 Beta 版发布已经过去接近一年,无论此刻你的手机是否已经收到基于 Android 12 而来的更新推送,Android 13 的更新都已经进入了最新的 Beta 测试阶段,从早先公布的 Android 更新时间线来看,距离进入各种系统功能都趋于稳定的阶段也仅一步之遥。
虽然近两年 Android Beta 测试计划时间表都有较多的提前,但随着时间进入五月,新一年的 Google I/O 照例也即将于 5 月 11 日正式开幕,届时也会公布所有 Android 13 中将会正式加入的重要功能。
但在那之前,我们仍然可以通过现有的 Android 13 Beta 1 更新,先来大致了解到 Android 13 中有哪些值得期待的好料。
应用独立语言设置
这个功能对于 iOS 用户来讲肯定不会陌生,但 Android 却是一直到第十三个大版本更新才终于等来。这一功能能让应用独立于系统所设置的语言之外设置另一种显示语言(当然前提仍需要应用本身提供了多语言的支持),这个功能往往会在使用一些语言类应用时相当方便。
在 Android 13 中,在系统设置中的“语言&输入”菜单打开之后,就可以看到新的独立应用语言设置选项。
虽然在最新的 Android 13 Beta 1 中,这个设置选项被暂时隐藏了起来,但预计出现在最终的 Android 13 正式版更新中并不会有太多的问题。
系统主题更进一步
Android 12 之所以被很多人认为是自 Android 5.0 以来改进最大的一次更新,很大程度上要归功于 Google 新引入的 Material You 主题取色系统。简而言之,就是一套可以跟随手机的桌面壁纸更换,来一次性自动更换手机的主题色、图标、桌面小组件,乃至第三方应用内页面设计都能一同被重新加载。
但也正是由于 Android 12 中在主题系统部分大刀阔斧地改进太多,甚至 Android 官方承诺的一些实现效果,最终都未能在 Android 12 正式版中出现,因此也有了“Android 12 分两年更新完”的说法。
事实也确实如此,在 Material You 主题应用实现的部分,一些 Android 12 中已经“PPT 首发”的内容,直到最近的 Android 13 Beta 1 才算是得到了最终的更新:例如下面的 Android 13 媒体卡片新增动画效果。
Material You 取色效果给了更多选择:在 Android 13 Beta 1 中,取色选择从 Android 12 时期的四个选择,直接升级为了壁纸提取颜色 + 基本颜色共计 32 种颜色组合,两种不同的取色算法能从壁纸颜色中分别能生成单色、双色以及三色的可选组合。让主题样式也有了更多可选项。
Material You 图标取色同样是重要的组成部分之一:在开启主题图标选项之后,桌面上适配过的应用图标也都可以跟随手机主题色设置来改变样式,但在 Android 12 阶段,这一功能的适配进度比较缓慢,过去一年之后也仍只有少数第三方 App 针对这一功能进行了适配。
为了解决这一问题,在 Android 13 中引入了新的主题图标 API,应用开发者只需在开发 App 时提供一个单色的应用图标,就能在 Android 13 中应用到动态图标样式功能,大幅降低了开发者适配新功能的开发成本。另外 Google 也已经在与其他 Android 手机厂商合作,将这一功能推向更多 Android 第三方操作系统上。
隐私安全仍是重点
隐私权限控制同样是 Android 12 就已经重点关注的改进方向之一,而且在这一点上原生 Android 直接对系统底层的改动对于其他第三方 Android 系统来讲同样有着重要的启示意义。例如在 Android 12 中首次出现了可以一键全局禁用摄像头/麦克风的控制中心卡片设计。
关掉这些开关之后,禁用摄像头甚至无法使用 Pixel 手机自带的人脸解锁,而禁用麦克风权限则无法通话;而在 Android 13 中,这些针对系统权限的控制收紧的趋势也有更多进一步改进,全局权限选项中还新增了新的地理位置一项,并将相机/麦克风权限一并整合成新的“隐私控制”卡片。
在设置中,Android 13 还将过去散落在各处的隐私相关设置进一步整合,成了新的“隐私信息中心”,供用户直接检查最多过去一周各项权限的使用情况,包括系统应用在内的权限使用都可以在这里看到详细到分钟的权限使用记录。
Android 13 中还引入了独立的照片选择器概念:比如你想在某“小而美”的聊天软件中给朋友分享一组照片,但却又不想授予软件访问你手机中所有照片的权限,这时 Android 13 新加入的照片选择器就可以手动选出你需要分享的照片内容,并仅将你所选中的照片的访问权限授予聊天应用,最大限度降低隐私泄露的风险。
大屏幕体验& 智能家居
在 Android 12 更新发布之后,Android 紧随其后启动了另一个特殊的系统测试计划 —— Android 12L;这一目的在于优化大屏幕/折叠屏的更新被赋予了独立系统代号,甚至在很大程度上成为了一个与早期 Android 13 测试计划并行的开发项目。
在 Android 13 中,针对折叠屏不同场景使用/不同屏幕尺寸的显示模式加入了更多深层的改进,一边在多屏幕切换之间获得更好的显示效果;例如屏幕 90 度展开状态的'“桌面模式”。
目前 Android 13 中,面向宅家场景中需求经常被提到的智能家居也是新功能加入的重要方向之一,例如 Android 13 Beta 1 中已经加入“锁屏控制智能设备”的选项,这一更新允许适配过的智能家居服务(例如米家、Google Home 等),无需解锁手机,在锁屏状态下即可进行基础的智能家居操作。
从目前现有信息来看,Android 13 中还将加入一个“媒体点击转移”(Media Tap to Transfer)的功能;虽然目前这一功能实际信息相对于其他已经出现的功能来讲还很少,但这会是一个类似苹果 AirDrop、华为分享功能的 Android 原生媒体投射功能,但检测到同一局域网下有平板/笔电等设备正在使用中,或是使用 UWB 近场通信,靠近智能音箱即可将音乐投射到上面播放。
同样属于尚未发布,但已经在相关代码中已经有所提到的,还有 Android 13 中新增的“Hub 模式”可以让多个用户在同一台 Android 13 设备之上共用一套数据,同时还能保留多个用户之间的个人信息。
这一功能明显并非是为了个人使用的平板电脑而准备,但三星也曾在今年的 CES 期间展示过搭载 Android 系统的“智能家居中枢”:本质上其实是一款 Android 系统平板,但除了搭配充电底座之外,也加入了更多针对家居使用场景的软件改进。
这也侧面印证了此前关于 Google 会发布搭载 Android 系统的智能显示屏的传闻,当然这些未必会是与 Android 13 一同出现在五月即将到来的 Google I/O 大会中的内容。但仍然值得期待。
安卓 13侧载 App 权限将被进一步限制3
谷歌将在下个月举行年度开发者大会,届时可能会对Android 13的更新内容做全面的介绍。至于正式版,预计要等到下半年或更晚的时候了。
据了解,Android 13首个Beta版本最主要的新功能,是此前已经出现在Android 13最初两个开发者预览版中就出现的,其中有蓝牙低能量音频,以及新的文件访问权限,用户可以决定应用能够读取相册中的哪些文件。此外,还有一个全新的权限,能够有效过滤垃圾邮件的通知,但认为这一功能在国内用处不大。
Android 13在界面设计风格上和Android 12没有太大区别,但在细节方面做了部分改进,比如音乐通知栏中的进度条,改为动态波浪样式。此外,新版还支持用户编辑剪贴板中的内容,对于常用复制粘贴的人会比较方便,建议国内深度定制系统也学习一下。
此前爆料的MGLRU多页面回收策略,Android 13首个测试版本似乎也没有实装,可能得等到正式版,用户才能体验到“满血”的原生系统。总体来看安卓13更像是12的隐私权限加强版,现在的操作系统都在权限做功课,功能、设计方面已经没有特别明显的改进,开始像iOS那样“挤牙膏”了。
在国内,随着近几年手机厂商对操作系统愈发重视,基于安卓的深度定制系统已经十分完善,从易用性、人性化的角度来看,已经能够迎合绝大部分消费者的需求,就连iOS、谷歌原生的某些新功能都是从国内定制系统中借鉴的。个人隐私方面,MIUI、ColorOS、Magic UI、Origin OS等定制系统也做到了,做的甚至还不差。
认为,近几年大家对系统大版本更新的期待值已经远不如以前,一方面是升级点有限,另一方面则是深度定制系统的崛起,原生系统或许就只剩下大版本更新快,动效流畅而已了。在用国产手机的小伙伴也不用着急,基于Android 13定制的正式版系统估计明年才会陆续推送。
㈡ 为什么 12G 内存的 Android 手机依旧会被杀后台
不知不觉间 Android 陷入了一个关于“后台”的怪圈:一边各大厂商陆续推出了 12G RAM 的手机,另一边你刚刚放到后台的下载任务没有如预期那样后台挂机下载,打开微信发现还得陪启动画面的孤独小人共赏蓝色星球、按照教程辛辛苦苦做了半个小时的 Tasker 规则、却没有按照计划自动执行……
于是一个耳熟能详的句子开始在我们脑海中成型—— 我的后台又被“杀”了 。
应用开发者的“控诉”
遇到上述问题的人不止你一个,很多人选择向这些应用的开发者反馈问题,殊不知问题其实不在应用本身。
Android 平台着名睡眠追踪应用 Sleep as Android 的开发团队 Urbandroid Team 不堪其扰,索性上线了一个名为“别‘杀’我应用”的网站,矛头直指手机厂商糟糕的后台管理机制。
以三星为例,Urbandroid Team 称,三星的部分机型在升级到基于 Android 9 的 One UI 后“杀后台”现象变得尤为严重,自适应电池(Adaptive Battery)机制相比原生 Android 变得尤为激进,3 天内没有启动过的应用甚至无法从后台再次启动。最为糟糕的情况是,如果你安装了一个可以自动跳过周末的第三方闹钟,那这个闹钟应用很有可能不会像系统闹钟那样在下周一早上准时响起……
正如“别‘杀’我应用”网站上控诉的那样,拥有类似机制的还包括华为、一加、小米、华硕等等手机厂商的定制版 Android 系统,它们管理后台的方式大同小异,但都秉承着 iOS 上那一套“划掉就杀掉”的原则——当我们把某款应用的任务卡片从多任务界面划去,它们也就彻底从手机后台中抹除掉了。
这里你可能会问很多问题:
我们得从一些基础的原理说起。
Android 的内存回收机制
在 官方文档 中,Google 将“不受应用自身直接控制的应用进程生命周期”描述为 Android 最为基础也最为独特的核心特性,这里我们不妨将“应用进程生命周期”暂时理解为文章开头和第一部分所说的“后台”或“后台进程(process)”。
所以 Android 应用的后台进程去留本应是由 Android 系统来决定的 。
当可用运行内存空间不足时,Android 系统会自行决定对特定应用后台进程占用的空间进行回收释放,这个过程中 Android 挥舞着的那把大刀,叫做 LMK(Low Memory Killer)。那 LMK 又是如何判断哪些应用可以被“杀”掉、哪些应用又该暂时放过的呢?
每个应用都有各种各样的组成部分,其中特定组件的运行状态共同组成了一套供 LMK 进行内存回收的“优先级”参考,包括:前台进程、可见进程、服务进程和缓存进程。
前台进程、可见进程和服务进程往往与我们正在手机上执行的操作直接或间接相关 ,比如正在前台供我们交互和操作的活动窗口(Activity)、正在通过广播接收器(BroadcastReceiver)等待触发的 Tasker 规则、正在后台通过 Wi-Fi 网络自动上传备份照片的 Google Photos 以及前面提到的有待触发的闹钟等等。这些进程优先级从高到低依次递减,LMK 一般不会触及。
缓存进程则是那些暂时放在运行内存中的部分,也是和本文探讨话题主要相关的重点 。
只有在极端情况下 ,比如 Android 系统在回收掉所有缓存进程后发现空闲内存依然不够用(比如在低内存的“老爷机”上运行《崩坏 3》),这时 LMK 才会根据优先级继续对服务进程、可见进程和前台进程采取回收策略。 而当这些我们在正常使用中能够直观感受到的进程都不得不被被回收时 ,文章开头提到的微信重载、音乐中断、下载消失等等现象也就出现了。
谁动了你的后台
在可用内存充裕的情况下遭遇“杀后台”现象,一方面可能是 LMK 这把“大刀”出了问题(常见于 Android 9 时期的 Pixel 3 用户),另一方面则有可能是其它规则额外干预了 Android 系统正常的内存回收机制。
这里提到的“其它规则”主要有两种形式,一种类似部分华为设备上预装的“省电精灵”,它会将所有没有加入后台白名单中的应用后台统统清除,另一种则依托于 Google 推出的 后台检查 、 后台限制 和 自适应电池 等功能进行“魔改”,让这些功能的实际效果远超预期,甚至达到意料之外的负面效果。
根据 Don't kill my app! 的统计,第二种后台干预机制在三星、一加和早期的诺基亚机型中常见,这里厂商们通常会用到一种类似“白名单”的方法来进行过滤。
以三星手机基于 Android 9 的 One UI 为例,除了微信、QQ 等国内常见应用,One UI 默认会为所有第三方应用关闭“允许后台活动”这一选项,同时开启“优化电池使用量”这一功能。
部分搭载氢 OS 的一加机型则将上面提到的应用进程进行拆分,除了基于原生 Android 的后台限制、电池优化,还有一套名为“自启动管理”的设置来对应用的自启动进行管理以及一套名为“深度优化”的电池优化机制,后者会造成很多智能手表、手环设备在一段时间后丢失与手机的蓝牙连接,最终导致睡眠追踪、运动记录等等功能的失效。
问题在于上述功能埋藏较深,一般用户在安装应用后往往不会第一时间前往设置,一加的氢 OS 更是以系统更新之后自动重置部分用户设置闻名,那些需要在后台正常工作的应用,因此也被都被直接扔进了原生 Android 中用来限制“毒瘤”应用的“黑箱”里。
换句话说,国内大部分定制 ROM 在后台管理这件事情上都选择采用一种“ 宁肯错杀一千不肯放过一个 ”的做法。
关联阅读: 控制频繁启动的“毒瘤”,Android 9.0 用这些方法让你的手机更省电
多任务管理还是后台管理?
从某种程度上来说,国产手机厂商在 Android 后台管理上的做法虽然偏激,但它们都是国内特殊生态下的产物 。
一方面,尽管 Google 为 Android 设想了一套非常理想化的应用运行与后台管理机制 ,但大多数于原生 Android 中行之有效的后台管理机制在国内似乎都会变成“鸡肋”。
如果 Google 有 100 种提升 Android 应用运行效率,保证后台绿色、纯净的方法,国内毒瘤应用开发商就有 101 种绕过这些限制的方法。
借助共用的第三方推送服务实现链式唤醒、借助透明的悬浮窗保证后台存活、通过不断获取定位的方式来避免进程被系统回收……不管是出于实现消息推送这样单纯的目的还是为了不断唤醒用户设备以实现 KPI 目标这种下作的行为,在国内 Android 生态中均有出现。
虽然国内外的具体环境有所不同,但这类设计不规范的 Android 应用带来的问题却是一样的,这类应用放在后台不仅不会为我们带来便利,反而还会因为频繁唤醒设备带来不小的耗电问题。待机续航问题作为悬在国产 Android 机头顶的几把利剑之一,手机厂商不得不各自从系统层面推出自家的应对机制——这就有了上面提到的各种偏激式的后台管理方法。
另一方面,这里还涉及到一个非常重要的概念区分 :多任务管理和后台管理究竟是不是一回事?
国内 Android 生态由于早期受 iOS 影响较深,无论是开发商还是用户都更倾向于把“将应用卡片从多任务列表里划掉”的行为理解为清除对应用的后台进程。在上面提到的特殊生态环境的影响之下,这里被清除的后台进程往往又包括那些用于保证应用后台运行的可见进程、服务进程乃至前台进程在内。
在酷安应用市场,甚至还有得以在原生 Android 上实现类似“划掉卡片即停止运行”效果的应用,iOS 的后台管理理念在国内有多么深入人心可见一斑。
但这种后台管理理念却与 Google 对 Android 的多任务管理设计方式相悖。Google 一直以来都将 Android 手机上呼出任务卡片的那个界面叫做 Recents,最近几个版本的 Android 系统更是将其本地化为“概览”。结合 Google 在 Android 9 和 Android 10 手势交互上的变革,注重多任务管理而非后台管理的意图也越发明显。
当最近运行的应用以一张张卡片的形式呈现在我们面前时,Google 想要呈现的是一个能够让我们在不同任务间快速切换的多任务交互,而在理想状态下,后台管理则是交由系统处理、完全不应被用户感知的。
至于如何理性看待 Android 平台的后台管理,这里我们不妨借用绿色守护开发者 @OasisFeng 在“Android 多任务界面的划除交互”这个话题上的 答疑 来回答这个问题:
换句话说, 今后绝大部分需要在海外市场搭载 Google 服务上市的手机都必须满足这个要求 。
小结
就在上周三(9 月 25 日),酝酿已久的安卓统一推送联盟正式宣布收到华为、OPPO、一加和 realme 四家公司的进度确认,虽然 Google 的缺席也让国内 Android 生态也变得异常复杂,但国内 Android 设备也能用上的统一推送服务也算是终于迈出了具有实际意义的第一步。
只是距离转变人们对 Android“杀后台”这件事的看法依然还有很长的路要走。事实上,国内早在四五年前就出现过一次对“Android 需不需要‘杀后台’”问题的科普,但收效甚微,盲从 iOS 设计风格和交互逻辑国内 Android 厂商要负很大一部分责任。
希望靠谱、省电的统一推送系统能成为改观的第一步,也希望 @OasisFeng 口中那个甚至可以跨越设备重启恢复“后台状态”的理想化生态早日到来——至于当下,我们依然只能见招拆招,遇到应用无法正常执行后台任务时打开手机设置仔细翻找、设置,把它们扔进白名单或是给它们的后台卡片套个“锁”……
下载、安装了一款非白名单应用,该如何确保它不被“杀后台”呢?欢迎在评论区把你的设置方法分享给大家。
㈢ android自带时钟应用的这个效果是怎么做的
次的小程序是一个Android小时钟。主要用到知识有Handler,Thread,Canvas,Path.
Handler:主要是用来接收子线程发送的数据,并用此数据配合主线程更新UI。Handler运行在主线程,通过message来与子线程传递数据。我只用到了sendMessage(Message)方法。
Thread在java中刚学过了,所以用起来还是比较简单的,就不多说了。
Canvas类就是表示一块画布,你可以在上面画你想画的东西。当然,你还可以设置画布的属性,如画布的颜色/尺寸等。
这东西我是第一次用,所以查了下canvas提供的方法。在时钟小程序中,我主要用的是rotate(),旋转画布。
Path也是第一次用到。moveTo和lineTo是用来设置开始的基点和最后的基点。我用path来画时钟的分针、秒针和时针。
关于android画图方面的知识是第一次接触到,(其实本来是想从网上下载一个时钟表盘的图片直接拿来用的),但是由于找不到合适的指针的图片,所以还是用画的了,虽然这样就会显得很朴素了,但是基本功能还是实现了。截图如下:
importjava.util.Calendar;
importandroid.annotation.SuppressLint;
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.view.View;
@SuppressLint({"ResourceAsColor","DrawAllocation"})
publicclassdrawextendsView{
publicdraw(Contextcontext){
super(context);
}
publicvoidonDraw(Canvascanvas){
Paintpaint=newPaint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);//空心的画笔
paint.setStrokeWidth(3);//设置paint的外框宽度
drawDial(canvas,paint);//绘制表盘
drawHand(canvas,paint);//绘制时针、分针、秒针
}
publicvoiddrawHand(Canvascanvas,Paintpaint){
intx=310;
inty=x;
inthour;
intminute;
intsecond;
finalCalendarcalendar=Calendar.getInstance();
hour=calendar.get(Calendar.HOUR);
minute=calendar.get(Calendar.MINUTE);
second=calendar.get(Calendar.SECOND);
floath=((hour+(float)minute/60)/12)*360;
floatm=((minute+(float)second/60)/60)*360;
floats=((float)second/60)*360;
//时针
paint.setColor(Color.WHITE);
canvas.save();//线锁定画布
canvas.rotate(h,x/2,y/2);//旋转画布
Pathpath1=newPath();
path1.moveTo(x/2,y/2);//开始的基点
path1.lineTo(x/2,y/4);//最后的基点
canvas.drawPath(path1,paint);
canvas.restore();
//分针
paint.setColor(R.color.MediumSlateBlue);
canvas.save();
canvas.rotate(m,x/2,y/2);//旋转画布
Pathpath2=newPath();
path2.moveTo(x/2,y/2);//开始的基点
path2.lineTo(x/2,y/6);//最后的基点
canvas.drawPath(path2,paint);
canvas.restore();
//秒针
paint.setColor(Color.BLUE);
canvas.save();
canvas.rotate(s,x/2,y/2);//旋转画布
Pathpath3=newPath();
path3.moveTo(x/2,y/2);//开始的基点
path3.lineTo(x/2,y/9);//最后的基点
canvas.drawPath(path3,paint);
canvas.restore();
}
publicvoiddrawDial(Canvascanvas,Paintpaint){
intx=310;
inty=x;
paint.setColor(Color.WHITE);
canvas.drawCircle(x/2,y/2,x/2-2,paint);
canvas.drawCircle(x/2,y/2,x/40,paint);
Pathpath9=newPath();//接下来的是,画时针的刻度
path9.moveTo(2,y/2);
path9.lineTo(y/18,y/2);
canvas.drawPath(path9,paint);
Pathpath12=newPath();
path12.moveTo(x/2,2);
path12.lineTo(x/2,y/18);
canvas.drawPath(path12,paint);
Pathpath3=newPath();
path3.moveTo(x-2,y/2);
path3.lineTo(x-x/18,y/2);
canvas.drawPath(path3,paint);
Pathpath6=newPath();
path6.moveTo(x/2,y-2);
path6.lineTo(x/2,y-y/18);
canvas.drawPath(path6,paint);
canvas.save();
canvas.rotate(32,x/2,y/2);
Pathpath10=newPath();
path10.moveTo(2,y/2);
path10.lineTo(x/32,y/2);
canvas.drawPath(path10,paint);
Pathpath1=newPath();
path1.moveTo(x/2,2);
path1.lineTo(x/2,y/32);
canvas.drawPath(path1,paint);
Pathpath4=newPath();
path4.moveTo(x-1,y/2);
path4.lineTo(x-x/32,y/2);
canvas.drawPath(path4,paint);
Pathpath7=newPath();
path7.moveTo(x/2,y-2);
path7.lineTo(x/2,y-y/32);
canvas.drawPath(path7,paint);
canvas.restore();
canvas.save();
canvas.rotate(60,x/2,y/2);
Pathpath11=newPath();
path11.moveTo(2,y/2);
path11.lineTo(x/32,y/2);
canvas.drawPath(path11,paint);
Pathpath2=newPath();
path2.moveTo(x/2,2);
path2.lineTo(x/2,y/32);
canvas.drawPath(path2,paint);
Pathpath5=newPath();
path5.moveTo(x-2,y/2);
path5.lineTo(x-x/32,y/2);
canvas.drawPath(path5,paint);
Pathpath8=newPath();
path8.moveTo(x/2,y-2);
path8.lineTo(x/2,y-y/32);
canvas.drawPath(path8,paint);
canvas.restore();
}
}
㈣ Android studio 开发app,如何抵抗动态调试,反调试代码怎么写请写上详细代码。
为了保护关键代码被逆向分析,一般放在应用程序初始化过程中,如init_array,或jni_onload函数里进行检查代码执行。
1.调试检测
对调试器的检测(ida,gdb,strace, ltrace等调试工具)
a.父进程检测
b.当前运行进程检测
例如对android_server进程检测。针对这种检测只需将android_server改名就可绕过
[objc] view plain
pid_t GetPidByName(const charchar *as_name) {
DIR *pdir = NULL;
struct dirent *pde = NULL;
FILEFILE *pf = NULL;
char buff[128];
pid_t pid;
char szName[128];
// 遍历/proc目录下所有pid目录
pdir = opendir("/proc");
if (!pdir) {
perror("open /proc fail.\n");
return -1;
}
while ((pde = readdir(pdir))) {
if ((pde->d_name[0] < '0') || (pde->d_name[0] > '9')) {
continue;
}
sprintf(buff, "/proc/%s/status", pde->d_name);
pf = fopen(buff, "r");
if (pf) {
fgets(buff, sizeof(buff), pf);
fclose(pf);
sscanf(buff, "%*s %s", szName);
pid = atoi(pde->d_name);
if (strcmp(szName, as_name) == 0) {
closedir(pdir);
return pid;
}
}
}
closedir(pdir);
return 0;
}
c.读取进程状态(/proc/pid/status)
State属性值T 表示调试状态,TracerPid 属性值正在调试此进程的pid,在非调试情况下State为S或R, TracerPid等于0
d.读取 /proc/%d/wchan
下图中第一个红色框值为非调试状态值,第二个红色框值为调试状态:
[objc] view plain
static void get_process_status(pid_t pid,const char* info,charchar *outline)
{
FILEFILE *fp;
char filename;
char line = {0};
snprintf( filename, sizeof(filename), "/proc/%d/status", pid );
fp = fopen( filename, "r" );
if ( fp != NULL )
{
while ( fgets( line, sizeof(line), fp ) )
{
if ( strstr( line, info ) )
strcpy(outline,line);
}
fclose( fp ) ;
}
return ;
}
static int getProcessStatus(int pid)
{
char readline = {0};
int result = STATUS_ELSE;
get_process_status(pid,"State",readline);
if(strstr(readline,"R"))
result = STATUS_RUNNING;
else if(strstr(readline,"S"))
result = STATUS_SLEEPING;
else if(strstr(readline,"T"))
result = STATUS_TRACING;
return result;
}
static int getTracerPid(int pid)
{
char readline = {0};
int result = INVALID_PID;
get_process_status(pid,"TracerPid",readline);
charchar *pidnum = strstr(readline,":");
result = atoi(pidnum + 1);
return result;
}
static int getWchanStatus(int pid)
{
FILEFILE *fp= NULL;
char filename;
char wchaninfo = {0};
int result = WCHAN_ELSE;
char cmd = {0};
sprintf(cmd,"cat /proc/%d/wchan",pid);
LOGANTI("cmd= %s",cmd);
FILEFILE *ptr; if((ptr=popen(cmd, "r")) != NULL)
{
if(fgets(wchaninfo, 128, ptr) != NULL)
{
LOGANTI("wchaninfo= %s",wchaninfo);
}
}
if(strncasecmp(wchaninfo,"sys_epoll\0",strlen("sys_epoll\0")) == 0)
result = WCHAN_RUNNING;
else if(strncasecmp(wchaninfo,"ptrace_stop\0",strlen("ptrace_stop\0")) == 0)
result = WCHAN_TRACING;
return result;
}
e. ptrace 自身或者fork子进程相互ptrace
[objc] view plain
ptrace me
if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
printf("DEBUGGING... Bye\n");
return 1;
}
void anti_ptrace(void)
{
pid_t child;
child = fork();
if (child)
wait(NULL);
else {
pid_t parent = getppid();
if (ptrace(PTRACE_ATTACH, parent, 0, 0) < 0)
while(1);
sleep(1);
ptrace(PTRACE_DETACH, parent, 0, 0);
exit(0);
}
}
f. 防止mp
利用Inotify机制,对/proc/pid/mem和/proc/pid/pagemap文件进行监视。inotify API提供了监视文件系统的事件机制,可用于监视个体文件,或者监控目录。具体原理可参考:http://man7.org/linux/man- pages/man7/inotify.7.html
伪代码:
[objc] view plain
void __fastcall anitInotify(int flag)
{
MemorPagemap = flag;
charchar *pagemap = "/proc/%d/pagemap";
charchar *mem = "/proc/%d/mem";
pagemap_addr = (charchar *)malloc(0x100u);
mem_addr = (charchar *)malloc(0x100u);
ret = sprintf(pagemap_addr, &pagemap, pid_);
ret = sprintf(mem_addr, &mem, pid_);
if ( !MemorPagemap )
{
ret = pthread_create(&th, 0, (voidvoid *(*)(voidvoid *)) inotity_func, mem_addr);
if ( ret >= 0 )
ret = pthread_detach(th);
}
if ( MemorPagemap == 1 )
{
ret = pthread_create(&newthread, 0, (voidvoid *(*)(voidvoid *)) inotity_func, pagemap_addr);
if(ret > 0)
ret = pthread_detach(th);
}
}
void __fastcall __noreturn inotity_func(const charchar *inotity_file)
{
const charchar *name; // r4@1
signed int fd; // r8@1
bool flag; // zf@3
bool ret; // nf@3
ssize_t length; // r10@3
ssize_t i; // r9@7
fd_set readfds; // @2
char event; // @1
name = inotity_file;
memset(buffer, 0, 0x400u);
fd = inotify_init();
inotify_add_watch(fd, name, 0xFFFu);
while ( 1 )
{
do
{
memset(&readfds, 0, 0x80u);
}
while ( select(fd + 1, &readfds, 0, 0, 0) <= 0 );
length = read(fd, event, 0x400u);
flag = length == 0;
ret = length < 0;
if ( length >= 0 )
{
if ( !ret && !flag )
{
i = 0;
do
{
inotity_kill((int)&event);
i += *(_DWORD *)&event + 16;
}
while ( length > i );
}
}
else
{
while ( *(_DWORD *)_errno() == 4 )
{
length = read(fd, buffer, 0x400u);
flag = length == 0;
ret = length < 0;
if ( length >= 0 )
}
}
}
}
g. 对read做hook
因为一般的内存mp都会调用到read函数,所以对read做内存hook,检测read数据是否在自己需要保护的空间来阻止mp
h. 设置单步调试陷阱
[objc] view plain
int handler()
{
return bsd_signal(5, 0);
}
int set_SIGTRAP()
{
int result;
bsd_signal(5, (int)handler);
result = raise(5);
return result;
}