本文博客地址:http://blog.csdn.net/qq1084283172/article/details/71037182

一、环境条件

Ubuntukylin 14.04.5 x64bit

Android 4.4.4

Nexus 5

二、Android内核源码的下载

执行下面的命令,获取 Nexus 5手机 设备使用的芯片即获取Nexus 5手机设备内核源码的版本信息。

$ adb shell  

# 查看移动设备使用的芯片信息
$ ls /dev/block/platform

执行的结果,如下图所示:

根据google官方的参考文档以及上面获取的Nexus 5手机设备芯片信息得到Nexus 5手机的内核源码的下载地址,具体的执行下面的命令:

$ git clone https://aosp.tuna.tsinghua.edu.cn/kernel/msm.git (清华的源)
# 或者
$ git clone https://android.googlesource.com/kernel/msm.git (或者谷歌官方的源需要翻墙) $ cd msm
# 查看可以下载的Linux内核源码的版本
$ git branch -a

Nexus 5手机内核源码版本的下载,需要根据Nexus 5手机的内核的版本信息来确定,具体的执行下面的命令:

$ adb shell

# 查看移动设备的内核版本
$ cat /proc/version
Linux version 3.4.0-gd59db4e (android-build@vpbs1.mtv.corp.google.com) (gcc version 4.7 (GCC) ) #1 SMP PREEMPT Mon Mar 17 15:16:36 PDT 2014

当然了,直接在 Nexus 5手机上,打开 关于手机选项,查看Nexus 5手机的内核版本信息也是可以的。 3.4.0-g 后面的7位十六进制数字 d59db4e
非常重要,使用这个字符串就可以 check out 出准确的commit。该内核版本字符串引用正好是AOSP的GIT仓库中的某个commit的哈希值,因此只要是使用google官方提供的Android内核源码的设备就可以通过该内核版本字符串下载到对应的Android内核的源码。执行下面的命令下载Nexus 5手机设备对应的Android内核源码:

# 下载对应的Android内核源码
$ git checkout d59db4e

Android内核源码下载好了,还需要下载编译Android内核源码的交叉编译工具链 arm-eabi-4.7 。为了方便起见,交叉编译工具链 arm-eabi-4.7 下载好以后,添加arm-eabi-4.7到 ubuntu14.04.5 x64bit 的系统环境变量中,具体的执行下面的命令:

# 下载编译工具链
$ git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/
# 或者
$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/ # 添加arm-eabi-4.7到系统环境变量中
$ sudo gedit /etc/profile # 添加到环境变量配置文件/etc/profile中的内容
export ANDROID_TOOLCHAIN=/home/fly2016/Desktop/Android4.4.4r1/android-4.4.4_r1/kernel_d59db4e/msm/arm-eabi-4.7
export PATH=$PATH:${ANDROID_TOOLCHAIN}/bin/ # 更新系统环境变量
$ source /etc/profile # 测试是否配置成功
$ arm-eabi-gdb

三、Android内核源码的编译
执行下面的命令,进行Android内核编译的配置,具体如下所示:

# 配置编译环境变量
$ export CROSS_COMPILE=arm-eabi-
$ export ARCH=arm
$ export SUBARCH=arm # 生成编译配置文件.config
$ make hammerhead_defconfig # 编辑编译配置文件.config
$ gedit .config

为了使Android内核支持自定义内核模块的加载、卸载和对Android内核内存空间的修改以支持对Android系统调用的Hook操作,需要对Android内核的编译配置文件.config 进行修改,具体的修改如下图:

对Android内核编译配置文件 .config 的修改如下:

# 需要修改
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_STRICT_MEMORY_RWX=n # 不需要修改
CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y

保存、关闭Android内核编译配置文件 .config ,执行下面的命令进行Android内核的编译。在编译的时候会有对Android内核编译选项的设置,当遇到我们上面设置的内核编译选项时,还是根据Android系统调用Hook的要求来。因为默认情况下Android内核为了移动设备的安全是没有开启这些选项,只有设置了这些选项,才能加载自定义的Android内核模块,实现对Android系统调用的Hook操作。

# 编译Android内核
$ make -j4

