PHP 安卓【apk】,苹果【ipa】,母包生成带渠道号子包

一,安卓V2签名加入渠道号

1,调用生成安卓子包自定义函数

   $apk_name = 'demo.apk';  // 安卓包名
   $channel_id = 1;  // 需要加入的渠道号
   try {
	 $apk_url =$this->setapk($apk_name ,$channel_id);
	}catch (\Exception $e){
	    $this->error($e->getMessage());
	}
	//得到生成的子包存放地址
	var_dump($apk_url ); 
	exit;

2,自定义函数内确定母包地址和生成子包存放的地址

public function setapk($apk_name = '',$channel_id= 0){
	//根据安卓包名得到名称与apk后缀
	$appname_arr = explode('.',$apk_name);
	//需要写入的渠道信息
	$channel_json = json_encode(['channel'=>$channel_id]);
	//母包地址
    $m_rootpath = $_SERVER['DOCUMENT_ROOT'].'/static/app/'.$appname;
	//拼接子包名称
    $s_appname = $appname_arr[0].'_'.$channel_id.'.'.$appname_arr[1];
    //指定子包存放的位置,  /static/app/apk/子包名
    $s_rootpath = $_SERVER['DOCUMENT_ROOT'].'/static/app/'.$appname_arr[1].'/'.$s_appname;
    try {
         $this->writeParam($m_rootpath,$s_rootpath,$channel_json);
         $data = [
             'local' => $s_rootpath,
             'apk'  => 'http://域名'.'/static/app/'.$appname_arr[1].'/'.$s_appname
         ];
         return $data;
     }catch (\Exception $e){
         throw new \Exception('安卓子包生成失败:'.$e->getMessage());
     }
}
    private function readParam(string $filePath, bool $moreParams = false)
    {
        if (!file_exists($filePath)) {
            throw new Exception(sprintf('需要分析的APK包 %s 不存在', $filePath));
        }
        $stream = fopen($filePath, 'r');
        //跳到结尾 寻找中心目录偏移位置
        fseek($stream, -6, SEEK_END);
        //中心目录偏移位置
        $centerDirectoryOffsetPosition = unpack('V*',fread($stream, 4))[1];
        //检查是否存在魔法数
        fseek($stream, $centerDirectoryOffsetPosition - 16);

        if (fread($stream, 16) != 'APK Sig Block 42') {
            throw new Exception('数据格式异常,不存在ApkSigBlock魔法数');
        }
        //获取V2签名块总大小
        fseek($stream, ftell($stream) - 24);
        $v2BlockSize = unpack('P*', fread($stream, 8))[1];
        //获取v2签名块开始位置
        $startPosition = $centerDirectoryOffsetPosition - 8 - $v2BlockSize + 0x00000010;
        //获取v2签名 特殊标识ID 0x7109871a
        fseek($stream, $startPosition);
        bin2hex(fread($stream, 8));
//        exit;
//        if (bin2hex(fread($stream, 8)) != '1a870971ca030000') {
//            throw new Exception('数据格式异常,不支持非V2签名进行操作');
//        }
        //获取第一个块大小
        fseek($stream, -16, SEEK_CUR);
        $blockSize1 = unpack('P*', fread($stream, 8))[1];
        //获取第二个块大小
        fseek($stream, $blockSize1, SEEK_CUR);
        $blockSize2 = unpack('P*', fread($stream, 8))[1];
        //获取第三个块大小
        fseek($stream, $blockSize2, SEEK_CUR);
        $blockSize3 = unpack('P*', fread($stream, 8))[1];
        //缺失第三个包 暂时不知道为什么
        if ($blockSize3 == $v2BlockSize) {
            $blockSize3        = 0;
            $attachContentSize = $v2BlockSize;
        } else {
            //获取附加信息块大小
            //检查是否缺包打出来的子包
            if (fread($stream, 4) == 'wwwq') {
                //是缺失包
                fseek($stream, -4, SEEK_CUR);
                $attachContentSize = $blockSize3;
                $blockSize3        = 0;
            } else {
                //不是缺失包
                fseek($stream, -4, SEEK_CUR);
                fseek($stream, $blockSize3, SEEK_CUR);
                $attachContentSize = unpack('P*', fread($stream, 8))[1];
            }
        }
        $moreInfo = [
            'centerDirPosition'    => $centerDirectoryOffsetPosition,
            'v2BlockSize'          => $v2BlockSize,
            'v2BlockStartPosition' => $startPosition,
            'b1Size'               => $blockSize1,
            'b2Size'               => $blockSize2,
            'b3Size'               => $blockSize3,
        ];
        $attachContent = '';
        do {
            //没有进行添加附加信息块 因为总大小与v2块总大小一致
            if ($attachContentSize == $v2BlockSize) {
                break;
            }
            //获取附加块信息
            $attachContent = fread($stream, $attachContentSize);
            fclose($stream);
            break;
        } while (false);

        //返回更多信息
        if ($moreParams) {
            return array_merge(['attackContent' => $attachContent], $moreInfo);
        }
        return ['attackContent' => $attachContent];
    }
