Re

某h软件 bika

Re

bika 动漫,dddd

flutter 以前用过,但是没有arm64的so,所有就放弃了,今天一个朋发给我,发现居然后arm64的,那就别挂我不客气。

blutter 启动,dart 2.19 解析出来了

主要是事看pp.txt和 addNames.py 这两个文件,找关键字,看有地址的,一般这种软件加密都是aes,所以直接搜索,安后挨个挨个frida hook,

blutter 会生成一个blutter_frida.js, 添加一句话

    const fn_addr = 0x7e1018;
    Interceptor.attach(libapp.add(fn_addr), {
        onEnter: function () {
            init(this.context);
            let objPtr = getArg(this.context, 0);
            const [tptr, cls, values] = getTaggedObjectValue(objPtr);
            console.log(`${cls.name}@${tptr.toString().slice(2)} =`, JSON.stringify(values, null, 2));
		    //就是添这句话,找关键字就行了, 
            console.log('hexdump ==> '+hexdump(objPtr));
        }
    });

多hook 几次基本拿下,然后就是猜,deviceId忙猜随机生成,拿uuid做md5去前16位,直接传上去,发现可以,最后就是喜闻乐见得绑定邀请码获得VIP,直接就是稳稳得拿下。

图片.png

import requests, time, json, uuid ,hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

code='YMMTK'
cipher = AES.new(b'525202f9149e061d', AES.MODE_ECB)

def get_header():
    return {
        'user-agent': 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleDart/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17',
        'version': '1.0',
        'devicetype': 'android',
        'time': str(int(time.time()*1000)),
        'host': 'api.bkrezwvjkc.com',
        'content-type': 'application/x-www-form-urlencoded',
    }
def remove_pkcs7_padding(data: bytes, block_size: int = 16):
    if len(data) % block_size != 0:
        raise ValueError("Data length is not a multiple of the block size")

    padding_length = data[-1]
    if padding_length > block_size or padding_length <= 0:
        raise ValueError("Invalid padding detected")

    padding = data[-padding_length:]
    if any(byte != padding_length for byte in padding):
        raise ValueError("Inconsistent padding detected")

    return data[:-padding_length]

def en(map):
    plaintext_bytes_padded = pad(json.dumps(map).encode(), AES.block_size)
    return cipher.encrypt(plaintext_bytes_padded).hex()
def de(s):
    a=cipher.decrypt(s)
    a=remove_pkcs7_padding(a)
    return json.loads(a.decode().replace('\r','').replace('\\\\','\\'))

def device_id():
    random_uuid = uuid.uuid4()
    uuid_str = str(random_uuid)
    return hashlib.md5(uuid_str.encode()).hexdigest()[:16]

def get_token():
    dviceId=device_id()
    map={"token":"","deviceId":dviceId,"data":{"app_code":"ch2","clipboard_text":"","channel_code":"channel://zwkab1bk"}}
    result=requests.post('http://api.bkrezwvjkc.com/cxapi/system/info',headers=get_header(),data=en(map))
    json_str=de(result.content)
    print(json_str)
    return json_str['data']['token']['token'], json_str['data']['token']['user_id'], dviceId


def bind():
    token, uid, device_id=get_token()
    map={'token':token+'_'+uid,'deviceId':device_id,'data':{'code':code}}
    response = requests.post('http://api.bkrezwvjkc.com/cxapi/user/bindParent', headers=get_header(), data=en(map))

    json_str=de(response.content)
    print(json_str)


bind()

某h软件-2

Re

所需工具

  • 逍遥模拟器
  • lamda
  • jadx
  • frida
  • 一双手

抓包 #

软件有root检测和代理检测,这里不讲绕过。代理不要挂。 安装lamda:

set HTTPS_PROXY=127.0.0.1:7890
git clone https://github.com/rev1si0n/lamda.git

rev1si0n/lamda: ⚡️ Android reverse engineering & automation framework | 史上最强安卓抓包/逆向/HOOK & 云手机/远程桌面/自动化辅助框架,你的工作从未如此简单快捷。

下载lamda-server-x86_64.tar.gz 以及busybox-x86_64

先把模拟器的root打开

# 在你下载好的目录打开终端
adb push lamda-server-x86_64.tar.gz /data/local/tmp
adb push busybox-x86_64 /data/local/tmp


adb shell
# 以下命令都是在adb shell中执行的
su

chmod 755 /data/local/tmp/busybox-x86_64
cd /data

/data/local/tmp/busybox-x86_64 tar -xzf /data/local/tmp/lamda-server-x86_64.tar.gz

rm /data/local/tmp/lamda-server-x864.tar.gz
rm /data/local/tmp/busybox-x86_64

启动lamda

继续上一步结尾

cd /data/local/tmp/server/bin

sh launch.sh

启动完成 Pasted image 20231216130555

...

淘金社区登陆

某个淘金社区,登陆有md5加密

用blutter 解析一下,找到hash_update的地址,放到frida的脚本中,在console的下面加一句

console.log('hexdump'+hexdump(args[0]))

打印出来4个uint32List,虽然看不懂,但是我们打印了hex

多看几次知道有随机的东西,在多看几次就知道应该是时间戳末尾在控制,反复测试得到所有出现过的字符,易得脚本


