AKP分析(基于取证)

APP基础

格式:

*.IPA IOS系统的app文件格式

*.APK 安卓系统的app文件格式

APK静态分析

加固(壳):

目的:

防止APK被逆向,防篡改,实现数据保护

种类:

APK加壳的中了非常多,我在取证过程中所见到的加壳种类有UPX和pyintaller的加壳方式

脱壳:

一般我们用雷电挂载APK时可以直接使用雷电自带的脱壳来脱壳之后反编译。但是有些恶意文件不能用雷电来挂载,这个时候我们就要手动查壳

查壳工具:

Detect It Easy3.7(之前老的版本可能查不到pyinstaller这个加壳方式)

PEiD:偶尔用,效果不是很好

当然可以用沙箱的方法看一下他是不是有壳

脱壳工具:

知道是什么壳加固了的话,就可以对症下药了

如果是UPX壳的话就用UPX tool来脱

或者360壳,使用取证工具来脱

如果是pyinstaller壳的话用脚本来脱,将脚本和恶意文件放到一个文件夹下面,用cmd跑,会得到一个文件夹,文件夹里的文件不带pyc后缀,要自己加上,然后这些文件也缺少文件头,还要自己补充

然后可以使用在线pyc反编译

pyc反编译 - 爱资料工具 (toolnb.com)

"""
PyInstaller Extractor v1.9 (Supports pyinstaller 3.3, 3.2, 3.1, 3.0, 2.1, 2.0)
Author : Extreme Coders
E-mail : extremecoders(at)hotmail(dot)com
Web    : https://0xec.blogspot.com
Date   : 29-November-2017
Url    : https://sourceforge.net/projects/pyinstallerextractor/

For any suggestions, leave a comment on
https://forum.tuts4you.com/topic/34455-pyinstaller-extractor/

This script extracts a pyinstaller generated executable file.
Pyinstaller installation is not needed. The script has it all.

For best results, it is recommended to run this script in the
same version of python as was used to create the executable.
This is just to prevent unmarshalling errors(if any) while
extracting the PYZ archive.

Usage : Just copy this script to the directory where your exe resides
        and run the script with the exe file name as a parameter

C:\path\to\exe\>python pyinstxtractor.py <filename>
$ /path/to/exe/python pyinstxtractor.py <filename>

Licensed under GNU General Public License (GPL) v3.
You are free to modify this source.

CHANGELOG
================================================

Version 1.1 (Jan 28, 2014)
-------------------------------------------------
- First Release
- Supports only pyinstaller 2.0

Version 1.2 (Sept 12, 2015)
-------------------------------------------------
- Added support for pyinstaller 2.1 and 3.0 dev
- Cleaned up code
- Script is now more verbose
- Executable extracted within a dedicated sub-directory

(Support for pyinstaller 3.0 dev is experimental)

Version 1.3 (Dec 12, 2015)
-------------------------------------------------
- Added support for pyinstaller 3.0 final
- Script is compatible with both python 2.x & 3.x (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)

Version 1.4 (Jan 19, 2016)
-------------------------------------------------
- Fixed a bug when writing pyc files >= version 3.3 (Thanks to Daniello Alto: https://github.com/Djamana)

Version 1.5 (March 1, 2016)
-------------------------------------------------
- Added support for pyinstaller 3.1 (Thanks to Berwyn Hoyt for reporting)

Version 1.6 (Sept 5, 2016)
-------------------------------------------------
- Added support for pyinstaller 3.2
- Extractor will use a random name while extracting unnamed files.
- For encrypted pyz archives it will dump the contents as is. Previously, the tool would fail.

Version 1.7 (March 13, 2017)
-------------------------------------------------
- Made the script compatible with python 2.6 (Thanks to Ross for reporting)

Version 1.8 (April 28, 2017)
-------------------------------------------------
- Support for sub-directories in .pyz files (Thanks to Moritz Kroll @ Avira Operations GmbH & Co. KG)

Version 1.9 (November 29, 2017)
-------------------------------------------------
- Added support for pyinstaller 3.3
- Display the scripts which are run at entry (Thanks to Michael Gillespie @ malwarehunterteam for the feature request)

"""

from __future__ import print_function
import os
import struct
import marshal
import zlib
import sys
import imp
import types
from uuid import uuid4 as uniquename


class CTOCEntry:
    def __init__(self, position, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name):
        self.position = position
        self.cmprsdDataSize = cmprsdDataSize
        self.uncmprsdDataSize = uncmprsdDataSize
        self.cmprsFlag = cmprsFlag
        self.typeCmprsData = typeCmprsData
        self.name = name


