s=[m;n];randn('ftnstat.stat..

Flanker Sky | About security and coding
Denial of App – Google Bug
Author: hqdvista,转载注明出处。该文也已发布在drops.wooyun.org.
Soot作者Eric Bodden所在的实验室, Secure Software Engineering最近宣布他们将在SPSM’14上讲述名为Denial-of-App-Attack的Android系统漏洞,影响4.4.3之前的机型,并给出了poc和对应的google commit id. 这个在googlecode上对应的链接是/p/android/issues/detail?id=65790。
POC:/secure-software-engineering/denial-of-app-attack
该问题可以导致攻击者可以指定应用使其无法安装在手机上,除非有root权限或者factory reset手机。可以被木马用来占位拒绝杀毒软件的安装,或者占位拒绝竞品安装。下面是根据commit diff和poc给出的漏洞具体分析。
###问题现象:
下载安装这个POC,可以看到其实就是指定一个packagename,例如com.taobao.taobao,然后生成了一个malformed的APK并执行安装,由于该APK的dex是非法的,安装的时候会报告INSTALL_FAILED_DEXOPT并安装失败。但如果随后安装真正的com.taobao.taobao时,即使指定了重新安装选项(pm install -r),却会报INSTALL_FAILED_UID_CHANGED,导致后续安装失败,而在被占位的手机上已安装应用中却找不到com.taobao.taobao,自然也无法清除掉占位的幽灵,造成真正的淘宝应用完全无法安装,推而广之可以用在360等杀毒软件上。
###问题本质:
Google的diff对此问题的描述是:
We'd otherwise leave the data dirs & native libraries lying around. This will leave the app permanently broken because the next install of the app will fail with INSTALL_FAILED_UID_CHANGED. Also remove an unnecessary instance variable. Cherry-pick from master Bug
We'd otherwise leave the data dirs & native libraries lying around. This will leave the app permanently broken because the next install of the app will fail with INSTALL_FAILED_UID_CHANGED. Also remove an unnecessary instance variable. Cherry-pick from master Bug &
通过观察可以发现,第一次安装(所谓“占位”)结束的时候,在/data/data/目录下已经有了com.taobao.taobao目录并分配了一个uid,例如u70(10070),但第二次安装的时候,PackageManager却出现了UID_CHANGED的error,而没有复用u70,这是为什么?
INSTALL_FAILED_DEXOPT和UID_CHANGED是在如下代码块中:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime, UserHandle user) {
if ((scanMode&SCAN_NO_DEX) == 0) {
if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)
== DEX_OPT_FAILED) {
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
1234567891011
3622&&&&private PackageParser.Package scanPackageLI(PackageParser.Package pkg,3623&&&&&&&&&&&&int parseFlags, int scanMode, long currentTime, UserHandle user) {//....4141&&&&&&&&if ((scanMode&SCAN_NO_DEX) == 0) {4142&&&&&&&&&&&&if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)4143&&&&&&&&&&&&&&&&&&&&== DEX_OPT_FAILED) {4144&&&&&&&&&&&&&&&&mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;4145&&&&&&&&&&&&&&&&return null;4146&&&&&&&&&&&&}4147&&&&&&&&}&
scanPackageLI函数流程大概如下:
//检查是否系统应用
//检查Package是否重复,否则抛出PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE
// Initialize package source and resource directories
File destCodeFile = new File(pkg.applicationInfo.sourceDir);
File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.flags, user, false);
//在这之后uid已经被指定了
//检查签名
//检查Provider权限
//开始创建目录
final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
File dataP
if (mPlatformPackage == pkg) {
// This is a normal package, need to make its data directory.
dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError =
if (dataPath.exists()) {
int currentUid = 0;
StructStat stat = Libcore.os.stat(dataPath.getPath());
currentUid = stat.st_
} catch (ErrnoException e) {
Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
// If we have mismatched owners for the data path, we have a problem.
if (currentUid != pkg.applicationInfo.uid) {
boolean recovered =
if (currentUid == 0) {
if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
|| (scanMode&SCAN_BOOTING) != 0)) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
} else if (!recovered) {
// If we allow this install to proceed, we will be broken.
// Abort, abort!
mLastScanError = PackageManager.INSTALL_FAILED_UID_CHANGED;
} else {//目录不存在,新建立
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.v(TAG, "Want this data dir: " + dataPath);
//invoke installer to do the actual installation
int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);//建立目录
if (ret & 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
Slog.w(TAG, "Unable to create data directory: " + dataPath);
pkg.applicationInfo.dataDir =
//拷贝nativeLibrary
//进行DexOpt
if ((scanMode&SCAN_NO_DEX) == 0) {
if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)
== DEX_OPT_FAILED) {
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
/**///检查是否系统应用/**///检查Package是否重复,否则抛出PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE&&// Initialize package source and resource directories3686&&&&&&&&File destCodeFile = new File(pkg.applicationInfo.sourceDir);3687&&&&&&&&File destResourceFile = new File(pkg.applicationInfo.publicSourceDir);//... // Just create the setting, don't add it yet. For already existing packages3812&&&&&&&&&&&&// the PkgSetting exists already and doesn't have to be created.3813&&&&&&&&&&&&pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,3814&&&&&&&&&&&&&&&&&&&&destResourceFile, pkg.applicationInfo.nativeLibraryDir,3815&&&&&&&&&&&&&&&&&&&&pkg.applicationInfo.flags, user, false);//在这之后uid已经被指定了/**///检查签名//检查Provider权限&//开始创建目录&& final long scanFileTime = scanFile.lastModified();3926&&&&&&&&final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;3927&&&&&&&&pkg.applicationInfo.processName = fixProcessName(3928&&&&&&&&&&&&&&&&pkg.applicationInfo.packageName,3929&&&&&&&&&&&&&&&&pkg.applicationInfo.processName,3930&&&&&&&&&&&&&&&&pkg.applicationInfo.uid);39313932&&&&&&&&File dataPath;3933&&&&&&&&if (mPlatformPackage == pkg) {//omit3937&&&&&&&&} else {3938&&&&&&&&&&&&// This is a normal package, need to make its data directory.3939&&&&&&&&&&&&dataPath = getDataPathForPackage(pkg.packageName, 0);39403941&&&&&&&&&&&&boolean uidError = false;39423943&&&&&&&&&&&&if (dataPath.exists()) {3944&&&&&&&&&&&&&&&&int currentUid = 0;3945&&&&&&&&&&&&&&&&try {3946&&&&&&&&&&&&&&&&&&&&StructStat stat = Libcore.os.stat(dataPath.getPath());3947&&&&&&&&&&&&&&&&&&&&currentUid = stat.st_uid;3948&&&&&&&&&&&&&&&&} catch (ErrnoException e) {3949&&&&&&&&&&&&&&&&&&&&Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);3950&&&&&&&&&&&&&&&&}39513952&&&&&&&&&&&&&&&&// If we have mismatched owners for the data path, we have a problem.3953&&&&&&&&&&&&&&&&if (currentUid != pkg.applicationInfo.uid) {3954&&&&&&&&&&&&&&&&&&&&boolean recovered = false;3955&&&&&&&&&&&&&&&&&&&&if (currentUid == 0) {3956&&&&&&&&&&&&&&&&&&&& //omit...3969&&&&&&&&&&&&&&&&&&&&}3970&&&&&&&&&&&&&&&&&&&&if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 03971&&&&&&&&&&&&&&&&&&&&&&&&&&&&|| (scanMode&SCAN_BOOTING) != 0)) {3972&&&&&&&&&&&&&&&&&&&&&&&&// If this is a system app, we can at least delete its3973&&&&&&&&&&&&&&&&&&&&&&&&// current data so the application will still work.3974&&&&&&&&&&&&&&&&&&&&&&&&//omit...4001&&&&&&&&&&&&&&&&&&&&} else if (!recovered) {4002&&&&&&&&&&&&&&&&&&&&&&&&// If we allow this install to proceed, we will be broken.4003&&&&&&&&&&&&&&&&&&&&&&&&// Abort, abort!4004&&&&&&&&&&&&&&&&&&&&&&&&mLastScanError = PackageManager.INSTALL_FAILED_UID_CHANGED;4005&&&&&&&&&&&&&&&&&&&&&&&&return null;4006&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&& } else {//目录不存在,新建立4029&&&&&&&&&&&&&&&&if (DEBUG_PACKAGE_SCANNING) {4030&&&&&&&&&&&&&&&&&&&&if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)4031&&&&&&&&&&&&&&&&&&&&&&&&Log.v(TAG, "Want this data dir: " + dataPath);4032&&&&&&&&&&&&&&&&}4033&&&&&&&&&&&&&&&&//invoke installer to do the actual installation4034&&&&&&&&&&&&&&&&int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);//建立目录4035&&&&&&&&&&&&&&&&if (ret < 0) {4036&&&&&&&&&&&&&&&&&&&&// Error from installer4037&&&&&&&&&&&&&&&&&&&&mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;4038&&&&&&&&&&&&&&&&&&&&return null;4039&&&&&&&&&&&&&&&&}40404041&&&&&&&&&&&&&&&&if (dataPath.exists()) {4042&&&&&&&&&&&&&&&&&&&&pkg.applicationInfo.dataDir = dataPath.getPath();4043&&&&&&&&&&&&&&&&} else {4044&&&&&&&&&&&&&&&&&&&&Slog.w(TAG, "Unable to create data directory: " + dataPath);4045&&&&&&&&&&&&&&&&&&&&pkg.applicationInfo.dataDir = null;4046&&&&&&&&&&&&&&&&}4047&&&&&&&&&&&&}//omit...//拷贝nativeLibrary//omit...//进行DexOpt4141&&&&&&&&if ((scanMode&SCAN_NO_DEX) == 0) {4142&&&&&&&&&&&&if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)4143&&&&&&&&&&&&&&&&&&&&== DEX_OPT_FAILED) {4144&&&&&&&&&&&&&&&&mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;4145&&&&&&&&&&&&&&&&return null;4146&&&&&&&&&&&&}4147&&&&&&&&}&&
那么漏洞的原理就很清楚了,第一次占位安装时,故意让PMS在数据目录已分配uid并写入了/data/data/下之后走到dexopt时使其报错,导致安装异常终止,此时已放置的数据目录却没有被清除掉。第二次安装的时候package被分配了新的的uid,但此时已有同名却不同uid的数据目录存在,导致uid_changed错误,安装失败。
为什么第二次安装的时候就会被分配不同的uid?关键在于 mSettings.getPackageLPw,辗转ref到/frameworks/base/services/java/com/android/server/pm/Settings.java
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
String nativeLibraryPathString, int vc, int pkgFlags,
UserHandle installUser, boolean add, boolean allowInstall) {
p = new PackageSetting(name, realName, codePath, resourcePath,
nativeLibraryPathString, vc, pkgFlags);
p.setTimeStamp(codePath.lastModified());
p.sharedUser = sharedU
// If this is not a system app, it starts out stopped.
if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (DEBUG_STOPPED) {
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);
List&UserInfo& users = getAllUsers();
if (users != null && allowInstall) {
for (UserInfo user : users) {
// By default we consider this app to be installed
// for the user if no user has been specified (which
// means to leave it at its original value, and the
// original default value is true), or we are being
// asked to install for all users, or this is the
// user we are installing for.
final boolean installed = installUser == null
|| installUser.getIdentifier() == UserHandle.USER_ALL
|| installUser.getIdentifier() == user.
p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
installed,
true, // stopped,
true, // notLaunched
null, null);
writePackageRestrictionsLPr(user.id);
if (sharedUser != null) {
p.appId = sharedUser.userId;
// Clone the setting here for disabled system packages
PackageSetting dis = mDisabledSysPackages.get(name);
if (dis != null) {
// Assign new user id
p.appId = newUserIdLPw(p);//关键点
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,359&&&&&&&&&&&&String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,360&&&&&&&&&&&&String nativeLibraryPathString, int vc, int pkgFlags,361&&&&&&&&&&&&UserHandle installUser, boolean add, boolean allowInstall) {//omit...&&&&} else {423&&&&&&&&&&&&&&&&p = new PackageSetting(name, realName, codePath, resourcePath,424&&&&&&&&&&&&&&&&&&&&&&&&nativeLibraryPathString, vc, pkgFlags);425&&&&&&&&&&&&&&&&p.setTimeStamp(codePath.lastModified());426&&&&&&&&&&&&&&&&p.sharedUser = sharedUser;427&&&&&&&&&&&&&&&&// If this is not a system app, it starts out stopped.428&&&&&&&&&&&&&&&&if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {429&&&&&&&&&&&&&&&&&&&&if (DEBUG_STOPPED) {430&&&&&&&&&&&&&&&&&&&&&&&&RuntimeException e = new RuntimeException("here");431&&&&&&&&&&&&&&&&&&&&&&&&e.fillInStackTrace();432&&&&&&&&&&&&&&&&&&&&&&&&Slog.i(PackageManagerService.TAG, "Stopping package " + name, e);433&&&&&&&&&&&&&&&&&&&&}434&&&&&&&&&&&&&&&&&&&&List<UserInfo> users = getAllUsers();435&&&&&&&&&&&&&&&&&&&&if (users != null && allowInstall) {436&&&&&&&&&&&&&&&&&&&&&&&&for (UserInfo user : users) {437&&&&&&&&&&&&&&&&&&&&&&&&&&&&// By default we consider this app to be installed438&&&&&&&&&&&&&&&&&&&&&&&&&&&&// for the user if no user has been specified (which439&&&&&&&&&&&&&&&&&&&&&&&&&&&&// means to leave it at its original value, and the440&&&&&&&&&&&&&&&&&&&&&&&&&&&&// original default value is true), or we are being441&&&&&&&&&&&&&&&&&&&&&&&&&&&&// asked to install for all users, or this is the442&&&&&&&&&&&&&&&&&&&&&&&&&&&&// user we are installing for.443&&&&&&&&&&&&&&&&&&&&&&&&&&&&final boolean installed = installUser == null444&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&|| installUser.getIdentifier() == UserHandle.USER_ALL445&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&|| installUser.getIdentifier() == user.id;446&&&&&&&&&&&&&&&&&&&&&&&&&&&&p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,447&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&installed,448&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&true, // stopped,449&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&true, // notLaunched450&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&null, null);451&&&&&&&&&&&&&&&&&&&&&&&&&&&&writePackageRestrictionsLPr(user.id);452&&&&&&&&&&&&&&&&&&&&&&&&}453&&&&&&&&&&&&&&&&&&&&}454&&&&&&&&&&&&&&&&}455&&&&&&&&&&&&&&&&if (sharedUser != null) {456&&&&&&&&&&&&&&&&&&&&p.appId = sharedUser.userId;457&&&&&&&&&&&&&&&&} else {458&&&&&&&&&&&&&&&&&&&&// Clone the setting here for disabled system packages459&&&&&&&&&&&&&&&&&&&&PackageSetting dis = mDisabledSysPackages.get(name);460&&&&&&&&&&&&&&&&&&&&if (dis != null) {//omit..484&&&&&&&&&&&&&&&&&&&&} else {485&&&&&&&&&&&&&&&&&&&&&&&&// Assign new user id486&&&&&&&&&&&&&&&&&&&&&&&&p.appId = newUserIdLPw(p);//关键点487&&&&&&&&&&&&&&&&&&&&}488&&&&&&&&&&&&&&&&}&
继续查看newUserIdLPw
private int newUserIdLPw(Object obj) {
// Let's be stupidly inefficient for now...
final int N = mUserIds.size();
for (int i = 0; i & N; i++) {
if (mUserIds.get(i) == null) {//检查空位
mUserIds.set(i, obj);
return Process.FIRST_APPLICATION_UID +
// None left?
if (N & (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
return -1;
mUserIds.add(obj);
return Process.FIRST_APPLICATION_UID + N;
12345678910111213141516171819
private int newUserIdLPw(Object obj) {2360&&&&&&&&// Let's be stupidly inefficient for now...2361&&&&&&&&final int N = mUserIds.size();2362&&&&&&&&for (int i = 0; i < N; i++) {2363&&&&&&&&&&&&if (mUserIds.get(i) == null) {//检查空位2364&&&&&&&&&&&&&&&&mUserIds.set(i, obj);2365&&&&&&&&&&&&&&&&return Process.FIRST_APPLICATION_UID + i;2366&&&&&&&&&&&&}2367&&&&&&&&}23682369&&&&&&&&// None left?2370&&&&&&&&if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {2371&&&&&&&&&&&&return -1;2372&&&&&&&&}23732374&&&&&&&&mUserIds.add(obj);2375&&&&&&&&return Process.FIRST_APPLICATION_UID + N;2376&&&&}&
mUserIds是一个PackageSettings的数组状结构,维护了当前的userid,并在安装时遍历进行分配。在第一次恶意的占位安装中,mUserIds这个array状结构已经被添加了一个PackageSettings进去,形成类似于[PackageSetting{(10001, bla)},…,PackageSetting{(10070, com.taobao.taobao)}]的结构,但在dexopt failed的时候最末尾一项没有被移除。随后再安装时,newUserIdLPw会遍历mUserIds,发现没有空位,就会在末尾重新添加一个,形成[PackageSetting{(10001, bla)},…,PackageSetting{(10070, com.taobao.taobao)},PackageSetting{(10071, com.taobao.taobao)}]的结构,导致两次安装分配的UID不同,触发INSTALL_FAILED_UID_CHANGED。
但值得注意的是,这时候mUserIds并没有被固化在packages.xml和packages.list中。
###进一步思考:
那么这样肯定会想到,如果杀掉system_server(软重启),让其重新扫描并建立mUserIds数组不就能修复这个问题了?
理论上来说,如果在重启前没有安装过其他应用的话,那么这还真是可行的。因为重启后重新建立的uid数组是[(10001, bla),…,(10069, haha)],那么重新安装的com.taobao.taobao刚好能占到10070的位置,皆大欢喜。
但如果在重启后又安装了其他应用,那么其就会占掉10070的位置,导致taobao再安装的时候以10071及之后的uid就拿不回原来应该属于它的/data/data/com.taobao.taobao了… what a pity.
以上在stock rom(Genymotion, SDK)和小米2、Nexus等上验证通过。
所以现在看来,原作者说只有root或者reset才能清除这个问题的说法似乎不准确,至少从给出的poc和google的diff来看实验结果某些情况下重启就能fix。总体来说,这是一个比较好玩的trick类漏洞,而且从issuelink来看,应该还有一些其他类型的同样效果的漏洞存在。
###Google对此的修复:
Google的diff主要是添加了SCAN_DELETE_DATA_ON_FAILURES的flag,在设置了该flag的时候安装失败时会删除遗留掉的文件。
@@ -43,10 @@
if ((scanMode&SCAN_NO_DEX) == 0) {
if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
== DEX_OPT_FAILED) {
if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.packageName);
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
@@ -24,10 @@
PackageParser.Package clientPkg = clientLibPkgs.get(i);
if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
== DEX_OPT_FAILED) {
if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.packageName);
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
1234567891011121314151617181920212223
@@ -4644,6 +4643,10 @@&&&&&&&& if ((scanMode&SCAN_NO_DEX) == 0) {&&&&&&&&&&&& if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)&&&&&&&&&&&&&&&&&&&& == DEX_OPT_FAILED) {+&&&&&&&&&&&&&&&&if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {+&&&&&&&&&&&&&&&&&&&&removeDataDirsLI(pkg.packageName);+&&&&&&&&&&&&&&&&}+&&&&&&&&&&&&&&&& mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;&&&&&&&&&&&&&&&& return null;&&&&&&&&&&&& }@@ -4721,6 +4724,10 @@&&&&&&&&&&&&&&&&&&&& PackageParser.Package clientPkg = clientLibPkgs.get(i);&&&&&&&&&&&&&&&&&&&& if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)&&&&&&&&&&&&&&&&&&&&&&&&&&&& == DEX_OPT_FAILED) {+&&&&&&&&&&&&&&&&&&&&&&&&if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {+&&&&&&&&&&&&&&&&&&&&&&&&&&&&removeDataDirsLI(pkg.packageName);+&&&&&&&&&&&&&&&&&&&&&&&&}+&&&&&&&&&&&&&&&&&&&&&&&& mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;&&&&&&&&&&&&&&&&&&&&&&&& return null;&&&&&&&&&&&&&&&&&&&& }&
###如何fix某个占位攻击:
root下删除该数据目录即可,非root。。。那只能reset了。
本条目发布于。属于分类。作者是。
author: hqdvista a.k.a flanker017
原创,转载请注明出处
0x01 Root下命令记录的情况
在Android应用中,有各种各样的应用都会去执行命令,很多灰色应用则是调用su去执行一些见不得人的勾当。一般来说执行root命令在framework层会这么做:
public static String execSuCommand(String cmd) throws IOException
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes(cmd+"n");
os.flush();
os.writeBytes("exitn");
os.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) & 0) {
output.append(buffer, 0, read);
reader.close();
os.close();
return output.toString();
123456789101112131415161718192021
public static String execSuCommand(String cmd) throws IOException&&&&{&&&&&&&&Process process = Runtime.getRuntime().exec("su");&&&&&&&&&&DataOutputStream os = new DataOutputStream(process.getOutputStream());&&&&&&&&&&os.writeBytes(cmd+"n");&&&&&&&&os.flush();&&&&&&&&os.writeBytes("exitn");&&&&&&&&os.flush();&&&&&&&&&&&&&&&&BufferedReader reader = new BufferedReader(new InputStreamReader(&&&&&&&&&&&&&&&&&&process.getInputStream()));&&&&&&&&&&int read;&&&&&&&&&&char[] buffer = new char[4096];&&&&&&&&&&StringBuffer output = new StringBuffer();&&&&&&&&&&while ((read = reader.read(buffer)) & 0) {&&&&&&&&&&&&&&output.append(buffer, 0, read);&&&&&&&&&&}&&&&&&&&&&reader.close();&&&&&&&&os.close();&&&&&&&&return output.toString();&&&&}
当然,在native层直接调用su也可以。那沙盒监控中的需求就来了:如何监控到app执行了什么样的shell命令?
本条目发布于。属于、分类。作者是。
author: hqdvista a.k.a flanker017
0x01 数据包分析
将数据包 (链接:/s/1ntrzThB 密码:cbf2)下载下来,在wireshark中打开,看一下statistics和conversations,会看到一大坨http和personal-agent(5555)端口。http看过一遍,基本都是新浪新闻、google搜索ACTF这种,似乎没有什么有价值信息。(X里的wireshark太难看了)
那么5555端口会是什么?大概follow stream一下,
玩过android的人应该会意识到这是adb的协议。从数据流大小来看,普通的adb shell命令很难会产生这么多数据,那么要么是adb pull从设备中拖取了什么信息,要么是adb push了什么东西。
再往下翻: ,就会发现有意思的东西,安装流量,也就是说流量里是一个完整的安装APK的过程!
本条目发布于。属于、、分类。作者是。
注:原文发表于 并同时发表于freebuf,后来在博客地震中消失了,现在的是根据freebuf上的恢复而来的。
近期Android爆出SMS smishing vuln, 首先来源于, 然后,具体来说是任意一个app在没有write_sms权限下可以伪造任意发件人的任意短信。
本条目发布于。属于分类。作者是。
注:原文发表于,之后在博客地震中消失了,这是从cc98上恢复的版本。
CC98论坛是浙大校内最大的论坛,每日有数万发帖,这个网站是由校内学生团队维护。由于历史原因,采用的是一套有一定历史的论坛系统,并在上面做了很多修改。修改后的系统基本杜绝了SQL注入漏洞,安全性有很大提高,但是也缺乏很多web2.0功能和一些对xss的防范。本文讲述的就是我在12.31日22点左右对其进行的一次XSS蠕虫测试。蠕虫以站短形式传播,会选取被感染发过的主题楼中第一页出现的ID发送站短,并使被感染账户在一个指定的楼里(标题名happy new year~) 进行回复。初始传播约为10人,传播开始后大约半小时感染了数百用户,蠕虫自动回复楼已经攀升到当日十大最热门贴子。然后被管理团队发现,删掉了自动回复楼,并关闭了站短功能(蠕虫传播渠道)。此后樱桃和我进行了沟通,并fix了此xss点。
本条目发布于。属于分类。作者是。
一般来说在使用cookielib的CookieJar时,如果想手动设置header中的cookie字段,需要使用MozillaCookieJar从cookie文件载入,这显然太不方便了,如果直接对每一个请求set-header又相当于需要完全自己管理cookie,放弃了urllib2提供的HttpCookieProcessor。有没有两全其美的办法?
读了会源码,发现Cookie库提供的SimpleCookie可以从raw string解析,但是它并不能用于CookieJar的set_cookie方法中,因为CookieJar事实上接受的是cookielib.cookie这个类型… 但仍然可以利用SimpleCookie的解析方式,提取出raw string的value pair,调用cookielib.cookie的构造函数传入set_cookie中。
本条目发布于。属于分类。作者是。
在改写cookiemonster时遇到了此问题,暂时通过将browser模块抽出来成为单独的py然后传参数execl解决,但仍不清楚为何Process中QApplication退出后开启新的event loop不可行
本条目发布于。属于、分类。作者是。
虽然Nokia已经结束了对Qt-jambi的官方支持,但是从个人较多使用Qt进行C++ GUI编程的这个角度来说,我还是比较喜欢jambi做Java上的GUI编程。Swing太过丑陋,SWT没有很多接触过,个人还是觉得熟悉的Qt比较优雅和熟练。
本条目发布于。属于、分类。作者是。
LaoHeShanXia 浙江大学教务网客户端 Android 状态:暂停
下载地址:
GradeNotifier 浙江大学教务网成绩提示工具 Qt/Java
下载地址:
本条目发布于。属于、、分类。作者是。
某天无聊抓包的时候研究了下zjuvpn的连接和断开过程,应该来说是典型的PPP协议的握手、验证与建立过程,但断开vpn时的流程引起了我的兴趣。
本条目发布于。属于、、分类。作者是。}

我要回帖

更多关于 randn 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信