import requests
import hashlib
import time
def get_data(p):

    A=['gcw7eeh5','eegc15nnccccoqh5r7','15gcnncc','h5ccccgc15nnee/i','15gcnnq4oq/ih5oqccccee','15cc/ir7oqnnq4oqh5','nncc/i','h515r7q4oqcc','oqoqnnr7q4/i','h5nn']

    B=['eeoqh5','','h5ccoqeer7','oqr7','r7','oqccgc','oqccgcoqh515r7oqq4','oqgcoqnn/i','h515oqcc','oq']

  
    C=['','','/iq4','q4','op','','','','gc','2115oqr7q4/ioqccgc']
    # 16位时间戳
    t=str(int(time.time()*1000000))
    k=int(t[-1])

    # B164D0C43EDB3BAD89D1A073EBE9691D
    # 为固定值,可能跟随app版本所迭代
    
    #一下情况特殊出现,需要对换位置
    if k==8 or k==5:

        message= 'action=sendcode&phone={}&verifytoken={}&verifycodes={}{}B164D0C43EDB3BAD89D1A073EBE9691D..{}W1W2W3W4W5W6W7W8W901020304050607..{}{}'.format(p,'{system::verifytoken}','{system::verifytoken}',A[k],B[k],C[k],t)

    else:

        message= 'action=sendcode&phone={}&verifytoken={}&verifycodes={}{}W1W2W3W4W5W6W7W8W901020304050607..{}B164D0C43EDB3BAD89D1A073EBE9691D..{}{}'.format(p,'{system::verifytoken}','{system::verifytoken}',A[k],B[k],C[k],t)

    # print(message)
    return {'action':'sendcode','phone':p,'verifytoken':hashlib.md5(message.encode()).hexdigest(),'verifycodes':t}

data=get_data('19999994444')

resp=requests.get('http://bbs.taojingdaohang.cn/index.php',params=data)

print(resp.text)

测试正常发送短信

爱多多 分析

Re

前言 #

放假了,得去挣点学费,遂进厂,厂里的app,越权漏洞和信息泄露,以及基础的逆向分析

注:以下内容仅供学习和参考,请勿用于违法行为活动

登陆 #

应该是加密了,并且有key,app有壳,xx御安全,我用的frida-dexdump -FU脱几个dex出来

用jadx打开不错,改用GDA,网上有免费版

这里一个一个dex的搜,出现在classes02.dex中

image-20230509091845206

其中key是由你的工号,用异变凯撒变换,工号和密码在so中完成加密

image-20230509092050306

可以先hook看一下传进去的值,在GDA这个工具中可以直接右键函数可以直接hook,还是比较方便的(提前在adb中启动frida)这里打一下码,见谅

所里面非常简单,没有混淆之类,但是把传进去的key又做了一次异变凯撒变换,再进行des cbc加密

看到一个字符串,不免想到是iv,(函数名就是DESencypt)

image-20230509092527547

不过后来我试图重写时发现,加密内容非8整倍用0字节填充,结果不一样(但不影响下文)

用py复写整个登陆,可以上号,我发现返回包中尽然没有任何维持身份的东西(就很奇怪,登陆就是个摆设?)

信息泄露 #

  1. 身份信息泄露

    在访问我的资料,发现从服务器上请求中参数值需要工号(注意,以下信息量爆炸,统统码住)

    注:据我像老员工打听,工号就是按照顺序来的,工号起初设计应是考虑了1000w的人(已离职工号是已使用,不会在重启工号给别人使用,该洞即可遍历所有工号,但是是一个工程量巨大的事情)

    image-20230509093801861

  2. 薪资卡信息

    访问薪资卡,会要求我们输入登陆密码,还有一个摆设本地验证码,获取信息,请求如下

    image-20230509094613960

    同样工号加密了,搜索易得java 层DES加密,key is fga2dfe@

    image-20230509095013721

    拿到key,对返回结果一统解密,泄露银行卡卡号(个人),开户行,姓名,这里就不放图片了,很暴力

  3. 人脸图像

    进入厂区有些时候需要刷脸,也可刷厂牌,用户可以自己在app中采集人脸,(进厂前已收集一张)

    先获取图片路径和图片绑定的uuid(码住)

    image-20230509103521432

    随后向接口getImageByUrl获取图片,我试图尝试任意文件读取漏洞,但是似乎失败了,fuzz读取/etc/passwd和/etc/shadow返回空,其他404

    人脸图片在app中不是每个用户都有(有的自己删除了),影响:都有了你的sfz_id和name、phone,再加个人脸图附魔不过分吧,别人可能干一些非法认证,绑定一些人脸业务

越权 #

额,我也不知道这算不算越权

回到头来分析,好像楼掉了些甚么,看了看验证码,修改忘记密码之类

其中忘记密码是在登陆界面,会先输入你的工号,会想服务器获取工号绑定的手机号,app上是对手机号的中间4位打了"*“号,我一看包,卧槽,这么暴力吗,响应包中就是完整的手机号(又是一个任意信息泄露),

你以为就这,之后向服务器请求发送验证码,发送过来的验证码我发现竟然在请求包中(即验证码在本地随机产生一个6位数)

image-20230509100448703

我马上即想到任意用户密码修改,因为验证码由我们所控制,后来我发现根本没必要

接下来重置密码(只需要填写新密码),发现新大陆(超级暴力)

请求包中的内容pwd和EmpNo(工号)是上文提到的so中的加密方式,key也同理的