class PyInstArchive:
    PYINST20_COOKIE_SIZE = 24           # For pyinstaller 2.0
    PYINST21_COOKIE_SIZE = 24 + 64      # For pyinstaller 2.1+
    MAGIC = b'MEI\014\013\012\013\016'  # Magic number which identifies pyinstaller

    def __init__(self, path):
        self.filePath = path


    def open(self):
        try:
            self.fPtr = open(self.filePath, 'rb')
            self.fileSize = os.stat(self.filePath).st_size
        except:
            print('[*] Error: Could not open {0}'.format(self.filePath))
            return False
        return True


    def close(self):
        try:
            self.fPtr.close()
        except:
            pass


    def checkFile(self):
        print('[*] Processing {0}'.format(self.filePath))
        # Check if it is a 2.0 archive
        self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)
        magicFromFile = self.fPtr.read(len(self.MAGIC))

        if magicFromFile == self.MAGIC:
            self.pyinstVer = 20     # pyinstaller 2.0
            print('[*] Pyinstaller version: 2.0')
            return True

        # Check for pyinstaller 2.1+ before bailing out
        self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)
        magicFromFile = self.fPtr.read(len(self.MAGIC))

        if magicFromFile == self.MAGIC:
            print('[*] Pyinstaller version: 2.1+')
            self.pyinstVer = 21     # pyinstaller 2.1+
            return True

        print('[*] Error : Unsupported pyinstaller version or not a pyinstaller archive')
        return False


    def getCArchiveInfo(self):
        try:
            if self.pyinstVer == 20:
                self.fPtr.seek(self.fileSize - self.PYINST20_COOKIE_SIZE, os.SEEK_SET)

                # Read CArchive cookie
                (magic, lengthofPackage, toc, tocLen, self.pyver) = \
                struct.unpack('!8siiii', self.fPtr.read(self.PYINST20_COOKIE_SIZE))

            elif self.pyinstVer == 21:
                self.fPtr.seek(self.fileSize - self.PYINST21_COOKIE_SIZE, os.SEEK_SET)

                # Read CArchive cookie
                (magic, lengthofPackage, toc, tocLen, self.pyver, pylibname) = \
                struct.unpack('!8siiii64s', self.fPtr.read(self.PYINST21_COOKIE_SIZE))

        except:
            print('[*] Error : The file is not a pyinstaller archive')
            return False

        print('[*] Python version: {0}'.format(self.pyver))

        # Overlay is the data appended at the end of the PE
        self.overlaySize = lengthofPackage
        self.overlayPos = self.fileSize - self.overlaySize
        self.tableOfContentsPos = self.overlayPos + toc
        self.tableOfContentsSize = tocLen

        print('[*] Length of package: {0} bytes'.format(self.overlaySize))
        return True


    def parseTOC(self):
        # Go to the table of contents
        self.fPtr.seek(self.tableOfContentsPos, os.SEEK_SET)

        self.tocList = []
        parsedLen = 0

        # Parse table of contents
        while parsedLen < self.tableOfContentsSize:
            (entrySize, ) = struct.unpack('!i', self.fPtr.read(4))
            nameLen = struct.calcsize('!iiiiBc')

            (entryPos, cmprsdDataSize, uncmprsdDataSize, cmprsFlag, typeCmprsData, name) = \
            struct.unpack( \
                '!iiiBc{0}s'.format(entrySize - nameLen), \
                self.fPtr.read(entrySize - 4))

            name = name.decode('utf-8').rstrip('\0')
            if len(name) == 0:
                name = str(uniquename())
                print('[!] Warning: Found an unamed file in CArchive. Using random name {0}'.format(name))

            self.tocList.append( \
                                CTOCEntry(                      \
                                    self.overlayPos + entryPos, \
                                    cmprsdDataSize,             \
                                    uncmprsdDataSize,           \
                                    cmprsFlag,                  \
                                    typeCmprsData,              \
                                    name                        \
                                ))

            parsedLen += entrySize
        print('[*] Found {0} files in CArchive'.format(len(self.tocList)))



    def extractFiles(self):
        print('[*] Beginning extraction...please standby')
        extractionDir = os.path.join(os.getcwd(), os.path.basename(self.filePath) + '_extracted')

        if not os.path.exists(extractionDir):
            os.mkdir(extractionDir)

        os.chdir(extractionDir)

        for entry in self.tocList:
            basePath = os.path.dirname(entry.name)
            if basePath != '':
                # Check if path exists, create if not
                if not os.path.exists(basePath):
                    os.makedirs(basePath)

            self.fPtr.seek(entry.position, os.SEEK_SET)
            data = self.fPtr.read(entry.cmprsdDataSize)

            if entry.cmprsFlag == 1:
                data = zlib.decompress(data)
                # Malware may tamper with the uncompressed size
                # Comment out the assertion in such a case
                assert len(data) == entry.uncmprsdDataSize # Sanity Check

            with open(entry.name, 'wb') as f:
                f.write(data)

            if entry.typeCmprsData == b's':
                print('[+] Possible entry point: {0}'.format(entry.name))

            elif entry.typeCmprsData == b'z' or entry.typeCmprsData == b'Z':
                self._extractPyz(entry.name)


    def _extractPyz(self, name):
        dirName =  name + '_extracted'
        # Create a directory for the contents of the pyz
        if not os.path.exists(dirName):
            os.mkdir(dirName)

        with open(name, 'rb') as f:
            pyzMagic = f.read(4)
            assert pyzMagic == b'PYZ\0' # Sanity Check

            pycHeader = f.read(4) # Python magic value

            if imp.get_magic() != pycHeader:
                print('[!] Warning: The script is running in a different python version than the one used to build the executable')
                print('    Run this script in Python{0} to prevent extraction errors(if any) during unmarshalling'.format(self.pyver))

            (tocPosition, ) = struct.unpack('!i', f.read(4))
            f.seek(tocPosition, os.SEEK_SET)

            try:
                toc = marshal.load(f)
            except:
                print('[!] Unmarshalling FAILED. Cannot extract {0}. Extracting remaining files.'.format(name))
                return

            print('[*] Found {0} files in PYZ archive'.format(len(toc)))

            # From pyinstaller 3.1+ toc is a list of tuples
            if type(toc) == list:
                toc = dict(toc)

            for key in toc.keys():
                (ispkg, pos, length) = toc[key]
                f.seek(pos, os.SEEK_SET)

                fileName = key
                try:
                    # for Python > 3.3 some keys are bytes object some are str object
                    fileName = key.decode('utf-8')
                except:
                    pass

                # Make sure destination directory exists, ensuring we keep inside dirName
                destName = os.path.join(dirName, fileName.replace("..", "__"))
                destDirName = os.path.dirname(destName)
                if not os.path.exists(destDirName):
                    os.makedirs(destDirName)

                try:
                    data = f.read(length)
                    data = zlib.decompress(data)
                except:
                    print('[!] Error: Failed to decompress {0}, probably encrypted. Extracting as is.'.format(fileName))
                    open(destName + '.pyc.encrypted', 'wb').write(data)
                    continue

                with open(destName + '.pyc', 'wb') as pycFile:
                    pycFile.write(pycHeader)      # Write pyc magic
                    pycFile.write(b'\0' * 4)      # Write timestamp
                    if self.pyver >= 33:
                        pycFile.write(b'\0' * 4)  # Size parameter added in Python 3.3
                    pycFile.write(data)


