文章Android Security Tools对1~4的使用有介绍,下面主要分析其源码实现。

1.Manifest Explorer

2.Package Play

Main.java

public void onCreate(Bundle savedInstanceState) {
...
Map<String, PackageInfo> packages = new HashMap<String, PackageInfo>();
PackageManager pm = getPackageManager();
List<PackageInfo> l = pm.getInstalledPackages(0xFFFFFFFF);
...

函数getInstalledPackages(int)参数有如下取值:

flags Value
GET_ACTIVITIES 0x00000001
GET_GIDS 0x00000100
GET_CONFIGURATIONS 0x00004000
GET_INSTRUMENTATION 0x00000010
GET_PERMISSIONS 0x00001000
GET_PROVIDERS 0x00000008
GET_RECEIVERS 0x00000002
GET_SERVICES 0x00000004
GET_SIGNATURES 0x00000040
GET_UNINSTALLED_PACKAGES 0x00002000

为oxFFFFFFFF是由于Android此函数实现对flags取“&”,不能包括GET_META_DATA,可取512或14111。详见ApplicationPackageManager.java

@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
return getInstalledPackages(flags, mContext.getUserId());
} /** @hide */
@Override
public List<PackageInfo> getInstalledPackages(int flags, int userId) {
try {
ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
return slice.getList();
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}

PackageManagerService.java

@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0; enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "get installed packages"); // writer
synchronized (mPackages) {
ArrayList<PackageInfo> list;
if (listUninstalled) {
list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
PackageInfo pi;
if (ps.pkg != null) {
pi = generatePackageInfo(ps.pkg, flags, userId);
} else {
pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
}
if (pi != null) {
list.add(pi);
}
}
} else {
list = new ArrayList<PackageInfo>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
PackageInfo pi = generatePackageInfo(p, flags, userId);
if (pi != null) {
list.add(pi);
}
}
} return new ParceledListSlice<PackageInfo>(list);
}
} PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
PackageInfo pi;
final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
}
final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
final PackageUserState state = ps.readUserState(userId);
return PackageParser.generatePackageInfo(p, gp.gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
state, userId);
} private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
int userId) {
if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
PackageParser.Package pkg = ps.pkg;
if (pkg == null) {
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
return null;
}
pkg = new PackageParser.Package(packageName);
pkg.applicationInfo.packageName = packageName;
pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
pkg.applicationInfo.sourceDir = ps.codePathString;
pkg.applicationInfo.dataDir =
getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
// pkg.mSetEnabled = ps.getEnabled(userId);
// pkg.mSetStopped = ps.getStopped(userId);
return generatePackageInfo(pkg, flags, userId);
}
return null;
}

PackageParser.java

public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions, PackageUserState state, int userId) { if (!checkUseInstalled(flags, state)) {
return null;
}
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
|| (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
pi.requiredForAllUsers = p.mRequiredForAllUsers;
}
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
pi.gids = gids;
}
if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
int N = p.configPreferences.size();
if (N > 0) {
pi.configPreferences = new ConfigurationInfo[N];
p.configPreferences.toArray(pi.configPreferences);
}
N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
if (N > 0) {
pi.reqFeatures = new FeatureInfo[N];
p.reqFeatures.toArray(pi.reqFeatures);
}
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
int N = p.activities.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities = new ActivityInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.activities.get(i).info.enabled) num++;
}
pi.activities = new ActivityInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Activity activity = p.activities.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_RECEIVERS) != 0) {
int N = p.receivers.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers = new ActivityInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.receivers.get(i).info.enabled) num++;
}
pi.receivers = new ActivityInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_SERVICES) != 0) {
int N = p.services.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.services = new ServiceInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.services.get(i).info.enabled) num++;
}
pi.services = new ServiceInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_PROVIDERS) != 0) {
int N = p.providers.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.providers = new ProviderInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.providers.get(i).info.enabled) num++;
}
pi.providers = new ProviderInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
int N = p.instrumentation.size();
if (N > 0) {
pi.instrumentation = new InstrumentationInfo[N];
for (int i=0; i<N; i++) {
pi.instrumentation[i] = generateInstrumentationInfo(
p.instrumentation.get(i), flags);
}
}
}
if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
int N = p.permissions.size();
if (N > 0) {
pi.permissions = new PermissionInfo[N];
for (int i=0; i<N; i++) {
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
N = p.requestedPermissions.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
for (int i=0; i<N; i++) {
final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
if (p.requestedPermissionsRequired.get(i)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
}
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
}
}
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
int N = (p.mSignatures != null) ? p.mSignatures.length : 0;
if (N > 0) {
pi.signatures = new Signature[N];
System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
}
}
return pi;
}

