河南宗教知识竞赛加密算法分析(第二篇)

上回说到

请先看第一回:https://blog.zixutech.cn/archives/664

本回

由于官方2天更新了7次,尤其是今天更新了4次,我人直接裂开,官方没有采用我的强加密手段,而是继续乱jb加密,但是更新频次如此之快,我也懒得搞了,本篇文章主要是分享一下期间几次更新的加解密算法,别说还挺有创意的。

算法代码

我已添加了详细的注释供读者们阅读学习,对代码有疑问可以评论提出。

// 宗教答题第2~5次更新的加密函数
        function encrypt(content) {
            // content 是要加密的值,宗教里就是题号 如1000065

            // 随机字符串 + # + 题号,拼接后进行base64编码
            content = window.btoa('1688106372619ddf853ddce845744766#' + content)
            // code是要穿插在上述content里的内容,其内容是用户ID(需经过base64解码)和csmpagerid(不知道这到底是啥)
            let code = window.atob(memberusercode) + csmpagerid;
            // 开始将code穿插进content进行混淆
            for (let i = 0; i < code.length; i++) {
                // 在1 3 5 7 9 ··· 索引位置逐位插入code的内容
                content = content.slice(0, 2 * i + 1) + code.slice(i, i + 1) + content.slice(2 * i + 1);
            }
            // 因为content是经过base64编码的,所以其最后可能会出现=== == =这样的,如果有则替换为特定的字符串,这里是O0O0OO0O0O
            // 如果没有,则直接在后面加上这个O0O0OO0O0O
            if (content.indexOf('===') == -1 && content.indexOf('==') == -1 && content.indexOf('=') == -1) {
                content += 'O0O0OO0O0O';
            } else {
                content = content.replace('===', 'O0O0OO0O0O');
                content = content.replace('==', 'O0O0OO0O0O');
                content = content.replace('=', 'O0O0OO0O0O');
            }
            // 加密完成,返回数据
            // console.log(content)
            return content;
        }

        // 宗教答题第2~5次更新的解密函数
        function decrypt(content) {
            // content 要解密的内容,一般是题号,解密前的样子大概是
            // M1T0A000N6j0I844N3j2c1y4M1T9Y8x8O5W9R8j0N6W1I9xdOcG5Qbx1N8zcIc4dM4z1I045M3j4Q240N7T1IjMTAwMDAwOQO0O0OO0O0O

            // 设计一个新的字符串用来当做解密后的字符串
            let newStr = ''
            // 逐位循环去除混淆
            for (let i = 0; i < content.length; i++) {
                // 42的意思是,用户ID的长度和csmpagerid的长度一共是42位,所以只需要去除42个字符即可
                if (i >= 42) {
                    // 循环次数大于等于42后,直接不再裁剪,而是拼接最后所有字符串
                    newStr += content.slice(2 * i)
                    break
                }
                // 裁剪字符串,把第0 2 4 6 8 10 ··· 位的混淆字符删掉
                newStr += content.slice(2 * i, 2 * i + 1)
            }
            // 将结果进行base64解码,至此解密完成
            newStr = window.atob(newStr)
            // console.log(newStr)
        }

结语

官方更新是好事,但是也请一次更新到位,用最好的技术来应对作弊,或者能给学生们一份题库用来背,也能达到学习的目的。

希望世上再无形式主义!

河南宗教知识竞赛加密算法分析

开头

我的博客因宗教插件而火,故在1400多人的强烈要求下今年还是更新了。这个东西确实没法说,要求满分不给题库,40分钟搜个锤子呢,这不是找着让大学生都去找代刷呢,能学了吗,达的到目的吗?

我的群一直讲究技术交流为主,资源分享为辅,和谐交流环境,禁商禁推广发现就踢。所以这次也算造福一下同学们了。

起因