image-20230509100811427

那么就出现了,任意用户密码重置,实测我朋友已成功

更加暴力的 #

众所周知,厂里是有储物柜的,有电子锁和自带钥匙的那种

...

神图壁纸

import random
import time
def encode(_str):
    staticchars = "PXhw7UT1B0a9kQDKZsjIASmOezxYG4CHo5Jyfg2b8FLpEvRr3WtVnlqMidu6cN"
    encodechars = ""
    for i in range(len(_str)):
        num0 = staticchars.find(_str[i])
        if num0 == -1:
            code = _str[i]
        else:
            code = staticchars[(num0 + 3) % 62]
        num1 = random.randint(0, 61)
        num2 = random.randint(0, 61)
        encodechars += staticchars[num1] + code + staticchars[num2]
    return encodechars

# def encode1(_str):
#     s='PXhw7UT1B0a9kQDKZsjIASmOezxYG4CHo5Jyfg2b8FLpEvRr3WtVnlqMidu6cN'
#     en=''
#     for i in _str:

def timestamp():
    # return 1699883428
    return int(time.time())
def sign():
    return encode("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcGVuaWQiOiJvR29TZjVKczRPRF95R2JSUDlVVGpMY0RmNjc0IiwiZXhwaXJlX3RpbWUiOjE2OTczMDQ1MzV9.)FxVKiDH9rnMKEuTNMkRa7ibMrhp-Vdi-FHBgWOomdaQ###"+str(t))
    
import requests
t=timestamp()

headers = {
    'authority': 'stapi.youpengw.com',
    'accept': '*/*',
    'accept-language': 'zh-CN,zh;q=0.9',
    'content-type': 'application/json',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'cross-site',
    'sign': sign(),
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF XWEB/8447',
    'xweb_xhr': '1',
}
json_data = {
    'homepage_id': '233033',
    'classify_id': 5,
    'sort_type': 'default',
    'album_id': 0,
    'uid': 219050,
    'page': 1,
    'limit': 15,
    'action': 'cover',
    'version': 1,
    'appid': 'wx535ad18d22b24807',
    'openid': 'oGoSf5Js4OD_yGbRP9UTjLcDf674',
    'timestamp': t,
}

response = requests.post('https://stapi.youpengw.com/miniwechat/v1/natujun/photoList', headers=headers, json=json_data)
print(response.text)

# print(response.text)
# k=response.json()
# s=[]
# for i in k['data']['photoList']:
#     s.append(i['url'])
# print(s)

sign解密代码,能够解出token,但是还是要报错

...

Bika Anime

Re

bika 动漫,dddd

flutter 以前用过,但是没有arm64的so,所以就放弃了,今天一个朋发给我,发现居然后arm64的,那就别怪我不客气。

blutter 启动,dart 2.19 解析出来了

主要是看pp.txt和 addNames.py 这两个文件,找关键字,看有地址的,一般这种软件加密都是aes,所以直接搜索,然后挨个挨个frida hook,

blutter 会生成一个blutter_frida.js, 添加一句话

    const fn_addr = 0x7e1018;
    Interceptor.attach(libapp.add(fn_addr), {
        onEnter: function () {
            init(this.context);
            let objPtr = getArg(this.context, 0);
            const [tptr, cls, values] = getTaggedObjectValue(objPtr);
            console.log(`${cls.name}@${tptr.toString().slice(2)} =`, JSON.stringify(values, null, 2));
		    //就是添这句话,找关键字就行了,
            console.log('hexdump ==> '+hexdump(objPtr));
        }
    });

图片.png

多hook 几次基本拿下,然后就是猜,deviceId忙猜随机生成,拿uuid做md5取前16位,直接传上去,发现可以,最后就是喜闻乐见得绑定邀请码获得VIP,直接就是稳稳得拿下。

import requests, time, json, uuid ,hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

code='YMMTK'
cipher = AES.new(b'525202f9149e061d', AES.MODE_ECB)

def get_header():
    return {
        'user-agent': 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleDart/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17',
        'version': '1.0',
        'devicetype': 'android',
        'time': str(int(time.time()*1000)),
        'host': 'api.bkrezwvjkc.com',
        'content-type': 'application/x-www-form-urlencoded',
    }
def remove_pkcs7_padding(data: bytes, block_size: int = 16):
    if len(data) % block_size != 0:
        raise ValueError("Data length is not a multiple of the block size")

    padding_length = data[-1]
    if padding_length > block_size or padding_length <= 0:
        raise ValueError("Invalid padding detected")

    padding = data[-padding_length:]
    if any(byte != padding_length for byte in padding):
        raise ValueError("Inconsistent padding detected")

    return data[:-padding_length]

def en(map):
    plaintext_bytes_padded = pad(json.dumps(map).encode(), AES.block_size)
    return cipher.encrypt(plaintext_bytes_padded).hex()
def de(s):
    a=cipher.decrypt(s)
    a=remove_pkcs7_padding(a)
    return json.loads(a.decode().replace('\r','').replace('\\\\','\\'))

def device_id():
    random_uuid = uuid.uuid4()
    uuid_str = str(random_uuid)
    return hashlib.md5(uuid_str.encode()).hexdigest()[:16]