回到此apk的源文件ViewPackage.java

@Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent startingIntent = this.getIntent(); try {
ArrayList<String> toShow = startingIntent.getExtras()
.getStringArrayList("pkgs");
/*
* Allow starting of activities when we are in single package view
* mode, and give package count when not exactly one.
*/
if (toShow.size() != 1)
mOut.append("Received " + toShow.size() + " package names\n");
else
setupActionStuff(pm.getPackageInfo(toShow.get(0), 0xFFFFFFFF)); for (String cur : toShow) {
mOut.append(describePackage(packages.get(cur)));
}
... void initControls() {
...
mStartButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startCurrentlySelected();
}
});
... /**
* Populates the activity selection spinner and activates start button if
* needed.
*/
public void setupActionStuff(PackageInfo p) {
showActivityUI(false);
mActivitiesList.clear();
mPkgName = null; // enable system and manifest views as this is an individual package
mSystemViewButton.setVisibility(View.VISIBLE);
try {
getPackageManager().getPackageGids(
"com.isecpartners.android.manifestexplorer");
mManifestButton.setVisibility(View.VISIBLE);
} catch (NameNotFoundException e) {
mManifestButton.setVisibility(View.GONE);
} if (null == p || null == p.activities)
return; mPkgName = p.packageName; for (ActivityInfo ai : p.activities)
if (ai.exported)
mActivitiesList.add(ai.name); ArrayAdapter<String> AA = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_dropdown_item, mActivitiesList);
mActivities.setAdapter(AA); if (mActivities.getCount() > 0)
showActivityUI(true);
} /**
* Looks at the spinner for the currently selected Activity, then starts it.
*/
public void startCurrentlySelected() {
Intent i = new Intent();
i.setComponent(new ComponentName(mPkgName, mActivities
.getSelectedItem().toString()));
startActivity(i);
}

3.Intent Sniffer

IntentSniffer.java