编译Android内核时遇到下面编译选项设置的情况,做如下的正确设置,其他的参数选项使用系统默认值,不作任何的修改。

有关Android内核支持自定义内核模块加载和卸载的设置,如下所示:


重要提示

CONFIG_MODVERSIONS 和 CONFIG_MODULE_SRCVERSION_ALL 这两个选项一定不能配置,需要去掉,否则的话:在Android系统加载自定义内核模块时会对内核模块进行代码的校验和版本的检查,容易出现如下的错误。具体的出错原因可以参考博文《内核模块编译时怎样绕过insmod时的版本检查》。

有关Android内核内存空间读写情况的设置,如下所示:

Android内核编译配置文件 .config 经过修改后的结果如下图:

对自定义内核模块加载支持的设置

对Android内核内存空间可读可写支持的设置

Android内核源码编译成功以后会生成内核引导模块文件 /msm/arch/arm/boot/zImage-dtb ,操作结果如下图:

四、替换Nexus 5 手机的内核并启动新内核

由于Nexus 5手机是高通的设备,因此可以执行下面的命令查找到启动分区 boot 的镜像位置。

$ adb shell  

# msm 代表高通的芯片,msm_sdcc.1是外接的SD卡挂载的目录,by-name指的是这个sd卡分区的名称
$ ls -al /dev/block/platform/msm_sdcc.1/by-name/

执行结果,如下图所示:



root权限下 ,将boot镜像所有的内容转储到Nexus 5手机的 /sdcard/boot.img 文件夹下,然后导出到 Ubuntu 14.04.5 x64bit 系统主机上,具体的执行下面的命令:

$ adb shell "su -c dd if=/dev/block/mmcblk0p19 of=/sdcard/boot.img"  

$ adb pull /sdcard/boot.img  ./  

使用 abootimg工具 对导出的 boot.img 镜像文件进行解包,然后替换替换掉Android内核镜像文件,重新打包生成新的 boot.img文件,使用 fastboot工具 将新的boot.img镜像文件刷入到Nexus 5设备上引导内核启动,执行的命令如下:

# 安装刷机工具fastboot和adb
$ sudo apt-get install android-tools-adb android-tools-fastboot # 安装boot.img文件解包和打包工具abootimg
$ sudo apt-get install build-essential abootimg # 对boot.img文件进行解包
$ abootimg -x boot.img

导出的boot.img文件解包的结果,如下图所示:

将编译生成的 新内核文件 msm/arch/arm/boot/zImage-dtb 替换掉原来的Android内核镜像文件,重新打包生成新的boot.img文件,然后重启Nexus 5手机进入刷机模式,用 “fastboot boot” 命令引导Android的新内核,执行下面的命令:

# 拷贝编译生成的Android内核文件zImage-dt到当前目录下
$ cp ~/msm/arch/arm/boot/zImage-dtb . # 重新打包生成新的boot.img文件
$ abootimg --create myboot.img -f bootimg.cfg -k zImage-dtb -r initrd.img # 重启手机设备进入刷机模式
$ adb reboot bootloader # 刷新boot.img文件,引导新的Android内核
$ fastboot boot myboot.img

fastboot boot 更新Nexus 5手机的内核成功后,重启手机设备。为了快速验证新的Android内核正确运行了,通过校验Settings->About phone中的“内核版本”的值,如下图。如果一切运行良好的话,内核版本下面将 显示自定义构建的版本字符串 ,结果如下图所示:

五、自定义加载Android内核模块Hook系统调用

在我们自定义的内核中,能用LKM加载自定义的代码到内核中,也可以访问/dev/kmem接口,用来修改Android内核的内存,Hook Android内核系统的调用都是基于这些前提条件实现的。有关Hook Android系统调用的原理和详细描述,可以参考前面的博文《Hook
android系统调用研究(一)
》。

在进行Hook Android系统调用之前,需要 先找到的是 sys_call_table的地址 。root权限下,通过 /proc/kallsyms 可以寻找到 sys_call_table的地址,具体的执行下面的命令:

