16bit
16进制的汇编阅读训练。汇编贼烦,还好师傅手下留情,逻辑段一点不复杂,拖进ida//ida牛逼,查看start函数,跟到sub_100ac下面,看的出来这个是主函数
mov di, 3
call sub_10115
mov di, 24h ; ‘$’
call sub_10123
mov bx, 24h ; ‘$’//输入
add bx, 1
cmp byte ptr [bx], 23h ; ‘#’//不是35个字符gg
jnz short loc_100DD
之后跳转这儿
mov di, 26h ; ‘&’
call sub_100F9
mov si, 76h ; ‘v’
mov dx, 23h ; ‘#’
call sub_100E4//这个是对比,对比输入的跟偏移值位76h的地方的是否相等,相等ok
test ax, ax
jnz short loc_100DD
其中sub_100f9是解释器
进去loc_100FE:
mov al, [bx+si]
mov dl, al
mov cl, 3
shr al, cl//input[i]>>3
mov cl, 5
shl dl, cl//input[i]<<5
xor al, dl//之后抑或操作下
mov [bx+si], al
inc si
cmp si, 23h ; ‘#’
jnz short loc_100FE
之后很明显了,逆向的话就是将原本存在内存中用来对比的//就是加密好的那个先右移3位,再左移5位,之后抑或,贴逆向代码
#include<stdio.h>
int main(){
int a[35]={0xc9,0x68,0x8a,0xc8,0x6f,0x7,0x6,0xf,0x7,0xc6,0xeb,0x86,0x6e,0x6e,0x66,0xad,0x4c,0x8d,
0xac,0xeb,0x26,0x6e,0xeb,0xcc,0xae,0xcd,0x8c,0x86,0xad,0x66,0xcd,0x8e,0x86,0x8d,0xaf};
for(int i=0;i<35;i++){
int front5=0;
int last3=0;
front5=a[i]<<3;
last3=a[i]>>5;
a[i]=front5 ^ last3;
printf("%c",a[i]);
}
2.后门
大水题,找函数,按r,注意xx=0,x0=x,之后就是个换位操作,结束。
3.box
王师傅的box,一开始没做出来的,因为找不到那个数组中的20,后来跟队友闲扯发现有段函数不能伪c,怀疑有猫腻,于是读汇编,贴汇编:
sub_400EB9 proc near ; DATA XREF:.init_array:0000000000601E10↓o
.text:0000000000400EB9
.text:0000000000400EB9 var_4 = dword ptr -4
.text:0000000000400EB9
.text:0000000000400EB9 ; __unwind {
.text:0000000000400EB9 push rbp
.text:0000000000400EBA mov rbp, rsp
.text:0000000000400EBD mov [rbp+var_4], 0
.text:0000000000400EC4 jmp short loc_400EE6
.text:0000000000400EC6 ;---------------------------------------------------------------------------
.text:0000000000400EC6
.text:0000000000400EC6 loc_400EC6: ; CODE XREF:sub_400EB9+34↓j
.text:0000000000400EC6 mov eax, [rbp+var_4]
.text:0000000000400EC9 cdqe
.text:0000000000400ECB movzx edx, byte_6020A0[rax]
.text:0000000000400ED2 mov eax, [rbp+var_4]
.text:0000000000400ED5 xor edx, eax
.text:0000000000400ED7 mov eax, [rbp+var_4]
.text:0000000000400EDA cdqe
.text:0000000000400EDC mov byte_6020A0[rax], dl
.text:0000000000400EE2 add [rbp+var_4], 1
.text:0000000000400EE6
.text:0000000000400EE6 loc_400EE6: ; CODE XREF:sub_400EB9+B↑j
.text:0000000000400EE6 cmp [rbp+var_4], 191
.text:0000000000400EED jle short loc_400EC6
.text:0000000000400EEF push rax
.text:0000000000400EF0 xor rax, rax
.text:0000000000400EF3 jz short loc_400EF9
.text:0000000000400EF5 add rsp, 40
.text:0000000000400EF9
.text:0000000000400EF9 loc_400EF9: ; CODE XREF:sub_400EB9+3A↑j
.text:0000000000400EF9 pop rax
.text:0000000000400EFA nop
.text:0000000000400EFB pop rbp
.text:0000000000400EFC retn
读了下,发现是对那个后来比较的那个数组进行加密,加密方法是a[i]=a[i]^I;
之后加密完得到地图:
//为了好看我把20改成2了,这样对齐
//a,b,c是三个特殊的起始点,后面会写
之后看主函数,最上面是那个神奇(可恶)的兔子函数,里面会有个sub_400796(),点进去发现是个初始化点坐标//就是我标出的a,b,c三点的初始坐标
之后,在主函数里面会有四个函数,分别对应4,5,W,0的情况,鉴于差不多,我就只发4的情况的那个函数的解析://那些变量被我用n健换成了a,b,i,j,n,m,看着舒服
_int64 sub_400C62()
{
__int64 result; // rax
result = (unsigned __int8)byte_6020A0[16 * i - 1 + j];
if( (_BYTE)result != 8 )
if ( j - 1 != m || i != n )
{
if ( j - 1 == a && i == b )
{
if ( byte_6020A0[16 * b - 1 + a] == 8 )
++j;
else
sub_400A1C(&a);
}
else if ( byte_6020A0[16 * n - 1 + m] == 8 )
{
++j;
}
else
{
sub_400A1C(&m);
}
result= (unsigned int)(j-- - 1);
}
return result;
}
之后三个类似,最后得出4左,5下,W上,0右
这个是个推箱子游戏
玩吧,之后把操作情况交上去就可以出flag了
4.基本操作:
直接看main
puts(“Input flag:”);
__isoc99_scanf("%64s", input); // 输入的是个树
i =0;
sub_400666(0); // 树的前序遍历
if(!strcmp(&s1, “bcec8d7dcda25d91ed3e0b720cbb6cf202b09fedbc3e017774273ef5d5581794”))
{
memset(&s1, 0, 0x80uLL);
i= 0;
sub_4006BE(0, 0LL); // 树的中序遍历
if ( !strcmp(&s1,“7d8dcdcaed592e1dcb07e02c36bcb2f0bf9e0bdcb0e13777237e25fd48515974”) )
printf("TQL! TQL! flag: nctf{%s}\n", input);
else
puts("Emmmm.....");
result = 0LL;
}
else
{
puts(“GG!”);
result = 0LL;
}
return result;
}
然后就很清楚了,这个是个给了你二叉树前序遍历下来后的字符串跟中序遍历的字符串
之后注意一个这个输入的是个字符串,所以画下来是个标准二叉树,标准的!!!所以每个节点都是有左右子树的!,64个字符,就是6层带最后还有一个连着,之后开始就很舒服了,就根据前序遍历的规则挨个写//其实这个是a掉后看着树才想到的,一开始做的时候,感谢我们队的密码手帮我手算出来。//都快算哭了,特别是知道可以这么玩后。
所以可以写个程序,输出64之内的标准二叉树前序遍历之后的输出顺序,之后跟题目中的对照,再得到flag。
贴代码
#include
using namespace std;
void pro(int i)
{
if(i<=63)
{
cout<<i<<"";
pro(2*i+1);
pro(2*i+2);
}
else
return;
}
int main()
{
pro(0);
}
得到输出顺序,对比得flag
还有题wcyvm,不知道为什么指令集好像搞崩掉了,先鸽着。
Ceypto
Math of homura
放到vscode中看源码,先贴源码
import gmpy2
import random
import GetFlag
c =random.randint(2 ** 127,2**128)
c = gmpy2.mpfr©
print “Homura sent some message afterdisappearing.”
print “But it was encoded and no onecan understand it.”
print “Fortunately you findsomething:”
print c
print “May be it will be useful.”
flag = GetFlag.getFlag()
mess = GetFlag.message()
i = 1
print “Here is the encodedmessage”
for ch in mess:
b=random.randint(2 ** 127,2**128)
b= gmpy2.mpfr(b)
a= gmpy2.root(b*b+c*c,2)
x= gmpy2.mpfr((gmpy2.mpfr(ord(ch))/128.0)*(a))
y= gmpy2.root((b*b-((b*b)/(a*a))*(x*x)),2)
l1= gmpy2.root((((x+c)**2)+(y**2)),2)
l2= gmpy2.root((((x-c)**2)+(y**2)),2)
print"l1[%d]:"%i
printl1
print"l2[%d]:"%i
printl2
i+=1
print “Now tell me what homurasaid?”
ans = raw_input()
if ans == mess:
printflag
else:
print"No you did not understand what homura said."
然后数学计算嘛,算下来是
L1=a+ch*c/128
L2=a-ch*c/128
之后l1-l2就是ch*c/64,c是给出了的
然后将它输出的东西进行操作,得到ascll码表,不过注意的是它这个居然是变化的!!!一开始以为是静态不变的直接用上次得到的数据逆向结果答案错了,所以要用pwn的思路。。。
之后还有个坑就是,它算下来不是精确的整数,会有微小偏差,如果直接变成字符会报错,所以还要+0.5后去除小数,贴代码
from pwn import *
r = remote(‘ctfgame.acdxvfsvd.net’, 30002)
r.recvuntil(‘Your Token:\n’)
r.sendline(‘a8x69OokWdZJrRAyfCzhKFSiu6tlS8bg’)
r.recvuntil(‘something:\n’)
c=r.recvline()
payload =’’
r.recvuntil(‘message\n’)
for i in range(261):
a = r.recvline(keepends=True)
l1 = r.recvline(keepends=True)
a = r.recvline(keepends=True)
l2 = r.recvline(keepends=True)
k=float(l1)-float(l2)
k=k/float©*64+0.5
ch=int(k)
payload+=chr(ch)
r.recvuntil(‘said?\n’)
r.sendline(payload)
r.interactive()
EAX:累加(Accumulator)寄存器,常用于函数返回值
EBX:基址(Base)寄存器,以它为基址访问内存
ECX:计数器(Counter)寄存器,常用作字符串和循环操作中的计数器
EDX:数据(Data)寄存器,常用于乘除法和I/O指针
ESI:源变址寄存器
DSI:目的变址寄存器
ESP:堆栈(Stack)指针寄存器,指向堆栈顶部
EBP:基址指针寄存器,指向当前堆栈底部
EIP:指令寄存器,指向下一条指令的地址
call指令隐含操作push EIP,ret指令隐含操作 pop EIP