// classes that have broadcast actions, this list is incomplete (a start).
// These classes are not all visible to SDK, so loading them at runtime for
// reflection on the actual device.
public String[] actionHarboringClasses = {
"android.content.Intent",
"android.bluetooth.BluetoothIntent",
"android.bluetooth.BluetoothA2dp",
// Application specific, and would be dangerous to load (as their
// static initializer might run etc.)
// "com.android.mms.transaction.MessageStatusReceiver",
// "com.android.mms.transaction.SmsReceiverService",
// "com.android.internal.telephony.gsm.stk.AppInterface",
"com.android.internal.location.GpsLocationProvider",
"com.android.internal.telephony.TelephonyIntents",
"android.provider.Telephony.Intents",
"android.proivder.Contacts.Intents",
"com.android.mms.util.RateController",
"android.media.AudioManager", "android.net.wifi.WifiManager",
"android.telephony.TelephonyManager",
"android.appwidget.AppWidgetManager",
"android.net.ConnectivityManager" }; /*
* Grabs the recent tasks from the ActivityManager.
*/
protected void updateWithRecents() {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RecentTaskInfo> rti = am.getRecentTasks(1000,
ActivityManager.RECENT_WITH_EXCLUDED);
StringBuffer log = new StringBuffer();
int count = 0;
for (RecentTaskInfo c : rti) {
count++;
Intent cur = c.baseIntent;
log.append("received: " + rti.toString() + "\n");
receiveIntent(RECENT_SOURCE, cur);
Log.d(TAG, "recent intent added: " + cur.toString()
+ cur.hashCode());
} } protected void updateKnownCategories() {
Set<String> l = new HashSet<String>();
// reflect across whatever Intent class is installed on the user's
// system to grab all static "CATEGORY" fields
for (Field f : Intent.class.getFields()) {
// the Intent class has a bunch of constants named "CATEGORY_*",
// grabbing em...cheap eh.
if (f.getName().startsWith("CATEGORY")
&& f.getType() == String.class) {
try {
l.add((String) f.get(null));
} catch (IllegalAccessException e) {
// should "adjust" protection level of field and retry.
// but I believe there is no need right now. Maybe a
// future constant will be private on some platform.
Log.d(TAG, "Access error on: " + f.getName());
}
}
} mKnownCategories = l.toArray(new String[l.size()]);
saveKnownCategories();
} protected void updateKnownActions() {
Set<String> l = new HashSet<String>();
List<Class> classes = new ArrayList<Class>(); for (String cur : actionHarboringClasses)
try {
classes.add(Class.forName(cur));
} catch (ClassNotFoundException cne) {
Log.e(TAG, "missing class " + cne.getMessage());
} for (Class c : classes)
for (Field f : c.getFields()) {
// actions constants tend to start or end with ACTION
if ((f.getName().startsWith("ACTION") || f.getName().endsWith(
"ACTION"))
&& f.getType() == String.class) {
try {
l.add((String) f.get(null));
} catch (Exception e) {
// should "adjust" protection level of field and retry.
Log.d(TAG, "Access error on: " + c.getName() + " : "
+ f.getName() + "" + e.getMessage());
}
}
}
this.mNumReflected = l.size();
findMoreActions(l);
mKnownBroadcastActions = l.toArray(new String[l.size()]);
saveKnownActions();
} protected void findMoreActions(Set<String> l) {
// walk the XML registrations for every package on the system looking
// for places where broadcast receivers are registered.
for (PackageInfo pi : getPackageManager().getInstalledPackages(
PackageManager.GET_DISABLED_COMPONENTS)) {
try {
XmlResourceParser x = createPackageContext(pi.packageName, 0)
.getAssets().openXmlResourceParser(
"AndroidManifest.xml");
int eventType = x.getEventType();
// looking for receiver tag, containing action tag(s), contains
// a name attribute has a value that is the known action.
int state = 0; // 0 out, 1 in receiver
String n;
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
n = x.getName();
n = (null == n) ? "" : n.toLowerCase();
if (n.equals("receiver"))
state = 1;
else if (1 == state && n.equals("action"))
for (int i = 0; i < x.getAttributeCount(); i++)
if (x.getAttributeName(i).equalsIgnoreCase(
"name"))
l.add(x.getAttributeValue(i));
break;
case XmlPullParser.END_TAG:
n = x.getName();
n = (null == n) ? "" : n.toLowerCase();
if (n.equals("receiver"))
state = 0;
break;
}
eventType = x.nextToken();
}
} catch (IOException ioe) {
Log.e(TAG, "IOException opening package: " + pi.packageName);
} catch (NameNotFoundException name) {
Log.e(TAG, "NameNotFoundException opening package: "
+ pi.packageName);
} catch (XmlPullParserException e) {
Log.e(TAG, "Exception reading manifest XML for package: "
+ pi.packageName);
}
}
mNumDug = l.size() - mNumReflected;
}

4.Intent Fuzzer

IntentFuzzer.java