# 获取root权限
$ adb shell su # 查看默认值
$ cat /proc/sys/kernel/kptr_restrict # root权限下,关闭symbol符号屏蔽
# 将 /proc/sys/kernel/kptr_restrict 重置为0,就可以打印显示出来
$ echo 0 > /proc/sys/kernel/kptr_restrict # 查看修改后的值
$ cat /proc/sys/kernel/kptr_restrict # 获取 sys_call_table的内存地址
$ cat /proc/kallsyms | grep sys_call_table
c000f884 T sys_call_table

执行操作的结果如下图:

sys_call_table=0xc000f884 即为需要找到的Android系统调用表的内存基址,后面很多Android系统调用的系统函数调用地址都需要通过这个基址加函数的偏移计算出来。下面以使用
Android内核模块隐藏一个文件 为例子进行学习,先在设备上创建一个文件,方便我们能在后面隐藏它:

$ adb shell su

# 创建文件nowyouseeme并输入内容HelloWorld
$ echo HelloWorld > /data/local/tmp/nowyouseeme # 显示新创建文件的内容
$ cat /data/local/tmp/nowyouseeme
HelloWorld

为了隐藏文件,需要Hook用来打开文件的一个Android系统调用。有很多关于打开文件操作的系统调用,如 open, openat, access,accessat, facessat, stat, fstat 等等。这里只需要
挂钩 openat 系统调用 ,这个系统调用被 "/bin/cat" 程序 访问文件时被调用。当 openat系统调用 被Hook以后,就可以进行文件显示的过滤,需要隐藏的文件就不显出来。

在Android内核源码的头文件(arch/arm/include/asm/unistd.h)中找到Android所有系统调用的函数原型,然后创建一个挂钩Android系统调用
openat 的代码文件 kernel_hook.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <asm/uaccess.h> asmlinkage int (*real_openat)(int, const char __user*, int); void **sys_call_table; // 替换Android系统调用的新的new_openat函数
int new_openat(int dirfd, const char __user* pathname, int flags)
{
char *kbuf;
size_t len; // 在内核中申请内存空间
kbuf=(char*)kmalloc(256, GFP_KERNEL);
// 获取需要打开的文件的文件路径
len = strncpy_from_user(kbuf, pathname,255); // 过滤,隐藏掉/data/local/tmp/nowyouseeme文件
if (strcmp(kbuf, "/data/local/tmp/nowyouseeme") == 0)
{
printk("Hiding file!\n"); return -ENOENT;
} // 释放申请的内存空间
kfree(kbuf); // 调用Android系统原来的系统调用openat函数
return real_openat(dirfd, pathname, flags);
} // ########### 将被加载的Android内核模块 ###############
int init_module(void) { // 前面查找的内存地址
sys_call_table = (void*)0xc000f884; // 获取Android系统的openat函数的调用地址
real_openat = (void*)(sys_call_table[__NR_openat]); return 0;
}

为了编译 kernel_hook.c文件 需要配置Android内核源码文件路径和交叉编译工具链路径,Makefile文件
的编写如下:

KERNEL=/home/fly2016/Android4.4.4r1/android-4.4.4_r1/kernel_d59db4e/msm  

TOOLCHAIN=arm-eabi-  

obj-m := kernel_hook.o  

all:
make ARCH=arm CROSS_COMPILE=$(TOOLCHAIN) -C $(KERNEL) M=$(shell pwd) CFLAGS_MODULE=-fno-pic modules clean:
make -C $(KERNEL) M=$(shell pwd) clean

提示
Android内核模块与内核是紧密联系的,由于内核模块也处于Android内核空间,一旦发生问题就会直接造成严重的系统崩溃,因此
编译Android自定义内核模块时需要Android内核的相关信息 。一般的流程是:首先编译内核,之后根据得到的内核配置符号信息等再编译自定义内核模块。这也意味着,当系统内核更新后,外部模块往往需要重新编译以兼容新内核。Linux系统下可通过 Dynamic Kernel Module Support (DKMS) 自动重新编译内核模块。