def main():
    if len(sys.argv) < 2:
        print('[*] Usage: pyinstxtractor.py <filename>')

    else:
        arch = PyInstArchive(sys.argv[1])
        if arch.open():
            if arch.checkFile():
                if arch.getCArchiveInfo():
                    arch.parseTOC()
                    arch.extractFiles()
                    arch.close()
                    print('[*] Successfully extracted pyinstaller archive: {0}'.format(sys.argv[1]))
                    print('')
                    print('You can now use a python decompiler on the pyc files within the extracted directory')
                    return

            arch.close()


if __name__ == '__main__':
    main()

内部结构:

APK本质是一个压缩包,可以修改为.zip然后解压看到里面的内部文件

AndroidManifest.xml全局清单文件(最重要)

使用雷电我们是可以直接看到的这些内容

内含:APK安装包名、版本、SDK版本、APP运行权限、Activity主入口函数,第三方SDK调用信息

classes.dex(在虚拟机上可执行文件)Bytecode程序代码主体

是我们逆向分析的主要对象是java源码编译生成的java字节文件(是java文件经过jdk编译,再经dex编译生成classes.dex)。可查看域名,IP,加解密算法之类的信息

查看工具:雷电、JEB(看不懂的代码可以使用tab键转换为java语言)、ida等反编译工具

分析方法:先找主程序/入口程序/主函数,然后顺着往下看(合理利用查找)

META-INF(签名信息)

APK中的证书公钥、有效日期、签名以及证书的MD5、SHA-1/256、指纹信息、数字签名(不同的程序可能名字相同但数字签名不同)

resources.arsc(二进制索引文件)