/**
* For any type, provide the registered instances based on what the package
* manager has on file. Only provide exported components.
*
* @param type
* IPC requested, activity, broadcast, etc.
* @return
*/
protected ArrayList<ComponentName> getExportedComponents(IPCType type) {
ArrayList<ComponentName> found = new ArrayList<ComponentName>();
PackageManager pm = getPackageManager();
for (PackageInfo pi : pm
.getInstalledPackages(PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_INSTRUMENTATION
| PackageManager.GET_PROVIDERS
| PackageManager.GET_SERVICES)) {
PackageItemInfo items[] = null; switch (type) {
case ACTIVITIES:
items = pi.activities;
break;
case BROADCASTS:
items = pi.receivers;
break;
case SERVICES:
items = pi.services;
break;
case PROVIDERS:
items = pi.providers;
break;
case INSTRUMENTATIONS:
items = pi.instrumentation;
} if (items != null)
for (PackageItemInfo pii : items)
found.add(new ComponentName(pi.packageName, pii.name));
} return found;
} protected int fuzzAllBroadcasts(List<ComponentName> comps) {
int count = 0;
for (int i = 0; i < comps.size(); i++) {
Intent in = new Intent();
in.setComponent(comps.get(i));
sendBroadcast(in);
count++;
}
return count;
} protected int fuzzAllServices(List<ComponentName> comps) {
int count = 0;
for (int i = 0; i < comps.size(); i++) {
Intent in = new Intent();
in.setComponent(comps.get(i));
try {
startService(in);
} catch (Exception e) {
mOut.append("Can't launch " + comps.get(i) + ""
+ e.getMessage() + "\n");
}
count++;
}
return count;
} protected boolean sendIntentByType(Intent i, IPCType t) {
try {
switch (t) {
case ACTIVITIES:
startActivity(i);
return true;
case BROADCASTS:
sendBroadcast(i);
return true;
case SERVICES:
startService(i); // stopping these might be nice too
return true;
case PROVIDERS:
// uh - providers don't use Intents...what am I doing...
Toast.makeText(this,
"Proivders don't use Intents, ignore this setting.",
Toast.LENGTH_SHORT).show();
return false;
case INSTRUMENTATIONS:
Toast
.makeText(
this,
"Instrumentations aren't Intent based... starting Instrumentation.",
Toast.LENGTH_SHORT).show();
startInstrumentation(i.getComponent(), null, null); // not
// intent based you could fuzz these params, if anyone cared.
return true;
}
} catch (Exception e) {
return false;
}
return false;
} protected Intent fuzzBroadcast(ComponentName toTest) {
Intent i = new Intent();
i.setComponent(toTest);
sendBroadcast(i);
return i;
}

5.drozer(原名Mercury)用作Android平台上动态分析的安全测试框架,其源码由Python编写,它能安装在Windows & Linux上,详见其用户指南,其研发公司MWR InfoSecurity还提供了Android 安全研究

6.androguard用Python编写,目前只能在Linux上安装使用,它安装时需要较多第三方库的支持,幻灯片Android-OEM-applications-insecurity-and-backdoors-without-permission用此工具作了分析。

7.DroidBox用Python编写,目前只能运行在Linux和Mac上,它用作对Android应用动态分析。

8.查看apk签名:

jarsigner -verify -verbose -certs apk_name.apk

或者Android APK 签名比对

9.Apk相关文件中如congfig、未加密的db中无工号、用户个人数据和系统数据、万能码之类内容、账户密码和敏感数据不要明文存储

10.Apk网络传输时不能泄露数据、不能有影响安全的开发端口,测试工具参考文章Android工具tcpdump和Nmap

11.Apk运行时Logcat输出不必要的信息

12.APK相关文件权限other组权限为---或--x,比如不含w权限:

-rw-rw--w- :data/data/com.snda.cloudary/shared_prefs/UserInfo.xml …该文件的这个权限不安全

13.Santoku

14.用至少两种杀毒软件对发布产品进行查杀

15.禁用私有加密算法和业界已知不安全加密算法,需解决兼容或标准协议规定的除外。目前较安全的加密算法为:

3DES      128及以上

AES        128及以上

RSA        1024及以上

SHA2      256及以上

HMAC-SHA2    128及以上

其它可见幻灯片Android软件安全攻防研究现状Android漏洞信息库Security Tips

16.APK禁止申请不必要权限