def get_token():
    dviceId=device_id()
    map={"token":"","deviceId":dviceId,"data":{"app_code":"ch2","clipboard_text":"","channel_code":"channel://zwkab1bk"}}
    result=requests.post('http://api.bkrezwvjkc.com/cxapi/system/info',headers=get_header(),data=en(map))
    json_str=de(result.content)
    print(json_str)
    return json_str['data']['token']['token'], json_str['data']['token']['user_id'], dviceId


def bind():
    token, uid, device_id=get_token()
    map={'token':token+'_'+uid,'deviceId':device_id,'data':{'code':code}}
    response = requests.post('http://api.bkrezwvjkc.com/cxapi/user/bindParent', headers=get_header(), data=en(map))

    json_str=de(response.content)
    print(json_str)


bind()

逆向3

Re

所需工具

  • 逍遥模拟器
  • lamda
  • jadx
  • frida
  • 一双手

抓包 #

软件有root检测和代理检测,这里不讲绕过。代理不要挂。 安装lamda:

set HTTPS_PROXY=127.0.0.1:7890
git clone https://github.com/rev1si0n/lamda.git

rev1si0n/lamda: ⚡️ Android reverse engineering & automation framework | 史上最强安卓抓包/逆向/HOOK & 云手机/远程桌面/自动化辅助框架,你的工作从未如此简单快捷。

下载lamda-server-x86_64.tar.gz 以及busybox-x86_64

先把模拟器的root打开

# 在你下载好的目录打开终端
adb push lamda-server-x86_64.tar.gz /data/local/tmp
adb push busybox-x86_64 /data/local/tmp


adb shell
# 以下命令都是在adb shell中执行的
su

chmod 755 /data/local/tmp/busybox-x86_64
cd /data

/data/local/tmp/busybox-x86_64 tar -xzf /data/local/tmp/lamda-server-x86_64.tar.gz

rm /data/local/tmp/lamda-server-x864.tar.gz
rm /data/local/tmp/busybox-x86_64

启动lamda

...

flutter 逆向小计

某个淘金社区,登陆有md5加密

用blutter 解析一下,找到hash_update的地址,放到frida的脚本中,在console的下面加一句

console.log('hexdump'+hexdump(args[0]))

打印出来4个uint32List,虽然看不懂,但是我们打印了hex

多看几次知道有随机的东西,在多看几次就知道应该是时间戳末尾在控制,反复测试得到所有出现过的字符,易得脚本


import requests
import hashlib
import time
def get_data(p):

    A=['gcw7eeh5','eegc15nnccccoqh5r7','15gcnncc','h5ccccgc15nnee/i','15gcnnq4oq/ih5oqccccee','15cc/ir7oqnnq4oqh5','nncc/i','h515r7q4oqcc','oqoqnnr7q4/i','h5nn']

    B=['eeoqh5','','h5ccoqeer7','oqr7','r7','oqccgc','oqccgcoqh515r7oqq4','oqgcoqnn/i','h515oqcc','oq']

  
    C=['','','/iq4','q4','op','','','','gc','2115oqr7q4/ioqccgc']
    # 16位时间戳
    t=str(int(time.time()*1000000))
    k=int(t[-1])

    # B164D0C43EDB3BAD89D1A073EBE9691D
    # 为固定值,可能跟随app版本所迭代
    
    #一下情况特殊出现,需要对换位置
    if k==8 or k==5:

        message= 'action=sendcode&phone={}&verifytoken={}&verifycodes={}{}B164D0C43EDB3BAD89D1A073EBE9691D..{}W1W2W3W4W5W6W7W8W901020304050607..{}{}'.format(p,'{system::verifytoken}','{system::verifytoken}',A[k],B[k],C[k],t)

    else:

        message= 'action=sendcode&phone={}&verifytoken={}&verifycodes={}{}W1W2W3W4W5W6W7W8W901020304050607..{}B164D0C43EDB3BAD89D1A073EBE9691D..{}{}'.format(p,'{system::verifytoken}','{system::verifytoken}',A[k],B[k],C[k],t)

    # print(message)
    return {'action':'sendcode','phone':p,'verifytoken':hashlib.md5(message.encode()).hexdigest(),'verifycodes':t}

data=get_data('19999994444')

resp=requests.get('http://bbs.taojingdaohang.cn/index.php',params=data)

print(resp.text)

测试正常发送短信

某H视频-mls

分析流程 #

这个软件呢,其实分析主要就是想要看他拉人获得vip的操作

抓包可以找到一个api

api/user/traveler

这之前token还没有得到,参数

{"deviceId": device, "dev": 随机3字符, "code": '{"p":"PDTVPD"}'}

这个code参数其实开始我也不知道是啥,走了一遍拉人的流程,发现app给我们的邀请链接有p参数,就是邀请码

https://z6mhir31k.xyz?p=PDTVPD

在进到网页中点击下载软件会将邀请码复制到我们的粘贴板上

{"p":"PDTVPD"}

当你进入app时,读取粘贴板的第一条数据

 public static String u() {
        ClipData v0 = FragmentAnim.f.getSystemService("clipboard").getPrimaryClip();
        String v1 = "";
        if(v0 != null && v0.getItemCount() > 0) {
            CharSequence v0_1 = v0.getItemAt(0).getText();
            if(TextUtils.isEmpty(v0_1)) {
                return v1;
            }
            else {
                return v0_1.toString();
            }
        }

        return v1;
    }

deviceId #