2021年11月23日早上群友发现插件全部失效了,上班前看了一眼,发现题目结构和开始时间都被加密了,导致老的数据全部失效,因此今天晚上下班回来简单分析了一下,发现并不是那么难,下面把分析过程写在这篇文章里,以作为技术交流。

分析

首先看一下加密后的代码结构:

可观察到大部分的重复以及最后面那显眼的0000000

起初我并没看出这是什么算法,但因为知道之前没加密的时候是一串数字,如1000055这类的,可以看出编码算法应该是个通用算法。

首先想到的就是Base64,经过测试,发现和图中数据不一致,如下面所示

原文:1000143

Base64:MTAwMDE0Mw==

图中加密的:M1T0A0w8M6D0E001M3w2O0O0O0O0

可观察出有很多相似的地方。

尝试对比后,发现如下规律(图中是另一个用户下测试的)

发现被插入的数字是 1004901214 在网页源码中查找此数字,发现正是memberusercode的值

也就是说算法其实很简单,就是把原先的数据经过Base64编码,然后每隔一位将memberusercode的一位数插入进去,最后把 == 替换成那一堆O0,如果没有 == 则直接在后面加上 O0

至此,分析完毕。

算法实现

我用js来实现这个算法,非常简单,拿来直接可以用。以下是代码实现:

function encrypt(content) {
    let code = memberusercode;
    content = window.btoa(content);
    for (let i = 0; i < 10; i++) {
        content = content.slice(0, 2 * i + 1) + code.slice(i, i + 1) + content.slice(2 * i + 1);
    }
    if (content.indexOf('==') == -1) {
        content += 'O0O0OO0O0O';
    } else {
        content = content.replace('==', 'O0O0OO0O0O');
    }
    return content;
}

就不再写注释了,各位同学可以度读一下代码看看是如何实现的,非常简单。

需要注意的是,我们需要在中间插入,要考虑到插入后的文本长度,如下所示

原文:A B C D

1次插入:A X B C D

2次插入:A X B X C D

3次插入:A X B X C X D

……

所以我们插入的索引位置是1 3 5 7 9 ……

因此,for循环内的slice的长度使用的是等差数列公式2i + 1

帮一下官方

我非常明白官方想防作弊的决心,但是说句实话,这样的算法难不倒广大高材生,我并不是第一个解出来的。我愿意在此祝官方一臂之力!

下面我给官方提供一套我在用的自定义算法,非常好用,我用它来生成用户TOKEN。

密钥不泄露几乎不存在被反解密的情况。语言是PHP的。