Android 安全测试的更多相关文章

  1. Android开源测试框架学习

    近期因工作需要,分析了一些Android的测试框架,在这也分享下整理完的资料. Android测试大致分三大块: 代码层测试 用户操作模拟,功能测试 安装部署及稳定性测试 代码层测试 对于一般java ...

  2. Android ui 测试课堂笔记

    开始接触Android ui测试了,笔记如下 模拟器 Genemotion , the fastest android simulator in the world Android ui 测试工具 S ...

  3. Android压力测试工具——Monkey

    Android压力测试工具——Monkey Monkey是运行在模拟器上和真机设备上的一段程序,它会产生用户事件的一系列伪随机流,比如点击.触摸.手势,还有很多系统级别的事件.Monkey通常是用来做 ...

  4. Android渗透测试Android渗透测试入门教程大学霸

    Android渗透测试Android渗透测试入门教程大学霸 第1章  Android渗透测试 Android是一种基于Linux的自由及开放源代码的操作系统,主要用于移动设备,如智能手机.平板等.目前 ...

  5. 监听Android CTS测试项解决方案(一)

    前言: 首先这里需要详细叙述一下标题中"监听Android CTS测试项解决方案"的需求.这里的需求是指我们需要精确的监听到当前CTS测试正在测试的测试项. 因为我们知道CTS认证 ...

  6. Android测试(一):在Android中测试App

    原文:https://developer.android.com/training/testing/index.html 测试你的App是开发过程中的重要组成部分.通过对应用程序持续的运行测试,你可以 ...

  7. Google+ 团队的 Android UI 测试

    https://github.com/bboyfeiyu/android-tech-frontier/tree/master/android-blog/Google%2B%20%E5%9B%A2%E9 ...

  8. [转]Android开源测试框架学习

    近期因工作需要,分析了一些Android的测试框架,在这也分享下整理完的资料. Android测试大致分三大块: 代码层测试 用户操作模拟,功能测试 安装部署及稳定性测试 代码层测试 对于一般java ...

  9. 20162311 编写Android程序测试查找排序算法

    20162311 编写Android程序测试查找排序算法 一.设置图形界面 因为是测试查找和排序算法,所以先要有一个目标数组.为了得到一个目标数组,我设置一个EditText和一个Button来添加数 ...

  10. 移动性能测试 | 持续集成中的 Android 稳定性测试

    前言 谈到Android稳定测试,大多数会联想到使用monkey工具来做测试.google官方提供了monkey工具,可以很快速点击被应用,之前我有一篇帖子提到了monkey工具的使用,详见: htt ...

随机推荐

  1. 10.5 noip模拟试题

    2bc*cosA=b^2+c^2-a^2 数学题QAQ 开始π精度不够40分 怪我喽~ #include<iostream> #include<cstdio> #include ...

  2. html获取gps坐标

    <script> function getLocation(){ var options={ enableHighAccuracy:true, maximumAge:1000 } if(n ...

  3. 2.添加键盘钩子。向进程中注入dll

    学习笔记 1.首先要建立mfc的动态链接库.在def文件中放入要导出的函数名. 2.添加函数如下 //安装钩子 //HHOOK SetWindowsHookEx( // int idHook,//钩子 ...

  4. C# 霍夫曼二叉树压缩算法实现

    知道有的人比较懒,直接贴全部代码. 一开始一次性Code完了压缩部分代码.只调试了2,3次就成功了. 一次性写150行代码,没遇到什么bug的感觉还是蛮爽的. 写解压代码,才发现压缩代码有些细节问题. ...

  5. 利用抽象、多态实现无反射的绿色环保ORM框架

    最近一直在忙新公司的基础库建设,对系统架构.开发框架及快速开发平台的设计实施都积累了一定的实践经验. 一般的中小型的软件开发公司,如果按照技术储备来衡量软件项目的技术含量的评定依据是可行的.但如果光是 ...

  6. [转帖]SD卡&FLASH&USB

    来源:http://www.cypress.com Cypress官网,了解任何芯片都应该从它的官网入手,资料一定是最多最原始的,像Ronnie学习. Cypress’s EZ-USB® FX2LP™ ...

  7. Android布局管理器(贞布局)

    贞布局有FrameLayout所代表,它直接继承了ViewGroup组建 贞布局为每个加入其中的组件创建一个空白区域(一帧),所以每个子组件占用一帧,这些贞都会根据gravity属性执行自动对齐 贞布 ...

  8. 极端气候频现 五款开发天气预报应用的API

    http://www.csdn.net/article/2014-02-07/2818322-weather-forecast-api-for-developing-apps

  9. SQL三大范式

    第一范式:确保每列的原子性. 如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式. 例如:顾客表(姓名.编号.地址.……)其中"地址"列还可 ...

  10. [转]PHP echo, print, printf, sprintf函数的区别和使用

    1. echo函数: 输出函数,是命令,不能返回值.echo后面可以跟很多个参数,之间用分号隔开,如: echo $myvar1; echo 1,2,$myvar,"<b>bol ...