顺着上去就发现了deviceId的生成

public final void C() {
        String v0_2;
        String v3_1;
        String v1 = "everb";
        String v2 = "";
        if(a.a.exists()) {
            try {
                ObjectInputStream v0_1 = new ObjectInputStream(new FileInputStream(a.a));
                Object v3 = v0_1.readObject();
                try {
                    v0_1.close();
                    goto label_21;
                }
                catch(Exception v0) {
                }
            }
            catch(Exception v0) {
                v3_1 = v2;
            }

            Log.d(v1, v0.toString());
        }
        else {
            v3_1 = v2;
        }

    label_21:
        if(v3_1 == null) {
            Log.d(v1, "data == null");
            v3_1 = v2;
        }

        if(TextUtils.isEmpty(((CharSequence)v3_1))) {
            v0_2 = String.valueOf(System.currentTimeMillis() + UUID.randomUUID().getLeastSignificantBits());
            try {
                byte[] v0_4 = MessageDigest.getInstance("MD5").digest(v0_2.getBytes());
                StringBuilder v1_1 = new StringBuilder();
                int v5;
                for(v5 = 0; v5 < v0_4.length; ++v5) {
                    String v6 = Integer.toHexString(v0_4[v5] & 0xFF);
                    if(v6.length() < 2) {
                        v1_1.append(0);
                    }

                    v1_1.append(v6);
                }

                v3_1 = v1_1.toString();
            }

他会打开是个名为.CAD的文件,把deviceid写进去,如果像要看一遍生成新device,记得删除这个文件

...

某洗衣小程序

小程序逆向 #

  1. 抓包

    电脑版微信最新版抓不了包,用3.7.6一下的

    微信在登陆时,右上角把代理填上

    把AppData\Roaming\Tencent\WeChat\XPlugin\Plugins里面的东西删了,不影响使用

  1. 解密

    下载aen516954023/wxappUnpacker: 微信小程序本地应用包逆向工具 (github.com)

    解密wxapkg文件,在C:\Users\xxx\Documents\WeChat Files\Applet

    在解密的时候出现各种几把问题,均可参考百度,google等

    可能因为解密不出来wml和wss不能动态调试很难受

    wxapkg文件解包

    其他的单独解

  2. 逆向

    因为没有拿到wml和wss,所以只能静态分析

    还好比较简单

    image-20221018221138862

​ 因为不能调试,不知道最后一个sign传的什么参数,是做的md5

import requests
import execjs

phone='17628818007'
url='https://www.sharewash.cn/saywash/WashCallApi/common/user/requestVerifyCode.api'
data={'phoneNumber':phone,'ssid':'','tokenId':'','sign':''}

with open('../js/2.js','r',encoding='utf-8') as fp:
    js1=fp.read()
ssid=execjs.compile(js1).call('getTime')
data['ssid']=ssid
data['sign']=execjs.compile(js1).call('getsign',ssid,phone)
print(data)
function getsign() {
    var i, g = arguments;
    i = { 'phoneNumber': '17628818007', 'ssid': '', 'tokenId': '' }
    return i.ssid = getTime(), i.tokenId = false || "", i.sign = sign(i)
}
    function r(r) {
        var a = new Array(), n = r.length, s = parseInt(n / 4), f = n % 4, o = 0;
        for (o = 0; o < s; o++) a[o] = e(r.substring(4 * o + 0, 4 * o + 4));
        return f > 0 && (a[o] = e(r.substring(4 * o + 0, n))), a;
    }

    function e(r) {
        var e = r.length, a = new Array(64), n = 0, s = 0, f = 0, o = 0;
        if (e < 4) {
            for (n = 0; n < e; n++) {
                var c = r.charCodeAt(n);
                for (s = 0; s < 16; s++) {
                    var t = 1, l = 0;
                    for (l = 15; l > s; l--) t *= 2;
                    a[16 * n + s] = parseInt(c / t) % 2;
                }
            }
            for (f = e; f < 4; f++) {
                c = 0;
                for (o = 0; o < 16; o++) {
                    t = 1, l = 0;
                    for (l = 15; l > o; l--) t *= 2;
                    a[16 * f + o] = parseInt(c / t) % 2;
                }
            }
        } else for (n = 0; n < 4; n++) {
            c = r.charCodeAt(n);
            for (s = 0; s < 16; s++) {
                t = 1;
                for (l = 15; l > s; l--) t *= 2;
                a[16 * n + s] = parseInt(c / t) % 2;
            }
        }
        return a;
    }

    function a(r) {
        var e;
        switch (r) {
            case "0000":
                e = "0";
                break;

            case "0001":
                e = "1";
                break;

            case "0010":
                e = "2";
                break;

            case "0011":
                e = "3";
                break;

            case "0100":
                e = "4";
                break;

            case "0101":
                e = "5";
                break;

            case "0110":
                e = "6";
                break;

            case "0111":
                e = "7";
                break;

            case "1000":
                e = "8";
                break;

            case "1001":
                e = "9";
                break;

            case "1010":
                e = "A";
                break;

            case "1011":
                e = "B";
                break;

            case "1100":
                e = "C";
                break;

            case "1101":
                e = "D";
                break;

            case "1110":
                e = "E";
                break;

            case "1111":
                e = "F";
        }
        return e;
    }

    function n(r) {
        for (var e = "", n = 0; n < 16; n++) {
            for (var s = "", f = 0; f < 4; f++) s += r[4 * n + f];
            e += a(s);
        }
        return e;
    }

    function s(r, e) {
        var a = k(e), n = f(r), s = new Array(32), b = new Array(32), i = new Array(32), w = 0, v = 0, A = 0, y = 0, h = 0;
        for (A = 0; A < 32; A++) s[A] = n[A], b[A] = n[32 + A];
        for (w = 0; w < 16; w++) {
            for (v = 0; v < 32; v++) i[v] = s[v], s[v] = b[v];
            var g = new Array(48);
            for (y = 0; y < 48; y++) g[y] = a[w][y];
            var p = c(l(t(c(o(b), g))), i);
            for (h = 0; h < 32; h++) b[h] = p[h];
        }
        var I = new Array(64);
        for (w = 0; w < 32; w++) I[w] = b[w], I[32 + w] = s[w];
        return u(I);
    }

    function f(r) {
        for (var e = new Array(64), a = 0, n = 1, s = 0; a < 4; a++, n += 2, s += 2) for (var f = 7, o = 0; f >= 0; f--,
            o++) e[8 * a + o] = r[8 * f + n], e[8 * a + o + 32] = r[8 * f + s];
        return e;
    }

    function o(r) {
        for (var e = new Array(48), a = 0; a < 8; a++) e[6 * a + 0] = 0 == a ? r[31] : r[4 * a - 1],
            e[6 * a + 1] = r[4 * a + 0], e[6 * a + 2] = r[4 * a + 1], e[6 * a + 3] = r[4 * a + 2],
            e[6 * a + 4] = r[4 * a + 3], e[6 * a + 5] = 7 == a ? r[0] : r[4 * a + 4];
        return e;
    }

    function c(r, e) {
        for (var a = new Array(r.length), n = 0; n < r.length; n++) a[n] = r[n] ^ e[n];
        return a;
    }

    function t(r) {
        for (var e = new Array(32), a = "", n = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]], s = [[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]], f = [[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]], o = [[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]], c = [[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]], t = [[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]], l = [[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]], u = [[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]], k = 0; k < 8; k++) {
            var i, w;
            switch (i = 2 * r[6 * k + 0] + r[6 * k + 5], w = 2 * r[6 * k + 1] * 2 * 2 + 2 * r[6 * k + 2] * 2 + 2 * r[6 * k + 3] + r[6 * k + 4],
            k) {
                case 0:
                    a = b(n[i][w]);
                    break;

                case 1:
                    a = b(s[i][w]);
                    break;

                case 2:
                    a = b(f[i][w]);
                    break;

                case 3:
                    a = b(o[i][w]);
                    break;

                case 4:
                    a = b(c[i][w]);
                    break;

                case 5:
                    a = b(t[i][w]);
                    break;

                case 6:
                    a = b(l[i][w]);
                    break;

                case 7:
                    a = b(u[i][w]);
            }
            e[4 * k + 0] = parseInt(a.substring(0, 1)), e[4 * k + 1] = parseInt(a.substring(1, 2)),
                e[4 * k + 2] = parseInt(a.substring(2, 3)), e[4 * k + 3] = parseInt(a.substring(3, 4));
        }
        return e;
    }

    function l(r) {
        var e = new Array(32);
        return e[0] = r[15], e[1] = r[6], e[2] = r[19], e[3] = r[20], e[4] = r[28], e[5] = r[11],
            e[6] = r[27], e[7] = r[16], e[8] = r[0], e[9] = r[14], e[10] = r[22], e[11] = r[25],
            e[12] = r[4], e[13] = r[17], e[14] = r[30], e[15] = r[9], e[16] = r[1], e[17] = r[7],
            e[18] = r[23], e[19] = r[13], e[20] = r[31], e[21] = r[26], e[22] = r[2], e[23] = r[8],
            e[24] = r[18], e[25] = r[12], e[26] = r[29], e[27] = r[5], e[28] = r[21], e[29] = r[10],
            e[30] = r[3], e[31] = r[24], e;
    }

    function u(r) {
        var e = new Array(64);
        return e[0] = r[39], e[1] = r[7], e[2] = r[47], e[3] = r[15], e[4] = r[55], e[5] = r[23],
            e[6] = r[63], e[7] = r[31], e[8] = r[38], e[9] = r[6], e[10] = r[46], e[11] = r[14],
            e[12] = r[54], e[13] = r[22], e[14] = r[62], e[15] = r[30], e[16] = r[37], e[17] = r[5],
            e[18] = r[45], e[19] = r[13], e[20] = r[53], e[21] = r[21], e[22] = r[61], e[23] = r[29],
            e[24] = r[36], e[25] = r[4], e[26] = r[44], e[27] = r[12], e[28] = r[52], e[29] = r[20],
            e[30] = r[60], e[31] = r[28], e[32] = r[35], e[33] = r[3], e[34] = r[43], e[35] = r[11],
            e[36] = r[51], e[37] = r[19], e[38] = r[59], e[39] = r[27], e[40] = r[34], e[41] = r[2],
            e[42] = r[42], e[43] = r[10], e[44] = r[50], e[45] = r[18], e[46] = r[58], e[47] = r[26],
            e[48] = r[33], e[49] = r[1], e[50] = r[41], e[51] = r[9], e[52] = r[49], e[53] = r[17],
            e[54] = r[57], e[55] = r[25], e[56] = r[32], e[57] = r[0], e[58] = r[40], e[59] = r[8],
            e[60] = r[48], e[61] = r[16], e[62] = r[56], e[63] = r[24], e;
    }

    function b(r) {
        var e = "";
        switch (r) {
            case 0:
                e = "0000";
                break;

            case 1:
                e = "0001";
                break;

            case 2:
                e = "0010";
                break;

            case 3:
                e = "0011";
                break;

            case 4:
                e = "0100";
                break;

            case 5:
                e = "0101";
                break;

            case 6:
                e = "0110";
                break;

            case 7:
                e = "0111";
                break;

            case 8:
                e = "1000";
                break;

            case 9:
                e = "1001";
                break;

            case 10:
                e = "1010";
                break;

            case 11:
                e = "1011";
                break;

            case 12:
                e = "1100";
                break;

            case 13:
                e = "1101";
                break;

            case 14:
                e = "1110";
                break;

            case 15:
                e = "1111";
        }
        return e;
    }

    function k(r) {
        var e = new Array(56), a = new Array();
        a[0] = new Array(), a[1] = new Array(), a[2] = new Array(), a[3] = new Array(),
            a[4] = new Array(), a[5] = new Array(), a[6] = new Array(), a[7] = new Array(),
            a[8] = new Array(), a[9] = new Array(), a[10] = new Array(), a[11] = new Array(),
            a[12] = new Array(), a[13] = new Array(), a[14] = new Array(), a[15] = new Array();
        for (var n = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], s = 0; s < 7; s++) for (var f = 0, o = 7; f < 8; f++,
            o--) e[8 * s + f] = r[8 * o + s];
        s = 0;
        for (s = 0; s < 16; s++) {
            var c = 0, t = 0;
            for (f = 0; f < n[s]; f++) {
                for (c = e[0], t = e[28], o = 0; o < 27; o++) e[o] = e[o + 1], e[28 + o] = e[29 + o];
                e[27] = c, e[55] = t;
            }
            var l = new Array(48);
            switch (l[0] = e[13], l[1] = e[16], l[2] = e[10], l[3] = e[23], l[4] = e[0], l[5] = e[4],
            l[6] = e[2], l[7] = e[27], l[8] = e[14], l[9] = e[5], l[10] = e[20], l[11] = e[9],
            l[12] = e[22], l[13] = e[18], l[14] = e[11], l[15] = e[3], l[16] = e[25], l[17] = e[7],
            l[18] = e[15], l[19] = e[6], l[20] = e[26], l[21] = e[19], l[22] = e[12], l[23] = e[1],
            l[24] = e[40], l[25] = e[51], l[26] = e[30], l[27] = e[36], l[28] = e[46], l[29] = e[54],
            l[30] = e[29], l[31] = e[39], l[32] = e[50], l[33] = e[44], l[34] = e[32], l[35] = e[47],
            l[36] = e[43], l[37] = e[48], l[38] = e[38], l[39] = e[55], l[40] = e[33], l[41] = e[52],
            l[42] = e[45], l[43] = e[41], l[44] = e[49], l[45] = e[35], l[46] = e[28], l[47] = e[31],
            s) {
                case 0:
                    for (var u = 0; u < 48; u++) a[0][u] = l[u];
                    break;

                case 1:
                    for (u = 0; u < 48; u++) a[1][u] = l[u];
                    break;

                case 2:
                    for (u = 0; u < 48; u++) a[2][u] = l[u];
                    break;

                case 3:
                    for (u = 0; u < 48; u++) a[3][u] = l[u];
                    break;

                case 4:
                    for (u = 0; u < 48; u++) a[4][u] = l[u];
                    break;

                case 5:
                    for (u = 0; u < 48; u++) a[5][u] = l[u];
                    break;

                case 6:
                    for (u = 0; u < 48; u++) a[6][u] = l[u];
                    break;

                case 7:
                    for (u = 0; u < 48; u++) a[7][u] = l[u];
                    break;

                case 8:
                    for (u = 0; u < 48; u++) a[8][u] = l[u];
                    break;

                case 9:
                    for (u = 0; u < 48; u++) a[9][u] = l[u];
                    break;

                case 10:
                    for (u = 0; u < 48; u++) a[10][u] = l[u];
                    break;

                case 11:
                    for (u = 0; u < 48; u++) a[11][u] = l[u];
                    break;

                case 12:
                    for (u = 0; u < 48; u++) a[12][u] = l[u];
                    break;

                case 13:
                    for (u = 0; u < 48; u++) a[13][u] = l[u];
                    break;

                case 14:
                    for (u = 0; u < 48; u++) a[14][u] = l[u];
                    break;

                case 15:
                    for (u = 0; u < 48; u++) a[15][u] = l[u];
            }
        }
        return a;
    }

    // module.exports = {
    function getTime() {
        return function (a, f, o, c) {
            var t, l, u, b, k, i, w = a.length, v = "";
            null != f && "" != f && (t = r(f), b = t.length);
            null != o && "" != o && (l = r(o), k = l.length);
            null != c && "" != c && (u = r(c), i = u.length);
            if (w > 0) if (w < 4) {
                var A = e(a);
                if (null != f && "" != f && null != o && "" != o && null != c && "" != c) {
                    for ($ = A, y = 0; y < b; y++) $ = s($, t[y]);
                    for (C = 0; C < k; C++) $ = s($, l[C]);
                    for (d = 0; d < i; d++) $ = s($, u[d]);
                    x = $;
                } else if (null != f && "" != f && null != o && "" != o) {
                    for ($ = A, y = 0; y < b; y++) $ = s($, t[y]);
                    for (C = 0; C < k; C++) $ = s($, l[C]);
                    x = $;
                } else if (null != f && "" != f) {
                    var y = 0;
                    for ($ = A, y = 0; y < b; y++) $ = s($, t[y]);
                    x = $;
                }
                v = n(x);
            } else {
                var h = parseInt(w / 4), g = w % 4, p = 0;
                for (p = 0; p < h; p++) {
                    var I = e(a.substring(4 * p + 0, 4 * p + 4));
                    if (null != f && "" != f && null != o && "" != o && null != c && "" != c) {
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        for (C = 0; C < k; C++) $ = s($, l[C]);
                        for (d = 0; d < i; d++) $ = s($, u[d]);
                        x = $;
                    } else if (null != f && "" != f && null != o && "" != o) {
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        for (C = 0; C < k; C++) $ = s($, l[C]);
                        x = $;
                    } else if (null != f && "" != f) {
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        x = $;
                    }
                    v += n(x);
                }
                if (g > 0) {
                    var x;
                    I = e(a.substring(4 * h + 0, w));
                    if (null != f && "" != f && null != o && "" != o && null != c && "" != c) {
                        var d;
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        for (C = 0; C < k; C++) $ = s($, l[C]);
                        for (d = 0; d < i; d++) $ = s($, u[d]);
                        x = $;
                    } else if (null != f && "" != f && null != o && "" != o) {
                        var C;
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        for (C = 0; C < k; C++) $ = s($, l[C]);
                        x = $;
                    } else if (null != f && "" != f) {
                        var $;
                        for ($ = I, y = 0; y < b; y++) $ = s($, t[y]);
                        x = $;
                    }
                    v += n(x);
                }
            }
            return v;
        }("saywash#" + new Date().valueOf(), "hlF#$1x)", "hlG#$2x)", "hlH#$3x)");
    }