function encryption($string, $operation)
{
    $key = md5(config('encryption_key')); // 这是密钥,可以写在数据库或配置文件内
    $key_length = strlen($key);
    $string = str_replace('_', '/', $string);
    $string = str_replace("-", '\\', $string);
    $string = str_replace("*", '+', $string);
    $string = $operation == 'D' ? base64_decode($string) : substr(md5($string . $key), 0, 8) . $string;
    $string_length = strlen($string);
    $rndkey = $box = array();
    $result = '';
    for ($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($key[$i % $key_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if ($operation == 'D') {
        if (substr($result, 0, 8) == substr(md5(substr($result, 8) . $key), 0, 8)) {
            $result = substr($result, 8);
            return $result;
        } else {
            return '';
        }
    } else {
        $result = str_replace('=', '', base64_encode($result));
        $result = str_replace('/', '_', $result);
        $result = str_replace("\\", '-', $result);
        $result = str_replace("+", '*', $result);
        // 正斜杠 / 改为下划线 _ ; 反斜杠 \ 改为减号 - 。
        return $result;
    }
}

使用方法:

加密时:encryption(要加密的字符串, “E”)

解密时:encryption(要解密的字符串, “D”)

结语

希望官方可以改进让学生学习的措施办法,做好加密防护,给学生们个题库,他们真的会认真搜题学习,我群里已有很多将题库背下来的同学,他们不就达到了官方的目的了吗。

[原创]狗屁不通高中英语作文生成器

想法起源

在git上看到了这么个项目:https://github.com/menzi11/BullshitGenerator

感觉非常有趣,虽然生成的文章基本不可读,随后我就想为什么不做一个生成英语作文的玩具?于是就有了此项目。

效果图

IOcjJK.jpg
IOcvRO.png

我发现我生成的英语作文似乎还TM可读!绝了#笑眼

项目地址

https://github.com/zjh4473/EnglishBullshitGenerator

在线体验地址:https://zjh4473.github.io/EnglishBullshitGenerator/

自用ThinkPHP的common.php 包含各种常用函数封装

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 紫旭 <zixutech@gmail.com> <https://www.zixutech.cn>
// +----------------------------------------------------------------------

// 应用公共文件
function assembleJson($code, $status, $message, $data)
{
    return json_encode(['code' => $code, 'status' => $status, 'message' => $message, 'data' => $data]);
}

function assembleTableJson($code, $status, $message, $totalCount, $data)
{
    return json_encode(['code' => $code, 'status' => $status, 'msg' => $message, 'totalCount' => $totalCount, 'data' => $data]);
}

function generateToken($tokenArray)
{
    $token = base64_encode(json_encode($tokenArray));
    $token = encryption($token, 'E');
    return $token;
}

function decryptToken($token)
{
    $tokenArray = encryption($token, 'D');
    $tokenArray = base64_decode($tokenArray);
    $tokenArray = json_decode($tokenArray, true);
    return $tokenArray;
}

// 第二个参数 E加密 D解密
function encryption($string, $operation)
{
    $key = md5(config('encryption_key'));
    $key_length = strlen($key);
    $string = str_replace('_', '/', $string);
    $string = str_replace("-", '\\', $string);
    $string = str_replace("*", '+', $string);
    $string = $operation == 'D' ? base64_decode($string) : substr(md5($string . $key), 0, 8) . $string;
    $string_length = strlen($string);
    $rndkey = $box = array();
    $result = '';
    for ($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($key[$i % $key_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if ($operation == 'D') {
        if (substr($result, 0, 8) == substr(md5(substr($result, 8) . $key), 0, 8)) {
            $result = substr($result, 8);
            return $result;
        } else {
            return '';
        }
    } else {
        $result = str_replace('=', '', base64_encode($result));
        $result = str_replace('/', '_', $result);
        $result = str_replace("\\", '-', $result);
        $result = str_replace("+", '*', $result);
        // 正斜杠 / 改为下划线 _ ; 反斜杠 \ 改为减号 - 。
        return $result;
    }
}

//获取用户真实IP
function getIp()
{
    if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
        $ip = getenv("HTTP_CLIENT_IP");
    else
        if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
            $ip = getenv("HTTP_X_FORWARDED_FOR");
        else
            if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
                $ip = getenv("REMOTE_ADDR");
            else
                if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
                    $ip = $_SERVER['REMOTE_ADDR'];
                else
                    $ip = "unknown";
    return ($ip);
}

//Remove the exploer'bug XSS
function RemoveXSS($val)
{
    // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
    // this prevents some character re-spacing such as <java\0script>
    // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
    $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
    // straight replacements, the user should never need these since they're normal characters
    // this prevents like <IMG SRC=@avascript:alert('XSS')>
    $search = 'abcdefghijklmnopqrstuvwxyz';
    $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $search .= '1234567890!@#$%^&*()';
    $search .= '~`";:?+/={}[]-_|\'\\';
    for ($i = 0; $i < strlen($search); $i++) {
        // ;? matches the ;, which is optional
        // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

        // @ @ search for the hex values
        $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ;
        // @ @ 0{0,7} matches '0' zero to seven times
        $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ;
    }

    // now the only remaining whitespace attacks are \t, \n, and \r
    $ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
    $ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
    $ra = array_merge($ra1, $ra2);

    $found = true; // keep replacing as long as the previous round replaced something
    while ($found == true) {
        $val_before = $val;
        for ($i = 0; $i < sizeof($ra); $i++) {
            $pattern = '/';
            for ($j = 0; $j < strlen($ra[$i]); $j++) {
                if ($j > 0) {
                    $pattern .= '(';
                    $pattern .= '(&#[xX]0{0,8}([9ab]);)';
                    $pattern .= '|';
                    $pattern .= '|(�{0,8}([9|10|13]);)';
                    $pattern .= ')*';
                }
                $pattern .= $ra[$i][$j];
            }
            $pattern .= '/i';
            $replacement = substr($ra[$i], 0, 2) . '<x>' . substr($ra[$i], 2); // add in <> to nerf the tag
            $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
            if ($val_before == $val) {
                // no replacements were made, so exit the loop
                $found = false;
            }
        }
    }
    return $val;
}

//防注入
function abacaAddslashes($var)
{
    if (!get_magic_quotes_gpc()) {
        if (is_array($var)) {
            foreach ($var as $key => $val) {
                $var [$key] = abacaAddslashes($val);
            }
        } else {
            $var = addslashes($var);
        }
    }
    return $var;
}

//检查各项过期时间,过期返回true 没过期返回false
function checkExpire($expiretime)
{
    if (!strtotime($expiretime)) {
        //不是时间
        return true;
    }
    $expiretime = number_format(strtotime($expiretime) - time());
    if ($expiretime <= 0) {
        return true;
    } else {
        return false;
    }
}

//取中间文本
function get_between($input, $start, $end)
{
    $substr = substr($input, strlen($start) + strpos($input, $start), (strlen($input) - strpos($input, $end)) * (-1));
    return $substr;
}

// 生成随机密码
function randomkeys($length, $pureNumber = false)
{
    // 生成字母和数字组成的6位字符串

    if ($pureNumber) {
        $arr = range(0, 9);
    } else {
        $str = range('A', 'Z');

        // 去除大写的O,以防止与0混淆

        unset($str[array_search('O', $str)]);
        $arr = array_merge(range(0, 9), $str);
    }

    shuffle($arr);

    $invitecode = '';

    $arr_len = count($arr);

    for ($i = 0; $i < $length; $i++) {

        $rand = mt_rand(0, $arr_len - 1);

        $invitecode .= $arr[$rand];

    }

    return $invitecode;
}

function getThisWeek()
{

//当前日期
    $sdefaultDate = date('Y-m-d');
//$first =1 表示每周星期一为开始日期 0表示每周日为开始日期
    $first = 0;
//获取当前周的第几天 周日是 0 周一到周六是 1 - 6
    $w = date('w', strtotime($sdefaultDate));
//获取本周开始日期,如果$w是0,则表示周日,减去 6 天
    $week_start = date('Y-m-d', strtotime("$sdefaultDate -" . ($w ? $w - $first : 6) . ' days'));
//本周结束日期
    $week_end = date('Y-m-d', strtotime("$week_start +6 days"));
    return [$week_start, $week_end];
}

function array_sort($array, $keys, $type = 'asc')
{
    $keysvalue = $new_array = array();
    foreach ($array as $k => $v) {
        $keysvalue[$k] = $v[$keys];
    }
    if ($type == 'asc') {
        asort($keysvalue);
    } else {
        arsort($keysvalue);
    }
    reset($keysvalue);
    foreach ($keysvalue as $k => $v) {
        $new_array[$k] = $array[$k];
    }
    return $new_array;
}

/**
 * 系统邮件发送函数
 * @param string $tomail 接收邮件者邮箱
 * @param string $name 接收邮件者名称
 * @param string $subject 邮件主题
 * @param string $body 邮件内容
 * @param string $attachment 附件列表
 * @return boolean
 * @throws \PHPMailer\PHPMailer\Exception
 */
function send_mail($tomail, $name, $subject = '', $body = '', $attachment = null)
{
    $mail = new PHPMailer\PHPMailer\PHPMailer();           //实例化PHPMailer对象
    $mail->CharSet = 'UTF-8';           //设定邮件编码,默认ISO-8859-1,如果发中文此项必须设置,否则乱码
    $mail->IsSMTP();                    // 设定使用SMTP服务
    $mail->SMTPDebug = 3;               // SMTP调试功能 0=关闭 1 = 错误和消息 2 = 消息
    $mail->SMTPAuth = true;             // 启用 SMTP 验证功能
    $mail->SMTPSecure = 'ssl';          // 使用安全协议
    $mail->Host = "smtp.qq.com"; // SMTP 服务器
    $mail->Port = 465;                  // SMTP服务器的端口号
    $mail->Username = "";    // SMTP服务器用户名
    $mail->Password = "";     // SMTP服务器密码
    $mail->SetFrom('发信地址', '邮件标题');
    $replyEmail = '';                   //留空则为发件人EMAIL
    $replyName = '请勿回复本邮件';                    //回复名称(留空则为发件人名称)
    $mail->AddReplyTo($replyEmail, $replyName);
    $mail->Subject = $subject;
    $mail->MsgHTML($body);
    $mail->AddAddress($tomail, $name);
    if (is_array($attachment)) { // 添加附件
        foreach ($attachment as $file) {
            is_file($file) && $mail->AddAttachment($file);
        }
    }
    return $mail->Send() ? true : $mail->ErrorInfo;
}

/**
 * http请求
 * @param string $url 请求地址
 * @param boolean|string|array $params 请求数据
 * @param integer $ispost 0/1,是否post
 * @param array $header
 * @param bool $verify 是否验证ssl
 * return string|boolean          出错时返回false
 * @param bool|array $proxy 代理数组
 * @return bool|mixed
 */
function http_request($url, $params = false, $ispost = 0, $header = [], $verify = false, $proxy = false)
{
    $httpInfo = array();
    $ch = curl_init();
    $ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36';
    if (!empty($header)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    }
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_USERAGENT, $ua);

    if ($proxy) {
        curl_setopt($ch, CURLOPT_PROXY, $proxy['ip']); //代理服务器地址
        curl_setopt($ch, CURLOPT_PROXYPORT, $proxy['port']); //代理服务器端口
    }
    //忽略ssl证书
    if ($verify === true) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    } else {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    }
    if ($ispost) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
        curl_setopt($ch, CURLOPT_URL, $url);
    } else {
        if (is_array($params)) {
            $params = http_build_query($params);
        }
        if ($params) {
            curl_setopt($ch, CURLOPT_URL, $url . '?' . $params);
        } else {
            curl_setopt($ch, CURLOPT_URL, $url);
        }
    }
    $response = curl_exec($ch);
    if ($response === FALSE) {
        trace("cURL Error: " . curl_errno($ch) . ',' . curl_error($ch), 'error');
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $httpInfo = array_merge($httpInfo, curl_getinfo($ch));
        trace($httpInfo, 'error');
        return false;
    }
    curl_close($ch);
    return $response;
}


/**
 * 检查是否是手机访问
 * @return bool
 */
function is_mobile()
{
    $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
    $is_pc = (strpos($agent, 'windows nt')) ? true : false;
    $is_mac = (strpos($agent, 'mac os')) ? true : false;
    $is_iphone = (strpos($agent, 'iphone')) ? true : false;
    $is_android = (strpos($agent, 'android')) ? true : false;
    $is_ipad = (strpos($agent, 'ipad')) ? true : false;


    if ($is_pc) {
        return false;
    }

    if ($is_mac) {
        return false;
    }

    if ($is_iphone) {
        return true;
    }

    if ($is_android) {
        return true;
    }

    if ($is_ipad) {
        return false;
    }
}