(文章目录)
前言
Unicorn是一个基于Ruby语言编写的Web服务器,它采用了多进程和多线程的方式来提高并发性能。
下面是Unicorn的基本流程:
-
Unicorn启动时会创建一个Master进程和多个Worker进程(默认为4个)。
-
Master进程会监听一个socket,接收来自外部的请求。
-
当有请求到达时,Master进程会将其发送给一个空闲的Worker进程。
-
Worker进程会处理请求,返回响应给Master进程。
-
Master进程将响应发送给客户端,并继续监听新的请求。
-
如果Worker进程出现异常或超时,Master进程会重新启动一个新的Worker进程,以保证系统的稳定性和可用性。
除此之外,Unicorn还提供了一些高级特性,如预加载应用程序、调整Worker进程的数量等,以进一步提高系统的性能和可伸缩性。
一、Triangle
1.题目
2.答题
2.1 源码分析
进入界面,查看源码,发现三个js文件 login界面的登录函数,用到了secret.js文件 1、查看secret.js文件
function test_pw(e, _) {
var t = stoh(atob(getBase64Image("eye")))
, r = 4096
, m = 8192
, R = 12288
, a = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
a.reg_write_i32(uc.ARM_REG_R9, m),
a.reg_write_i32(uc.ARM_REG_R10, R),
a.reg_write_i32(uc.ARM_REG_R8, _.length),
a.mem_map(r, 4096, uc.PROT_ALL);
for (var o = 0; o < o1.length; o++)
a.mem_write(r + o, [t[o1[o]]]);
a.mem_map(m, 4096, uc.PROT_ALL),
a.mem_write(m, stoh(_)),
a.mem_map(R, 4096, uc.PROT_ALL),
a.mem_write(R, stoh(e));
var u = r
, c = r + o1.length;
return a.emu_start(u, c, 0, 0),
a.reg_read_i32(uc.ARM_REG_R5)
}
function enc_pw(e) {
var _ = stoh(atob(getBase64Image("frei")))
, t = 4096
, r = 8192
, m = 12288
, R = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
R.reg_write_i32(uc.ARM_REG_R8, r),
R.reg_write_i32(uc.ARM_REG_R9, m),
R.reg_write_i32(uc.ARM_REG_R10, e.length),
R.mem_map(t, 4096, uc.PROT_ALL);
for (var a = 0; a < o2.length; a++)
R.mem_write(t + a, [_[o2[a]]]);
R.mem_map(r, 4096, uc.PROT_ALL),
R.mem_write(r, stoh(e)),
R.mem_map(m, 4096, uc.PROT_ALL);
var o = t
, u = t + o2.length;
return R.emu_start(o, u, 0, 0),
htos(R.mem_read(m, e.length))
}
function get_pw() {
for (var e = stoh(atob(getBase64Image("templar"))), _ = "", t = 0; t < o3.length; t++)
_ += String.fromCharCode(e[o3[t]]);
return _
}
都用unicorn框架仿真ARM,这得逆向了啊
2、查看 util.js
定义了get_pw()函数里用到的三个函数
function stoh(t) {
return t.split("").map(function(t) {
return t.charCodeAt(0)
})
}
function htos(t) {
return String.fromCharCode.apply(String, t)
}
function getBase64Image(t) {
var e = document.getElementById(t)
, a = document.createElement("canvas");
a.width = e.width,
a.height = e.height;
var n = a.getContext("2d");
n.drawImage(e, 0, 0);
var r = a.toDataURL("image/png");
return r.replace(/^data:image\/(png|jpeg);base64,/, "")
}
3、unicorn.js
unicorn.js非常大是JavaScript框架的源码在secret.js中用于仿真ARM
思路
- 根据index,get_pw()应该输出一个固定值
- enc_pw和test_pw函数仿真ARM,需要逆向了解它在干嘛
- 然后想办法满足index里的login
2.2 函数分析
1、尝试直接在console输出get_pw()
得到XYzaSAAX_PBssisodjsal_sSUVWZYYYb
这样util就不用管了
2、enc_pw()
分析enc_pw()函数我们发现题目是采用 ARM 构架的模拟器来实现加密函数,通过以下exp,可以获取到ARM的16进制码
function enc_pw(e) {
var _ = stoh(atob(getBase64Image("frei")))
, t = 4096
, r = 8192
, m = 12288
, R = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
R.reg_write_i32(uc.ARM_REG_R8, r),
R.reg_write_i32(uc.ARM_REG_R9, m),
R.reg_write_i32(uc.ARM_REG_R10, e.length),
R.mem_map(t, 4096, uc.PROT_ALL);
for (var a = 0; a < o2.length; a++)
R.mem_write(t + a, [_[o2[a]]]);
R.mem_map(r, 4096, uc.PROT_ALL),
R.mem_write(r, stoh(e)),
R.mem_map(m, 4096, uc.PROT_ALL);
var o = t
, u = t + o2.length;
return R.emu_start(o, u, 0, 0),
htos(R.mem_read(m, e.length))
}
转换为
function getARM1(){
var x = stoh(atob(getBase64Image("frei")));
var output = new Array();
for(var i = 0; i < o1.length ; i++){
output[i] = x[o1[i]];
}
return output;
}
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}
toHexString(getARM1())
输出
得到
0800a0e10910a0e10a20a0e10030a0e30050a0e30040d0e5010055e30100001a036003e2064084e0064084e2015004e20040c1e5010080e2011081e2013083e2020053e1f2ffffba0000a0e30010a0e30020a0e30030a0e30040a0e30050a0e30060a0e30070a0e30090a0e300a0a0e3
转换为ARM:https://armconverter.com/?disasm
得到
mov r0, r8 //r8根据secret.js,赋值了r=8192,在这里赋值给r0
mov r1, sb //sb是静态基址寄存器,与r9同义,根据secret.js,赋值了m=12288,这里赋值给r1
mov r2, sl //sl是堆栈限制寄存器,与r10同义,根据secret.js,赋值了输入e的长度,这里赋值给r2
mov r3, #0 //r3初始化为0
mov r5, #0 //r5初始化为0
ldrb r4, [r0] //将存储器地址为R0的字节数据读入寄存器R4,并将R4的高24位清零,这里在rom中是0x14
cmp r5, #1 //将R5的值与1相减,结果存在标志位中
bne #0x28 //根据标志位的结果,判断R5与1是否相等,若不相等则跳转到0x28处
and r6, r3, #3 //将R3和3相与结果传入R6,相当于截取R3二进制最后两位传入R6
add r4, r4, r6 //将R4 与R6相加的结果传入R4
add r4, r4, #6 //R4加6,这里在rom中是0x28
and r5, r4, #1 //将R4和1相与的结果传入R5,若R4为偶数则R5=0反之R5=1
strb r4, [r1] //将R4的低8位传入以R1为基址的存储器地址中
add r0, r0, #1 //R0加一
add r1, r1, #1 //R1加一
add r3, r3, #1 //R3加一
cmp r3, r2 //将R3与R2相减,结果存在标志位中
blt #0x14 //根据标志位的结果判断R3是否小于R2若小于则跳转到0x14处,即若计数器小于输入密码长度则继续循环
mov r0, #0 //这里往下都是清零
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov sb, #0
mov sl, #0
3、test_pw()
function getARM2(){
var x = stoh(atob(getBase64Image("eye")));
var output = new Array();
for(var i = 0; i < o1.length ; i++){
output[i] = x[o1[i]];
}
return output;
}
得到
0900a0e10a10a0e10830a0e10040a0e30050a0e300c0a0e30020d0e50060d1e5056086e201c004e200005ce30000000a036046e2060052e10500001a010080e2011081e2014084e2030054e1f1ffffba0150a0e30000a0e30010a0e30020a0e30030a0e30040a0e30060a0e30070a0e30080a0e30090a0e300a0a0e300c0a0e3
mov r0, sb //向r0赋值m = 8192
mov r1, sl //向r1赋值R = 12288
mov r3, r8 //向r3赋值输入值的长度
mov r4, #0 //初始化r4
mov r5, #0 //初始化r5
mov ip, #0 //初始化IP
ldrb r2, [r0] //将存储器地址为R0的字节数据读入寄存器R2,并将R2的高24位清零,此处在rom中是0x18
ldrb r6, [r1] //将存储器地址为R1的字节数据读入寄存器R6,并将R6的高24位清零
add r6, r6, #5 //将R6加5的结果传入R6
and ip, r4, #1 //将R4与1相与的结果传入IP
cmp ip, #0 //判断IP与0是否相等
beq #0x34 //如果IP==0,即R4是偶数,将会跳转到0x34处
sub r6, r6, #3 //else,即如果IP!=0,即R4是奇数,将R6减3的结果传入R6
cmp r2, r6 //判断R2与R6是否相等,此处在rom中是0x34
bne #0x54 //如果R2与R6不相等则跳转到0x54
add r0, r0, #1 //R0加一
add r1, r1, #1 //R1加一
add r4, r4, #1 //R4加一
cmp r4, r3 //比较R4与R3的大小
blt #0x18 //如果R4小于R3则跳转到0x18
mov r5, #1 //设置r5为1
mov r0, #0 //这里往下都是清零,此处在rom中是0x54
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov sb, #0
mov sl, #0
mov ip, #0
2.3 流程分析
1、enc_pw函数的流程如下
def enc_pw(e):
res = ''
f = 0
for i, c in enumerate(e):
c = ord(c)
if f == 1:
c += i & 3
c += 6
f = c & 1
res += chr(c)
return res
2、test_pw函数的流程如下:
def test_pw(e, t):
for i, (c, d) in enumerate(zip(e, t)):
c, d = ord(c), ord(d)
c += 5
if i & 1:
c -= 3
if c != d:
return 0
return 1
2.4 脚本
搞定了enc_pw和test_pw函数的python表示,那再搞个满足index里login的输入,逆着搞一下就是了
import string
def enc_pw(e):
res = ''
f = 0
for i, c in enumerate(e):
c = ord(c)
if f == 1:
c += i & 3
c += 6
f = c & 1
res += chr(c)
return res
encrypted = 'XYzaSAAX_PBssisodjsal_sSUVWZYYYb' # get_pw()的输出结果
flag = ''
# 逆向test_pw和index的login
for i, c in enumerate(encrypted):
c = ord(c)
c -= 5
if i & 1:
c += 3
for d in string.printable:
if enc_pw(flag + d)[i] == chr(c):
flag += d
break
print flag
得到flag:flag:{MPmVH94PTH7hhafgYahYaVfKJNLRNQLZ}