private function writeParam(string $inputFile, string $outputFile, string $content)
    {
        if (!file_exists($inputFile)) {
            throw new Exception(sprintf('masterApk包 %s 不存在', $inputFile));
        }
        $contentLength = strlen($content);
        if ($contentLength == 0) {
            throw new Exception('写入参数内容不能为空');
        }
        //补全兼容缺失包
        $content       = 'wwwq' . $content;
        $contentLength = strlen($content);
        $masterApkPackageInfo = $this->readParam($inputFile, true);
        if ($masterApkPackageInfo['attackContent'] != '') {
            throw new Exception('masterApk包不能是已经修改的包的');
        }
        $masterApkStream     = fopen($inputFile, 'r');
        $subPackageApkStream = fopen($outputFile, 'w');
        //第一步 复制第一段内容
        fwrite($subPackageApkStream, fread($masterApkStream, $masterApkPackageInfo['v2BlockStartPosition'] - 16));
        //第二步 写出需要添加附加文本长度大小 与 偏移写出块大小
        fwrite($subPackageApkStream, pack('P*', $masterApkPackageInfo['v2BlockSize'] + $contentLength + 8));
        fseek($masterApkStream, 8, SEEK_CUR);
        fwrite($subPackageApkStream, fread($masterApkStream, 8 + $masterApkPackageInfo['b1Size']));
        fwrite($subPackageApkStream, fread($masterApkStream, 8 + $masterApkPackageInfo['b2Size']));
        if ($masterApkPackageInfo['b3Size'] != 0) {
            fwrite($subPackageApkStream, fread($masterApkStream, 8 + $masterApkPackageInfo['b3Size']));
        }
        //第三步 写出附加文本大小 与 附加文本内容
        fwrite($subPackageApkStream, pack('P*', $contentLength));
        fwrite($subPackageApkStream, $content);
        //写出v2签名块大小
        fwrite($subPackageApkStream, pack('P*', $masterApkPackageInfo['v2BlockSize'] + $contentLength + 8));
//        exit(var_dump(222));
        //复制后续内容
        $copyLength = filesize($inputFile) - ftell($masterApkStream) - 14;
        fseek($masterApkStream, 8, SEEK_CUR);
        fwrite($subPackageApkStream, fread($masterApkStream, $copyLength));
        fwrite($subPackageApkStream, pack('V*', $masterApkPackageInfo['centerDirPosition'] + $contentLength + 8));
        fwrite($subPackageApkStream, pack('v*', 0));
        fclose($masterApkStream);
        fclose($subPackageApkStream);
    }

3,怎么跟前端解释这个东西,前端怎么获取渠道号呢?

参考来源:安卓v2签名描述信息

参考来源: 点击跳转




二,苹果包怎么加入渠道号

1,调用生成苹果子包自定义函数

   $ipa_name = 'demo.ipa';  // 安卓包名
   $channel_id = 1;  // 需要加入的渠道号
   try {
	 $ipa_url = $this->setipa($ipa_name,$channel_id);
	}catch (\Exception $e){
	    $this->error($e->getMessage());
	}
	//得到生成的子包存放地址
	var_dump($ipa_url); 
	exit;

2,自定义函数内确定母包地址和生成子包存放的地址

public function setipa($ipa_name = '',$channel_id = 0){
	  //根据苹果包名得到名称与ipa后缀
      $appname_arr = explode('.',$ipa_name);
      //母包地址
      $m_rootpath = $_SERVER['DOCUMENT_ROOT'].'/static/app/'.$ipa_name;
      if (!file_exists($m_rootpath)) {
          throw new \Exception("苹果母包不存在");
      }
      //拼接子包名称
      $s_appname = $appname_arr[0].'_'.$channel_id.'.'.$appname_arr[1];
      //指定子包存放的位置,  /static/app/apk/子包名
      $s_rootpath = $_SERVER['DOCUMENT_ROOT'].'/static/app/'.$appname_arr[1].'/'.$s_appname;
      //复制母包生成子包
      $copyipa = copy($m_rootpath,$s_rootpath);
      if($copyipa){
          $zip = new \ZipArchive();
          $iszip = false;
          if($zip->open($s_rootpath) === TRUE){
          	  //在包内找到 _CodeSignature 文件夹,在同级目录下加入两个文件夹作为渠道号
          	  //怎么找?可以用压缩软件直接打开然后慢慢找
          	  //也可以先用压缩软件在里面直接创建文件夹看是否还能安装
          	  //是用压缩软件打开后加文件夹,不是解压加文件夹再压缩
              $zip->addEmptyDir( "Payload/Runner.app/channel");//这里加文件夹时需要一层一层的加,不然可能会出现找不到情况
              $zip->addEmptyDir( "Payload/Runner.app/channel/".$channel_id);
              $zip->close();
              $iszip = true;
          }
          if($iszip){
              $data = [
                  'local' => $s_rootpath,
                  'ipa'  => 'http://域名'.'/static/app/'.$appname_arr[1].'/'.$s_appname
              ];
              return $data;
          }
      }else{
          throw new \Exception("苹果子包生成失败");
      }
  }

3,怎么跟前端解释这个东西,前端怎么获取渠道号呢?

需要找到包内文件夹的名称获取文件夹值

参考来源:点击跳转

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值