前面Andorid内核源码的编译配置环境下,继续直接执行 make 命令就可以编译 kernel_hook.c文件生成
内核模块文件kernel_hook.ko 。如果前面的Android内核编译环境丢失,可以通过在Android内核源码的根目录下,执行下面的命令进行 kernel_hook.c文件的编译。和其他的Linux发行版类似,在编译自定义内核模块之前无需编译整个Android内核,只要生成编译内核模块必要的脚本和头文件就行

# 配置编译环境
$ export ARCH=arm
$ export SUBARCH=arm
$ export CROSS_COMPILE=arm-eabi- # 生成编译配置文件.config
$ make hammerhead_defconfig # 修改编译配置文件.config
$ gedit .config ###################################################
# 修改.config编译配置文件,保存、关闭
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_STRICT_MEMORY_RWX=n CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
################################################### # 生成内核编译需要的脚本和头文件
$ make prepare modules_prepare # 或者
$ make prepare
$ make scripts
################################################### # 切换到工作目录编译 kernel_hook.c 文件
$ make

自定义内核模块编译成功后的结果截图,如下所示:

拷贝 kernel_hook.ko文件 到Nexus 5手机设备的 /data/local/tmp/ 目录下并在 root权限下 ,用 insmod
命令 加载编译后的内核模块 kernel_hook.ko 。用 lsmod 命令 查看该内核模块是否加载成功。

# 查看编译的Android内核模块文件的版本信息
$ modinfo kernel_hook.ko $ adb shell rm /data/local/tmp/kernel_hook.ko # 拷贝 kernel_hook.ko文件 到移动设备的/data/local/tmp/目录下
$ adb push kernel_hook.ko /data/local/tmp/ # root权限下,加载自定义的内核模块kernel_hook.ko
$ adb shell su -c insmod /data/local/tmp/kernel_hook.ko # 查看自定义内核模块是否加载成功
$ adb shell lsmod

执行的操作结果,如下图所示:

六、修改Android的系统调用表