function sign(e) {
    Array.prototype.indexVf = function (r) {
        for (var e = 0; e < this.length; e++) if (this[e] == r) return e;
    }
    var t = [], n = [], i = [];
    for (var s in e) t.push(s), i.push(s), n.push(e[s]);
    for (var o = i.sort(), u = "SING=HLYF", f = 0; f < o.length; f++) if ("tokenId" != o[f] && "ssid" != o[f] && "sign" != o[f]) {
        var a = t.indexVf(o[f]);
        "" != n[a] && null != n[a] && (u = u + "&" + o[f] + "=" + n[a]);
    }

    return "SING=HLYF" == (u = u.toUpperCase()) && (u = "SING=HLYF&"), u = md5(u);
}

function md5(string) {
    function md5_RotateLeft(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }

    function md5_AddUnsigned(lX, lY) {
        var lX4, lY4, lX8, lY8, lResult;
        lX8 = (lX & 0x80000000);
        lY8 = (lY & 0x80000000);
        lX4 = (lX & 0x40000000);
        lY4 = (lY & 0x40000000);
        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
        if (lX4 & lY4) {
            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
            } else {
                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
            }
        } else {
            return (lResult ^ lX8 ^ lY8);
        }
    }

    function md5_F(x, y, z) {
        return (x & y) | ((~x) & z);
    }

    function md5_G(x, y, z) {
        return (x & z) | (y & (~z));
    }

    function md5_H(x, y, z) {
        return (x ^ y ^ z);
    }

    function md5_I(x, y, z) {
        return (y ^ (x | (~z)));
    }

    function md5_FF(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_GG(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_HH(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_II(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    };

    function md5_WordToHex(lValue) {
        var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
        for (lCount = 0; lCount <= 3; lCount++) {
            lByte = (lValue >>> (lCount * 8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
        }
        return WordToHexValue;
    };

    function md5_Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    };
    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
    var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
    var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
    var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
    string = md5_Utf8Encode(string);
    x = md5_ConvertToWordArray(string);
    a = 0x67452301;
    b = 0xEFCDAB89;
    c = 0x98BADCFE;
    d = 0x10325476;
    for (k = 0; k < x.length; k += 16) {
        AA = a;
        BB = b;
        CC = c;
        DD = d;
        a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
        d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
        c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
        b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
        a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
        d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
        c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
        b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
        a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
        d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
        c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
        b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
        a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
        d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
        c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
        b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
        a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
        d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
        c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
        b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
        a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
        d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453);
        c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
        b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
        a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
        d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
        c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
        b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
        a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
        d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
        c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
        b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
        a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
        d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
        c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
        b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
        a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
        d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
        c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
        b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
        a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
        d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
        c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
        b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
        a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
        d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
        c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
        b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
        a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244);
        d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
        c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
        b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
        a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
        d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
        c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
        b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
        a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
        d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
        c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314);
        b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
        a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
        d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
        c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
        b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
        a = md5_AddUnsigned(a, AA);
        b = md5_AddUnsigned(b, BB);
        c = md5_AddUnsigned(c, CC);
        d = md5_AddUnsigned(d, DD);
    }
    return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase();
}