diff --git a/README.md b/README.md index abae94b..a6594cf 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,13 @@ Well done you did it! The last step is to star this repo :smile ## Supported ROM image types -| Image Type | file names | | +| Image Type | file names | platforms | | --------------- | ----------------------------------- | ---- | -| boot images | boot.img, vendor_boot.img | | -| recovery images | recovery.img, recovery-two-step.img | | -| vbmeta images | vbmeta.img, vbmeta_system.img etc. | | -| dtbo images | dtbo.img | | +| boot images | boot.img, vendor_boot.img | all | +| recovery images | recovery.img, recovery-two-step.img | all | +| vbmeta images | vbmeta.img, vbmeta_system.img etc. | all | +| dtbo images | dtbo.img | linux & mac | +| sparse images | system.img, vendor.img, product.img etc. | linux & mac | Please note that the boot.img MUST follows AOSP verified boot flow, either [Boot image signature](https://source.android.com/security/verifiedboot/verified-boot#signature_format) in VBoot 1.0 or [AVB HASH footer](https://android.googlesource.com/platform/external/avb/+/master/README.md#The-VBMeta-struct) (a.k.a. AVB) in VBoot 2.0. diff --git a/aosp/dispol/Makefile b/aosp/dispol/Makefile new file mode 100644 index 0000000..0987b9a --- /dev/null +++ b/aosp/dispol/Makefile @@ -0,0 +1,24 @@ +.PHONY: all checkpolicy libsepol prepare +nothing: + @echo "Nothing to do" + +checkpolicy: export CFLAGS := -g -Wall -Werror -Wshadow -pipe -fno-strict-aliasing -I$(CURDIR)/libsepol-3.2/include +checkpolicy: export LIBSEPOLA := $(CURDIR)/libsepol-3.2/src/libsepol.a +checkpolicy: export LDLIBS_LIBSEPOLA := -l:libsepol.a -L$(CURDIR)/libsepol-3.2/src +checkpolicy: libsepol + make -C checkpolicy-3.2 -j + cp checkpolicy-3.2/test/dispol . + +libsepol: + make -C libsepol-3.2 -j + +prepare: + rm -fr libsepol-3.2 checkpolicy-3.2 + wget https://github.com/SELinuxProject/selinux/releases/download/3.2/libsepol-3.2.tar.gz + wget https://github.com/SELinuxProject/selinux/releases/download/3.2/checkpolicy-3.2.tar.gz + tar xaf checkpolicy-3.2.tar.gz + tar xaf libsepol-3.2.tar.gz + +all: checkpolicy libsepol prepare +# vim:ft=make +# diff --git a/aosp/dispol/README.md b/aosp/dispol/README.md new file mode 100644 index 0000000..9a75fbf --- /dev/null +++ b/aosp/dispol/README.md @@ -0,0 +1,7 @@ +# dispol +decompile binary selinux policy file +Android sepolicy file is `/sys/fs/selinux/policy` +``` +make all +./dispol +``` diff --git a/bbootimg/src/main/kotlin/avb/Avb.kt b/bbootimg/src/main/kotlin/avb/Avb.kt index 47adf49..5414e71 100644 --- a/bbootimg/src/main/kotlin/avb/Avb.kt +++ b/bbootimg/src/main/kotlin/avb/Avb.kt @@ -484,7 +484,7 @@ class Avb { } } if (-1 == seq) { - log.warn("main vbmeta doesn't have $partitionName hashDescriptor, skip") + log.warn("main vbmeta doesn't have $partitionName hashDescriptor, won't update vbmeta.img") } else { val hd = newHashDesc.auxBlob!!.hashDescriptors.get(0).apply { this.sequence = seq } this.auxBlob!!.hashDescriptors.add(hd) diff --git a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt index eb7d8e7..0a5f957 100644 --- a/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt +++ b/bbootimg/src/main/kotlin/bootimg/cpio/AndroidCpio.kt @@ -180,6 +180,7 @@ class AndroidCpio { companion object { private val log = LoggerFactory.getLogger(AndroidCpio::class.java) + private val PERM_MASK = java.lang.Long.valueOf("777", 8) fun decompressCPIO(cpioFile: String, outDir: String, fileList: String? = null) { run { //clean up if (File(outDir).exists()) { @@ -222,7 +223,7 @@ class AndroidCpio { } else { Files.setPosixFilePermissions( Paths.get(outEntryName), - Helper.modeToPermissions((entry.mode and 0xfff).toInt()) + Helper.modeToPermissions((entry.mode and PERM_MASK).toInt()) ) } } @@ -232,7 +233,7 @@ class AndroidCpio { if (!EnvironmentVerifier().isWindows) { Files.setPosixFilePermissions( Paths.get(outEntryName), - Helper.modeToPermissions((entry.mode and 0xfff).toInt()) + Helper.modeToPermissions((entry.mode and PERM_MASK).toInt()) ) } else { //Windows diff --git a/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt b/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt index 75efd5b..e1bb47b 100644 --- a/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt +++ b/bbootimg/src/main/kotlin/kernel_util/KernelExtractor.kt @@ -29,7 +29,7 @@ class KernelExtractor { it.addArgument("--output-version") it.addArgument(kernelVersionFile) } - DefaultExecutor().let { + DefaultExecutor().let { it -> it.workingDirectory = workDir ?: File("../") try { it.execute(cmd) @@ -41,6 +41,11 @@ class KernelExtractor { ret.add(kernelVersionFile) ret.add(kernelConfigFile) } catch (e: org.apache.commons.exec.ExecuteException) { + listOf(kernelConfigFile, kernelVersionFile).forEach { fn -> + File(fn).let { f -> + if (f.exists()) f.delete() + } + } log.warn("can not parse kernel info") } } diff --git a/bbootimg/src/test/kotlin/CVEtest.kt b/bbootimg/src/test/kotlin/CVEtest.kt new file mode 100644 index 0000000..6a064ff --- /dev/null +++ b/bbootimg/src/test/kotlin/CVEtest.kt @@ -0,0 +1,97 @@ +import org.junit.Test +import java.math.BigInteger +import java.util.concurrent.Callable +import java.util.regex.Matcher +import java.util.regex.Pattern + +class CVEtest { + private val printkFormat = """ +0xffffff8008cce7ee : "Rescheduling interrupts" +0xffffff8008cce806 : "Function call interrupts" +0xffffff8008cce81f : "CPU stop interrupts" +0xffffff8008cce833 : "CPU stop (for crash dump) interrupts" +0xffffff8008cce858 : "Timer broadcast interrupts" +0xffffff8008cce873 : "IRQ work interrupts" +0xffffff8008cce887 : "CPU wake-up interrupts" +0xffffff8009070140 : "rcu_sched" +0xffffff8009070500 : "rcu_bh" +0xffffff8009070920 : "rcu_preempt" +""" + + private val printkFormatPatch = """ +0x0 : "Rescheduling interrupts" +0x0 : "Function call interrupts" +0x0 : "CPU stop interrupts" +0x0 : "Timer broadcast interrupts" +0x0 : "IRQ work interrupts" +0x0 : "CPU wake-up interrupts" +0x0 : "CPU backtrace" +0x0 : "rcu_bh" +0x0 : "rcu_preempt" +0x0 : "rcu_sched" +""".trimIndent() + + @Test + fun testPocCVE_2017_0630() { + val printkFormats: String = printkFormatPatch + val pointerStrings = printkFormats.split("\n").toTypedArray() + assertNotKernelPointer(object : Callable { + var index = 0 + override fun call(): String? { + while (index < pointerStrings.size) { + val line = pointerStrings[index] + val pattern = "0x" + val startIndex = line.indexOf(pattern) + if (startIndex == -1) { + index++ + continue + } + return line.substring(startIndex + pattern.length) + } + return null + } + }, null) + } + + fun assertNotKernelPointer(getPtrFunction: Callable, deviceToReboot: String?) { + var ptr: String? = null + for (i in 0..3) { // ~0.4% chance of false positive + ptr = getPtrFunction.call() + if (ptr == null) { + return + } + if (!isKptr(ptr)) { + // quit early because the ptr is likely hashed or zeroed. + return + } + } + throw IllegalArgumentException("\"$ptr\" is an exposed kernel pointer.") + } + + private fun isKptr(ptr: String): Boolean { + val RADIX_HEX = 16 + val m: Matcher = Pattern.compile("[0-9a-fA-F]*").matcher(ptr) + if (!m.find() || m.start() != 0) { + // ptr string is malformed + return false + } + val length: Int = m.end() + if (length == 8) { + // 32-bit pointer + val address = BigInteger(ptr.substring(0, length), RADIX_HEX) + // 32-bit kernel memory range: 0xC0000000 -> 0xffffffff + // 0x3fffffff bytes = 1GB / 0xffffffff = 4 GB + // 1 in 4 collision for hashed pointers + return address >= BigInteger("C0000000", RADIX_HEX) + } else if (length == 16) { + // 64-bit pointer + val address = BigInteger(ptr.substring(0, length), RADIX_HEX) + // 64-bit kernel memory range: 0x8000000000000000 -> 0xffffffffffffffff + // 48-bit implementation: 0xffff800000000000; 1 in 131,072 collision + // 56-bit implementation: 0xff80000000000000; 1 in 512 collision + // 64-bit implementation: 0x8000000000000000; 1 in 2 collision + return address >= BigInteger("ff80000000000000", RADIX_HEX) + } + return false + } +} \ No newline at end of file diff --git a/doc/additional_tricks.md b/doc/additional_tricks.md index 2fac77a..35034b3 100644 --- a/doc/additional_tricks.md +++ b/doc/additional_tricks.md @@ -65,6 +65,9 @@ place 'ramdisk.img.gz' in directory, delete "root/", program will use it as preb ## cpio decompress cpio with commandline `cpio -idmv -F ` +Some file system(also java) doesn't support special file permissions, https://docs.oracle.com/cd/E19455-01/805-7229/secfiles-69/index.html +So we have to save the file perms in `build/unzip_boot/ramdisk_filelist.txt`, and use it when doing 'pack'. + ### cpio on windows * got `java.nio.file.FileSystemException` and says "A required privilege is not held by the client" ``` @@ -87,4 +90,3 @@ out/host/linux-x86/bin/avbtool add_hash_footer --image out/target/product/vsoc_a ``` As it's only used for GKI verification, I don't want to spend too much time on any special steps in 'gradle pack' flow, as long as DUT can boot up properly. - diff --git a/helper/src/main/kotlin/cfig/helper/Helper.kt b/helper/src/main/kotlin/cfig/helper/Helper.kt index 1652b75..f44c412 100644 --- a/helper/src/main/kotlin/cfig/helper/Helper.kt +++ b/helper/src/main/kotlin/cfig/helper/Helper.kt @@ -93,7 +93,7 @@ class Helper { } fun extractFile(s: Slice) { - return extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) + return extractFile(s.srcFile, s.dumpFile, s.offset.toLong(), s.length) } fun extractFile(fileName: String, outImgName: String, offset: Long, length: Int) { @@ -320,12 +320,10 @@ class Helper { fun modeToPermissions(inMode: Int): Set { var mode = inMode - val PERMISSIONS_MASK = 4095 - // setgid/setuid/sticky are not supported. - val MAX_SUPPORTED_MODE = 511 - mode = mode and PERMISSIONS_MASK - if (mode and MAX_SUPPORTED_MODE != mode) { - throw IOException("Invalid mode: $mode") + mode = mode and Integer.valueOf("7777", 8) //trim to xxxx + val maxSupportedMode = Integer.valueOf("777", 8) //setgid/setuid/sticky are not supported + if (mode and maxSupportedMode != mode) { + throw IOException("Invalid mode(oct): ${Integer.toOctalString(mode)}") } val allPermissions = PosixFilePermission.values() val result: MutableSet = EnumSet.noneOf(PosixFilePermission::class.java)