通过访问 /dev/kmem接口 将Hook的
新函数地址new_openat
来覆盖sys_call_table中的原始函数openat的调用地址(这也能直接在内核模块中做,但是用/dev/kmem更加简单)。在参考了Dong-Hoon You的文章后,决定使用文件接口代替nmap(),因为经过试验发现会引起一些内核警告。用下面代码创建文件
kmem_util.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <asm/unistd.h>
#include <sys/mman.h> #define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1) // 保存内存文件的句柄
int kmem; // 读取内存文件中的数据
void read_kmem2(unsigned char *buf, off_t off, int sz)
{
off_t offset;
ssize_t bread; // 设置内存文件的偏移在(从文件头开始)
offset = lseek(kmem, off, SEEK_SET);
// 读取内存文件的数据
bread = read(kmem, buf, sz); return;
} // 向内存文件写入数据
void write_kmem2(unsigned char *buf, off_t off, int sz)
{
off_t offset;
ssize_t written; // 设置内存文件的偏移
offset = lseek(kmem, off, SEEK_SET);
// 向内存文件写入数据
if (written = write(kmem, buf, sz) == -1)
{
perror("Write error");
exit(0);
} return;
} // 主函数
int main(int argc, char *argv[])
{ off_t sys_call_table;
unsigned int addr_ptr, sys_call_number; // 对传入的参数的个数进行校验,不能少于3个
if (argc < 3)
{
return 0;
} // 打开内核文件接口/dev/kmem
kmem = open("/dev/kmem", O_RDWR);
// 判断文件是否打开成功
if(kmem < 0)
{
perror("Error opening kmem");
return 0;
} // 获取输入的sys_call_table地址
sscanf(argv[1], "%x", &sys_call_table);
// 获取Android系统调用openat函数的偏移值
sscanf(argv[2], "%d", &sys_call_number);
// 获取新的new_openat的调用地址
sscanf(argv[3], "%x", &addr_ptr); char buf[256];
// 内存清零
memset(buf, 0, 256); // 获取Android系统调用openat函数的原始调用地址
read_kmem2(buf, sys_call_table+(sys_call_number*4), 4);
// 打印Android系统调用openat函数的原始调用地址
printf("Original value: %02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // 将Android系统调用的openat函数的原始调用地址替换为新的new_openat的调用地址
write_kmem2((void*)&addr_ptr,sys_call_table+(sys_call_number*4), 4);
// 获取替换后的新new_openat函数的调用地址
read_kmem2(buf,sys_call_table+(sys_call_number*4), 4);
// 打印替换后的new_openat函数的调用地址
printf("New value: %02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // 关闭文件
close(kmem); return 0;
}

编译构建 kmem_util.c文件 并拷贝编译后的文件 kmem_util 到Nexus 5手机设备的 /data/local/tmp/ 目录下。编译时生成可执行文件必须是
PIE支持编译的,需要添加编译选项 -pie -fpie。直接使用 /arm-eabi-4.7/bin/arm-eabi-gcc编译工具 对 kmem_util.c文件 进行编译也是可以的,但是提示缺少系统头文件,为了不破坏Android源码的编译环境。这里使用
Adt-bundle-x86_64 对 kmem_util.c文件 进行 Android NDK 的编译,编译配置文件 Android.mk
的编写如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := kmem_util
LOCAL_SRC_FILES := kmem_util.c LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE include $(BUILD_EXECUTABLE)

kmem_util.c文件 编译成功以后的结果截图如下:

执行下面的命令,拷贝文件 kmem_util 到Nexus 5手机设备上。

# 拷贝文件kmem_util到手机设备上
$ adb push kmem_util /data/local/tmp/ # 赋予文件可执行权限0755
$ adb shell chmod 755 /data/local/tmp/kmem_util

在开始修改Android内核内存之前,需要先知道 Android系统调用表 中
openat函数 的正确调用偏移位置。openat系统调用在Android内核源码的 arch/arm/include/asm/unistd.h
中定义的,在Android内核源码的根目录下,执行下面的命令获取 openat系统调用 的调用偏移位置。

$ grep -r "__NR_openat" arch/arm/include/asm/unistd.h
#define __NR_openat (__NR_SYSCALL_BASE+322)

从上面的操作结果获取到 openat系统调用的偏移量为322。自定义内核模块kernel_hook.ko已经加载到Android内核内存中,通过符号文件 /proc/kallsyms
可以得到 new_openat函数的调用地址,然后使用该new_openat函数的调用地址替换原来openat函数的调用地址。

$ adb shell cat /proc/kallsyms | grep new_openat
bf000000 t new_openat [kernel_hook]

执行操作的结果截图,如下所示:



现在可以 覆盖Android内核内存中系统调用函数的调用地址了 ,kmem_util可执行程序 的用法如下:

./kmem_util <syscall_table_base_address> <offset> <new_fun_addr>

在root权限下,执行下面的命令 修改Android系统调用表中的系统函数调用地址,将系统函数调用地址替换为我们自定的Hook函数的调用地址。具体修改Android系统调用函数openat地址的示例,执行下面的命令:

$ adb shell su -c /data/local/tmp/kmem_util c000f884 322 bf000000
Original value: c01734b4
New value: bf000000

执行操作的结果截图,如下所示:



在root权限下,执行 /bin/cat 检查是否Hook Android系统调用函数openat成功。如果成功的话,执行 /bin/cat 不会显示我们隐藏的文件/data/local/tmp/nowyouseeme。具体的执行下面的命令:

$ adb shell su -c cat /data/local/tmp/nowyouseeme  

tmp-mksh: cat: /data/local/tmp/nowyouseeme: No such file or directory

执行操作的结果截图,如下所示:



七、总结

本篇博文是在前面博文《Hook android系统调用研究(一)》的基础上进行实践和查错总结写出来的,非常遗憾的是在最后关键验证Hook是否成功的步骤上再一次出现错误,猜测可能还是前面的Hook代码或者操作步骤的细节上有问题,没有注意到,后面有时间会再进行研究。这篇博文最原始的参考还是文章《hook
Android系统调用的乐趣和好处
》,虽然原文中有不少的错误,但是还是值得研究和实践,前面也写过这篇博文的实践但是各种错误和问题,只实践了一半就继续不下去了,本篇博文中一一将前面的遇到的问题都给解决了,遗憾的是还是失败了~

Hook android系统调用的实践的更多相关文章

  1. Hook android系统调用研究(一)

    本文的博客链接:http://blog.csdn.net/qq1084283172/article/details/55657300 一.Android内核源码的编译环境 系统环境:Ubuntu 14 ...

  2. hook Android系统调用的乐趣和好处

    翻译:myswsun 0x00 前言 Android的内核是逆向工程师的好伙伴.虽然常规的Android应用被限制和沙盒化,逆向工程师可以按自己希望自定义和改变操作系统和内核中行为.这给了你不可多得的 ...

  3. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

  4. Android游戏开发实践(1)之NDK与JNI开发01

    Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码&q ...

  5. Android游戏开发实践(1)之NDK与JNI开发02

    Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...

  6. Android软件安全开发实践(下)

    Android开发是当前最火的话题之一,但很少有人讨论这个领域的安全问题.本系列将分两期,探讨Android开发中常见的安全隐患和解决方案.第一期将从数据存储.网络通信.密码和认证策略这三个角度,带你 ...

  7. 使用Cydia Substrate 从Native Hook Android Native世界

    同系列文章: 使用Cydia Substrate 从Native Hook Android Java世界 使用Cydia Substrate Hook Android Java世界 一.建立工程 手机 ...

  8. 一个快速、完善的Android开发框架整合实践(QuickAndroid)

    https://github.com/alafighting/QuickAndroid QuickAndroid 一个快速.完善的Android开发框架整合实践 QA项目简介 本框架QuickAndr ...

  9. Android游戏开发实践(1)之NDK与JNI开发04

    Android游戏开发实践(1)之NDK与JNI开发04 有了前面几篇NDK与JNI开发相关基础做铺垫,再来通过代码说明下这方面具体的操作以及一些重要的细节.那么,就继续NDK与JNI的学习总结. 作 ...

随机推荐

  1. #progma pack(x)说明

    1.字节对齐(内存相关) 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数 ...

  2. 在scanf函数中占位符使用错误而产生的一些错误

    出现的问题 在做编程题的的时候,遇到了一个很奇怪的错误,出问题的代码如下: 1 #include <cstdio> 2 using namespace std; 3 4 int main( ...

  3. 动态规划-最长公共上升子序列-n^2解法

    1. 题目描述 给定两个数列\(A, B\),如果他们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列.求\(A\)和\(B\)的最长公共上升子序列. 输入格 ...

  4. 如何让python脚本支持命令行参数--getopt和click模块

    一.如何让python脚本支持命令行参数 1.使用click模块 如何使用这个模块,在我前面的博客已经写过了,可参考:https://www.cnblogs.com/Zzbj/p/11309130.h ...

  5. Vue.js 学习笔记之七:使用现有组件

    5.3 使用现有组件 在之前的五个实验中,我们所演示的基本都是如何构建自定义组件的方法,但在具体开发实践中,并非项目中所有的组件都是需要程序员们自己动手来创建的.毕竟在程序设计领域,"不要重 ...

  6. for-in 语句

    for-in 语句循环专门用于遍历范围,列表,元素和字典等可迭代对象. 循环中的变量的值受for-in循环控制,该变量将会在每次循环开始时自动被赋值,因此程序不应该在循环中对该变量进行赋值 for-i ...

  7. Python数据分析入门(一):搭建环境

    Python版本: 本课程用到的Python版本都是3.x.要有一定的Python基础,知道列表.字符串.函数等的用法. Anaconda: Anaconda(水蟒)是一个捆绑了Python.cond ...

  8. Java中的集合Set - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的集合Set - 入门篇>,希望对大家有帮助,谢谢 简介 前面介绍了集合List,映射Map,最后再简单介绍下集合Set,相关类如下 ...

  9. 显示IPC信息--ipcs

    ipcs                                       显示共享内存,消息队列, 信号量全部的IPC ipcs -q                            ...

  10. 高精度减法(c++)

    高精度减法 每当要进行精度较高的运算时,就要用到高精度. 下图是各个类型的数值范围: 如果想不起各个类型占多少字节,可以采用下面的方法: printf("%d %d",sizeof ...