图片,颜色等现在这个文件里面存储,然后在映射道Android的R文件中,形成ID,在使用程序资源是直接就可以通过ID调用

Assets(资源文件夹)

lib(存放so文件)

存放第三方库文件,这些so文件一般都是使用C或C++编写,单靠JAVA很难实现高性能,会依赖SO文件,一般一些隐藏的加密文件都会用so文件封装

根据CPU性能架构,lib分为四种分别是ARM(高通骁龙、麒麟基本都是)、ARM-V7、MIPS、x86(桌面笔记本CPU一般使用),对应目录为armeabi、armeabi-v7a、mips、x86

一般越好的程序他的架构越多,能在各种手机上面使用,甚至电脑

分析方法

混淆市代码加固方式的解决

通过修该代码函数的名字起到影响查找函数的作用,比如把函数名字改成abcdef什么的

先找主函数,可以找LAUNCHER,LAUNCHER上面那个activity名字就是主函数,然后一步步找主函数页面,找到主函数只有再找oncreate

正则表达式搜索

使用魔盾安全可以直接看到一些重要信息

APK动态分析

APK抓包概述

一般抓包使用https抓包

抓包工具

代理抓包工具(小而美):FIddler、Charles等。可以解密https的,tcp之上的都可以解析

非代理抓包:wireshark、httpdebugger

取证抓包工具:应用程序检测大师系统、雷电

FLDDLER抓包

首先下载并配置了flddler的抓包工具

端口写一个不会冲突的端口

然后我们要配置我们的雷电模拟器(注意这里必须要用雷电九,雷电四不能设置WiFi代理)

点击WiFi的齿轮设置手动代理

代理服务器主机名是我们电脑的wife地址

设置完代理之后我们用手机访问就是可以发现是能抓到报的了

确实抓到了

若要解密https流,我们要重新设置

双方都要安装假证书

两者都安装好之后,查看https的官网就可以看到了

这两个图片都有

adb.exe命令

adb.exe在雷电的文件夹里面,使用要使用cmd

使用方法:

先使用adb devices命令测试链接(出现xxx device表示测试成功可以通讯,不成功可以考虑usb模式是否打开)

在手机上打开要测试的软件程序

adb shell dumpsys window | findstr mCurrentFocus(mCurrentFocus表示鼠标当前光标位置,也就是打开的软件) 获取当前窗口特征,比如包名

adb shell ps 获取进程信息,可以找进程名

adb shell pm path 包名 获取安装路径

adb pull + 路径名,可以讲文件备份拉出来,不指定路径的话就默认存储在雷电文件夹下面

安卓文件目录查看:

APP安装包: /data/app/目录

APP存储位置: /data/data/目录

vmdk文件取证

有的时候给的不是E01或者DD镜像格式,而是给的vmdk格式的镜像,一般会给你三个:data.vmdk system.vmdk sdcard.vmdk文件,这种用火眼是不能分析的

解析方法:

手机大师解析

模拟器镜像替换解析:在雷电文件夹下的vms文件里面替换

取证大师解析

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
缩小uniapp打包生成的apk体积可以采取以下几个方法: 1. 通过压缩资源文件和图片来减小apk的大小。可以使用工具对资源文件进行压缩,如TinyPNG用于压缩图片,同时可以使用webpack插件对资源文件进行优化,如image-webpack-loader用于压缩和优化图片。 2. 移除无用的插件和模块。检查项目中是否有不必要的插件或模块,如果没有使用到的可以将其移除,以减少apk体积。 3. 优化代码和减少依赖。通过代码优化和减少额外的依赖项可以减小apk的体积。可以通过使用Tree Shaking和Code Splitting等技术来剔除未使用的代码和依赖。 4. 使用混淆和压缩工具。可以使用工具对代码进行混淆和压缩,如ProGuard和UglifyJS等,来减小生成的apk大小。 5. 配置合适的资源压缩和混淆策略。通过配置合适的资源压缩和混淆策略,可以进一步减小apk的大小。可以根据项目需求选择合适的压缩和混淆策略。 通过以上方法,可以有效地减小uniapp打包生成的apk体积,提高应用的性能和用户体验。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [so没有打包进AKP导致java.lang.UnsatisfiedLinkError: dlopen failed: library “lib.so“ not found](https://blog.csdn.net/quantum7/article/details/121092699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Uniapp 实现app自动更新功能](https://blog.csdn.net/S1516843523/article/details/124213993)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [ionic打包签名akp(查看apk签名)](https://blog.csdn.net/Maxbalance/article/details/49157579)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值