【UNCTF】逆向WriteUp以及出题笔记
【UNCTF】逆向WriteUp以及出题笔记
- WriteUp
- re_checkin
- 反编译
- babypy
- easyMaze
- ICU
- ezRust
- base_on_rust
- Trap
- ezre
- ezvm
- BetterCPU
- 出题笔记
- CTFilter
- 原神
WriteUp
re_checkin
IDA打开,搜索字符串
查看交叉引用,定位到主函数
可以看到直接比较字符串
由于Str2在IDA中没有出现明文值,因此直接x64dbg打开,主函数下断,即可看到flag
反编译
这道题…有意思???
正规解题步骤参照下面的babypy,不过这道题据说运行就有flag?!
babypy
ExeinfoPe打开,发现是pyinstaller打包的,用网上的脚本解包
将解包出来的struct的前16字节添加到babypy文件头部,改名为babypy.pyc,用uncompyle6反编译,得到.py文件
import os, libnum, binascii
flag = 'unctf{*******************}'
x = libnum.s2n(flag)def gen(x):y = abs(x)while 1:if y > 0:yield y % 2y = y >> 1else:if x == 0:yield 0l = [i for i in gen(x)]
l.reverse()
f = '%d' * len(l) % tuple(l)
a = binascii.b2a_hex(f.encode())
b = int(a, 16)
c = hex(b)[2:]
print(c)
os.system('pause')
再结合tip.txt,很容易写出逆向算法
flag = '313131303130313031313031313130303131303030313130313131303130303031313030313130303131313130313130313031303130303031313031303030303130303030303030313131303130303031303131313131303131303130303130313131303031313031303131313131303131313030313030313130303130313031313030303031303031313030303130303131303030313031313131303031303130313131313130313130303031313030313130303030303031313030303030303131303030313031313131313031'
n = 0
for i in flag:if i == '1':n = (n << 1) + 1elif i == '0':n = n << 1
s = libnum.n2s(n)
print(s)
easyMaze
IDA打开,标准的迷宫题
检查输入格式
输入wasd控制方向,Dst处是迷宫地图
根据交叉引用定位
迷宫是10x10的,这样看着不直观,而且是反的,倒过来换成每行10个稍微直观了一点
Oo00oD00SD
0oooo0Dooo
o0D0oD0o00
ooooo00o00
oD0D0ooooo
o00o0o0o0o
oDoooooDDD
o00o00oooo
oD0D0000oD
oooooooooD
很容易看出来走出迷宫的路径:dsdddssaaaassssssddddddddwwaawawwddwwwdw
ICU
IDA打开,明文对比字符串
看似好像是先处理一遍然后依次对单个字符进行处理,动态调试看看情况
输入123456下断查看第一次处理的结果,6位变8位,看起来像base64,IDA搜索字符串可以看到base64的变表
单步即可看到第二次处理的结果
最终解密脚本
import base64table = 'UyOPef2ghvwx3ABdT7856QSijuCDFGst0LKER4ZabckHIJMNnopqrlmz1VWXY9+/'
origin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
t = 'HSWEH2vXHmRtGZRJvSmKviwtviv4Ga5rD25Mvl:u6ewBUKg9'
f = ''
odd = True
for i in t:if odd:f += chr(ord(i) - 1)else:f += iodd = not odd
flag = f.translate(str.maketrans(table, origin))
print(base64.b64decode(flag))
ezRust
通过试错可以发现需要2个额外的命令行参数
x64dbg打开,给2个假的命令行参数“1234”和“5678”,一路F8,遇到Error输出就F7,最后进入下面的函数
这一行会把第一个命令行和“YLBNB”入参,“1234”的情况下执行完call后rax等于0
尝试把第一个命令行换成“YLBNB”,rax等于1
返回0会直接输出Error,返回1会进行第二次判断,同理得到第二个命令行为“RUSTPROGRAMING”
运行直接出flag(IDA也能找到对应的地方)
base_on_rust
(做题环境忘了保存,口头说一下做法)
这题我是动调出来的,但是我想站在上帝视角教大家一个简单的办法
程序需要一个额外的命令行输入,我们假设输入123456,会发现它会输出一段base64,将其解码,发现疑似base32,再解,发现是313233343536,正好是输入的字符的ascii码的十六进制拼起来
IDA的F5里面很多JUMPOUT,在汇编窗口手动修一下(没有解析的代码C一下,C不行的话就nop,再U整个函数,再P),可以看到疑似比较的操作,把待比较的数据dump下来,解码一下,跑脚本出flag
s = ''
f = '756E6374667B6261736536345F6261736533325F6261736531365F656E636F64655F5F5F7D'
i = 0
while i < len(f):c = f[i:i + 2]s += chr(int(c, 16))i += 2
print(s)
Trap
IDA打开,发现输入的flag s1和dest各留一份,在sub_400CBE后s1与s2比较
跟进函数内部,异或后会开启线程
这个程序有很多地方不能正常反编译,手动修一下汇编就好了
比如说sub_400C13,一开始F5后看不懂,修好以后发现是个简单递归
跟进另一个函数,这个函数里面本来是有反调试的,被我nop掉了
对s1 s2的算法都很简单,很容易求出输入的是 941463c8-2bcb-
运行的时候输入正确的flag会异或解密整个动态链接库文件,然后写入文件并调用jo_enc函数对接下来的输入进行检查
我的Ubuntu20不知道为什么调用动态库会失败,于是在/tmp中找到文件直接静态分析
也是进行运算操作后和固定值比较,逆向脚本还算好写
cipher = [1668, 1646, 1856, 4118, 1899, 1752, 640, 2000, 4412, 1835, 820, 984, 968, 1189, 4353, 1646, 4348, 4561, 1564,1566, 5596, 1525]
input1 = ''
input2 = '941463c8-2bcb-'
a = [i * 2 for i in range(128)]
b = [i * 2 + 1 for i in range(128)]
c = [16 * ord(input2[m % 14]) ^ cipher[m] for m in range(22)]
for d in c:for n in range(128):j = 0if not n % 2:for i in range(0, n, 2):j += a[i]else:for i in range(0, n, 2):j += b[i]if j == d:input1 += chr(n)
print('unctf{' + input2 + input1 + '}')
ezre
IDA打开,发现有混淆,根据字符串定位到主函数,flag有18位
整个函数充满着混淆后的junk代码,自己调一调就能明白这些代码什么意思
前面写了用来对比的数据,此处就是check的代码
2张截图之间的160多行代码就是具体的算法了,下面给出化简后的代码
v1 = v45[0];
v42 = 0;
v2 = 9;
while (1)
{v3 = v45[v2];if (v1 + v3 < 128){v43[v42] = 128 - v3;v43[v42 + 9] = 2 * v3 + v1 - 128;}else{if (v1 + v3 == 128){v43[v42] = v1;v43[v42 + 9] = v3;}else{v43[v42] = 2 * v1 + v3 - 128;v43[v42 + 9] = 128 - v1;}}if (++v42 == 9)break;v1 = v45[v42];v2 = v42 + 9;
}
用python写一个爆破脚本跑出flag
def chk(a, b):x = a + bif x < 128:y = 128 - ba, b = y, x - yelif x == 128:a = ab = belse:y = 128 - ba, b = x - y, yreturn a, bflag = [141, 99, 177, 201, 161, 205, 177, 177, 203, 51, 62, 33, 19, 31, 12, 24, 33, 23]
flag_pre = ''
flag_post = ''
for i in range(0, 9):for j in range(0, 256):for k in range(0, 256):if chk(j, k) == (flag[i], flag[i + 9]):flag_pre += chr(k)flag_post += chr(j)
print(flag_pre + flag_post)
ezvm
IDA打开,就是一个VM
一开始程序对VM进行了初始化
根据初始化代码,自建一个结构体
分析每条指令作用的时候有几个地方需要注意
将opcode提取出来,并写出伪代码
-9 -15 6 3 -15 7 0 * -15 5 0 -8 -15 2 0 -16 20 -13 2 -12 11 -7-7 return-16 if [8]==next [0]=0 else [0]=([8]>=next)+1rip+=2-15 if next==5 [0]=input[[8]]else if next==6 [4]=nnexttelse if next==7 [8]=nnexttelse if next==2 input[[8]]=[0]rip+=3-13 [8]+=next-12 if [0]==1 rip-=next else rip+=2-9 input flag 21-8 [0]???=[0]rip++input flag 21
[4]=3
[8]=0
*
[0]=input[0]
[0]???=[0]
input[0]=[0]
[0]=1
[8]+=2
jmp * ([0]!=1 return)
最终解密脚本
def func(v4):v5 = 3v6 = 1v7 = 7while True:if v7 & 1:v6 = v4 * v6 - 187 * ((((6313324174959418735 * v4 * v6) >> 64) >> 6) - (v4 * v6 >> 63))v7 >>= 1v5 -= 1v4 = v4 * v4 - 187 * ((((6313324174959418735 * v4 * v4) >> 64) >> 6) - (v4 * v4 >> 63))if v5 == 0:breakreturn v6flag = [150, 48, 144, 106, 159, 54, 39, 116, 179, 49, 157, 95, 142, 95, 17, 97, 157, 121, 39, 118, 131]
s = ''for i in range(21):if i % 2 == 1:s += chr(flag[i])else:for j in range(128):if func(j) == flag[i]:s += chr(j)print(s)
BetterCPU
不知道为什么,我这边运行不了这道题的附件,于是干脆纯静态分析
IDA打开,发现是VM
有符号表,很不错
主要逻辑函数反编译不了,原因是jmp rax
很长很长的opcode
用于jmp rax的表
我们先把可以跳到的地址找到,并且硬啃汇编,找到每个jmp的作用
0x401810 : mov_regreg=*(rip+1)rip+=90x401c62 : put_errorputs runtime error0x401872 : putchar_regputchar regrip++0x401930 : stack_subrsp--*(stack+rsp-1)-=*(stack+rsp)rip++0x40189a : stack_addrsp--*(stack+rsp-1)+=*(stack+rsp)rip++0x4016c0 : push_regrsp++*(stack+rsp)=regrip++0x401756 : pop_memoryrsp--*(memory+reg)=*(stack+rsp)rip++0x40184a : getchar_reggetcharreg=inputrip++0x4017b7 : push_memoryrsp++*(stack+rsp)=*(memory+reg)rip++0x401707 : pop_regrsp--reg=*(stack+rsp)0x401bd2 : stack_cmprsp--a=*(stack+rsp)rsp--b=*(stack+rsp)this->?=a==brip++0x401669 : jmp_subif this->?==0rip+=9elserip-=order[rip+1]0x4019c3 : stack_xorrsp--b_1=*(stack+rsp)rsp--a_1=*(stack+rsp)rsp++*(stack+rsp)=a_1^b_1rip++0x401612 : jmp_addif this->?==0rip+=9elserip+=order[rip+1]0x401c94 : endreturn
然后写IDAPython脚本解读opcode
def getaddr(i):return -0x100000000+0x40a020+Dword(0x40a020+(Byte(i)-14)*4)s=''
jmp=0
for i in range(0x4091a0,0x409480):if jmp>0:jmp-=1continueif Byte(i)==0x20:s+=str(i-0x4091a0)+' : mov_reg\n'+str(i-0x4091a0+1)+' : '+str(Qword(i+1))+' "'+chr(Qword(i+1)%256)+'" \n'jmp=8continueif Byte(i)==0xf:s+=str(i-0x4091a0)+' : jmp_sub\n'+str(i-0x4091a0+1)+' : '+str(i-Qword(i+1)-0x4091a0)+'\n'jmp=8continueif Byte(i)==0xe:s+=str(i-0x4091a0)+' : jmp_add\n'+str(i-0x4091a0+1)+' : '+str(i+Qword(i+1)-0x4091a0)+'\n'jmp=8continues+=str(i-0x4091a0)+' : '+str(hex(getaddr(i)))[:-1]+'\n'
s=s.replace('0x401c62','put_error')
s=s.replace('0x401872','putchar_reg')
s=s.replace('0x401930','stack_sub')
s=s.replace('0x40189a','stack_add')
s=s.replace('0x4016c0','push_reg')
s=s.replace('0x401756','pop_memory')
s=s.replace('0x40184a','getchar_reg')
s=s.replace('0x4017b7','push_memory')
s=s.replace('0x401707','pop_reg')
s=s.replace('0x401bd2','stack_cmp')
s=s.replace('0x4019c3','stack_xor')
s=s.replace('0x401c94','end')
f=open('op.txt','w')
f.write(s)
f.close()
可以得到伪代码
0 : mov_reg
1 : 87 "W"
9 : putchar_reg
10 : mov_reg
11 : 101 "e"
19 : putchar_reg
20 : mov_reg
21 : 49 "1"
29 : putchar_reg
30 : mov_reg
31 : 99 "c"
39 : putchar_reg
40 : mov_reg
41 : 48 "0"
49 : putchar_reg
50 : mov_reg
51 : 109 "m"
59 : putchar_reg
60 : mov_reg
61 : 101 "e"
69 : putchar_reg
70 : mov_reg
71 : 10 "
"
79 : putchar_reg
80 : mov_reg
81 : 80 "P"
89 : putchar_reg
90 : mov_reg
91 : 108 "l"
99 : putchar_reg
100 : mov_reg
101 : 101 "e"
109 : putchar_reg
110 : mov_reg
111 : 97 "a"
119 : putchar_reg
120 : mov_reg
121 : 115 "s"
129 : putchar_reg
130 : mov_reg
131 : 101 "e"
139 : putchar_reg
140 : mov_reg
141 : 32 " "
149 : putchar_reg
150 : mov_reg
151 : 73 "I"
159 : putchar_reg
160 : mov_reg
161 : 110 "n"
169 : putchar_reg
170 : mov_reg
171 : 80 "P"
179 : putchar_reg
180 : mov_reg
181 : 117 "u"
189 : putchar_reg
190 : mov_reg
191 : 116 "t"
199 : putchar_reg
200 : mov_reg
201 : 58 ":"
209 : putchar_reg
210 : mov_reg
211 : 10 "
"
219 : putchar_reg
220 : mov_reg
221 : 0 " "
229 : push_reg
230 : pop_memory
231 : getchar_reg
232 : push_reg
233 : mov_reg
234 : 256 " "
242 : push_reg
243 : mov_reg
244 : 0 " "
252 : push_memory
253 : stack_add
254 : pop_reg
255 : pop_memory
256 : mov_reg
257 : 1 ""
265 : push_reg
266 : mov_reg
267 : 0 " "
275 : push_memory
276 : stack_add
277 : pop_memory
278 : mov_reg
279 : 0 " "
287 : push_memory
288 : mov_reg
289 : 44 ","
297 : push_reg
298 : stack_cmp
299 : jmp_sub
300 : 231
308 : mov_reg
309 : 0 " "
317 : push_reg
318 : pop_memory
319 : mov_reg
320 : 256 " "
328 : push_reg
329 : mov_reg
330 : 0 " "
338 : push_memory
339 : stack_add
340 : pop_reg
341 : push_memory
342 : mov_reg
343 : 102 "f"
351 : push_reg
352 : stack_add
353 : mov_reg
354 : 54 "6"
362 : push_reg
363 : stack_xor
364 : mov_reg
365 : 66 "B"
373 : push_reg
374 : stack_sub
375 : mov_reg
376 : 256 " "
384 : push_reg
385 : mov_reg
386 : 0 " "
394 : push_memory
395 : stack_add
396 : pop_reg
397 : pop_memory
398 : mov_reg
399 : 1 ""
407 : push_reg
408 : mov_reg
409 : 0 " "
417 : push_memory
418 : stack_add
419 : pop_memory
420 : mov_reg
421 : 0 " "
429 : push_memory
430 : mov_reg
431 : 44 ","
439 : push_reg
440 : stack_cmp
441 : jmp_sub
442 : 319
450 : mov_reg
451 : 0 " "
459 : push_reg
460 : pop_memory
461 : mov_reg
462 : 256 " "
470 : push_reg
471 : mov_reg
472 : 0 " "
480 : push_memory
481 : stack_add
482 : pop_reg
483 : push_memory
484 : mov_reg
485 : 336 "P"
493 : push_reg
494 : mov_reg
495 : 0 " "
503 : push_memory
504 : stack_add
505 : pop_reg
506 : push_memory
507 : stack_cmp
508 : jmp_add
509 : 660
517 : mov_reg
518 : 1 ""
526 : push_reg
527 : mov_reg
528 : 0 " "
536 : push_memory
537 : stack_add
538 : pop_memory
539 : mov_reg
540 : 0 " "
548 : push_memory
549 : mov_reg
550 : 44 ","
558 : push_reg
559 : stack_cmp
560 : jmp_sub
561 : 461
569 : mov_reg
570 : 83 "S"
578 : putchar_reg
579 : mov_reg
580 : 117 "u"
588 : putchar_reg
589 : mov_reg
590 : 99 "c"
598 : putchar_reg
599 : mov_reg
600 : 99 "c"
608 : putchar_reg
609 : mov_reg
610 : 101 "e"
618 : putchar_reg
619 : mov_reg
620 : 115 "s"
628 : putchar_reg
629 : mov_reg
630 : 115 "s"
638 : putchar_reg
639 : mov_reg
640 : 33 "!"
648 : putchar_reg
649 : mov_reg
650 : 10 "
"
658 : putchar_reg
659 : end
660 : mov_reg
661 : 70 "F"
669 : putchar_reg
670 : mov_reg
671 : 65 "A"
679 : putchar_reg
680 : mov_reg
681 : 73 "I"
689 : putchar_reg
690 : mov_reg
691 : 76 "L"
699 : putchar_reg
700 : mov_reg
701 : 10 "
"
709 : putchar_reg
710 : end
711 : 0x40a01f
712 : 0x40a01f
713 : 0x40a01f
714 : 0x40a01f
715 : 0x40a01f
716 : 0x40a01f
717 : 0x40a01f
718 : 0x40a01f
719 : 0x40a01f
720 : 0x40a01f
721 : 0x40a01f
722 : 0x40a01f
723 : 0x40a01f
724 : 0x40a01f
725 : 0x40a01f
726 : 0x40a01f
727 : 0x40a01f
728 : 0x40a01f
729 : 0x40a01f
730 : 0x40a01f
731 : 0x40a01f
732 : 0x40a01f
733 : 0x40a01f
734 : 0x40a01f
735 : 0x40a01f
如果读不懂可以再脑内解析一下
0:
Welcome...220:
[0]=0231:
push input
push 256
push [0]
add
[[0]+256]=input
push 1
push [0]
add
[0]=[0]+1
push [0]
push 44
cmp
jmp 231308:
[0]=0319:
push 256
push [0]
add
push [[0]+256]
push 102
add
push 54
xor
push 66
sub
push 256
push [0]
add
[0]=[0]+256
push 1
push [0]
add
[0]=[0]+1
push [0]
push 44
cmp
jmp 319450:
[0]=0461:
push 256
push [0]
add
push [[0]+256]
push 336
push [0]
add
push [[0]+336]
cmp
jmp 660517:
push 1
push [0]
add
[0]=[0]+1
push [0]
push 44
cmp
jmp 461569:
Success...
可以看到,读取44个字符,进行一系列操作,然后与memory+336比较
memory+336在虚拟机初始化的时候memcpy了一段数据
写脚本提取出来,最终解密脚本
def ch(i):return chr((((i + 66) % 256 ^ 54) % 256 - 102) % 256)flag = [171, 160, 189, 170, 184, 149, 74, 86, 87, 77, 177, 72, 67, 177, 183, 173, 177, 92, 187, 170, 170, 187, 172, 177,74, 182, 175, 160, 177, 71, 66, 92, 121, 173, 177, 91, 107, 177, 108, 104, 107, 94, 147, 4]s = ''
for i in flag:s += ch(i)
print(s)
出题笔记
CTFilter
这道题呢,本来是想弄一个完整一点的透明加解密,后来因为太忙了,加上这是一个新生赛,于是搞了一个阉割版的
加密算法也很简单,只是异或
我感觉有点经验的赛棍可以直接盲猜出来答案
Hint.exe(flag.exe)的源码:
#include <stdio.h>const char* s = "flag{Oh!You_found_me~}";int main() {wprintf_s(L"Did you find my secret file? It's named flag.txt");getchar();
}
因为内核中使用的是UnicodeString,而等会内核会读取这里的字符,所以这里要用宽字符
CTFilter.sys头文件:
#include <fltKernel.h>#define SystemProcessesAndThreadsInformation 5typedef struct _SYSTEM_THREAD_INFORMATION {LARGE_INTEGER KernelTime;LARGE_INTEGER UserTime;LARGE_INTEGER CreateTime;ULONG WaitTime;PVOID StartAddress;CLIENT_ID ClientId;ULONG64 Priority;LONG BasePriority;ULONG ContextSwitches;ULONG ThreadState;ULONG WaitReason;ULONG PadPadAlignment;
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;typedef struct _SYSTEM_PROCESS_INFORMATION {ULONG NextEntryOffset;ULONG NumberOfThreads;LARGE_INTEGER WorkingSetPrivateSize;ULONG HardFaultCount;ULONG NumberOfThreadsHighWatermark;ULONGLONG CycleTime;LARGE_INTEGER CreateTime;LARGE_INTEGER UserTime;LARGE_INTEGER KernelTime;UNICODE_STRING ImageName;ULONG64 BasePriority;HANDLE UniqueProcessId;HANDLE InheritedFromUniqueProcessId;ULONG HandleCount;ULONG SessionId;ULONG_PTR UniqueProcessKey;SIZE_T PeakVirtualSize;SIZE_T VirtualSize;ULONG PageFaultCount;SIZE_T PeakWorkingSetSize;SIZE_T WorkingSetSize;SIZE_T QuotaPeakPagedPoolUsage;SIZE_T QuotaPagedPoolUsage;SIZE_T QuotaPeakNonPagedPoolUsage;SIZE_T QuotaNonPagedPoolUsage;SIZE_T PagefileUsage;SIZE_T PeakPagefileUsage;SIZE_T PrivatePageCount;LARGE_INTEGER ReadOperationCount;LARGE_INTEGER WriteOperationCount;LARGE_INTEGER OtherOperationCount;LARGE_INTEGER ReadTransferCount;LARGE_INTEGER WriteTransferCount;LARGE_INTEGER OtherTransferCount;SYSTEM_THREAD_INFORMATION Threads[1];
}SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;EXTERN_C_START
NTSTATUS ZwQuerySystemInformation(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
EXTERN_C_ENDNTSTATUS CTFilterUnload(FLT_FILTER_UNLOAD_FLAGS);
FLT_PREOP_CALLBACK_STATUS PreWriteCallback(PFLT_CALLBACK_DATA, PCFLT_RELATED_OBJECTS, PVOID*);
BOOLEAN FindProcess();
BOOLEAN GetFileNameAndKey();
CTFilter.sys的主代码:
#include "CTFilter.h"PWCHAR ProcessName;
HANDLE Pid;
PWCHAR FileName;
PUCHAR Key;
PFLT_FILTER FilterHandle;CONST FLT_OPERATION_REGISTRATION Callbacks[] = {{ IRP_MJ_WRITE, 0, PreWriteCallback, NULL },{ IRP_MJ_OPERATION_END }
};CONST FLT_REGISTRATION FilterRegistration = {sizeof(FLT_REGISTRATION), FLT_REGISTRATION_VERSION, 0, NULL, Callbacks, CTFilterUnload,NULL, NULL, NULL, NULL, NULL, NULL, NULL
};NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {NTSTATUS status;ProcessName = ExAllocatePool(PagedPool, 0x12);memcpy(ProcessName, "\"D(D%D#DjD!D<D!DDD", 0x12);if (!FindProcess()) {ExFreePool(ProcessName);return STATUS_UNSUCCESSFUL;}if (!GetFileNameAndKey()) {ExFreePool(ProcessName);return STATUS_UNSUCCESSFUL;}status = FltRegisterFilter(DriverObject, &FilterRegistration, &FilterHandle);if (!NT_SUCCESS(status)) {ExFreePool(Key);ExFreePool(FileName);ExFreePool(ProcessName);return status;}status = FltStartFiltering(FilterHandle);if (!NT_SUCCESS(status))FltUnregisterFilter(FilterHandle);return status;
}NTSTATUS CTFilterUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {FltUnregisterFilter(FilterHandle);ExFreePool(Key);ExFreePool(FileName);ExFreePool(ProcessName);return STATUS_SUCCESS;
}FLT_PREOP_CALLBACK_STATUS PreWriteCallback(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {NTSTATUS status;PFLT_FILE_NAME_INFORMATION info;ULONG len = Data->Iopb->Parameters.Read.Length;if (!Data)return FLT_PREOP_SUCCESS_NO_CALLBACK;status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &info);if (!NT_SUCCESS(status))return FLT_PREOP_SUCCESS_NO_CALLBACK;status = FltParseFileNameInformation(info);if (!NT_SUCCESS(status)) {FltReleaseFileNameInformation(info);return FLT_PREOP_SUCCESS_NO_CALLBACK;}if (info->Name.Buffer && !wcscmp(wcsrchr(info->Name.Buffer, L'\\') + 1, FileName)) {if (Data->Iopb->Parameters.Write.WriteBuffer) {PUCHAR buffer = (PUCHAR)Data->Iopb->Parameters.Write.WriteBuffer;for (int i = 0; i < Data->Iopb->Parameters.Write.Length; ++i)buffer[i] ^= Key[i % 0x16];}}FltReleaseFileNameInformation(info);return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}BOOLEAN FindProcess() {ULONG size = 0;PVOID buffer;PSYSTEM_PROCESS_INFORMATION info;ULONG count = 0;for (PCHAR p = (PCHAR)ProcessName; p < ProcessName + 0x9; ++p)*p ^= 0x44;ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &size);buffer = ExAllocatePool(PagedPool, size);ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, buffer, size, NULL);info = (PSYSTEM_PROCESS_INFORMATION)buffer;do {if (info->ImageName.Buffer && !wcscmp(info->ImageName.Buffer, ProcessName)) {Pid = info->UniqueProcessId;++count;}info = (PSYSTEM_PROCESS_INFORMATION)((ULONG64)info + info->NextEntryOffset);} while (info->NextEntryOffset);ExFreePool(buffer);return count == 1;
}BOOLEAN GetFileNameAndKey() {NTSTATUS status;PEPROCESS process;KAPC_STATE apc;status = PsLookupProcessByProcessId(Pid, &process);if (!NT_SUCCESS(status))return FALSE;KeStackAttachProcess(process, &apc);FileName = ExAllocatePool(PagedPool, 0x12);memcpy(FileName, (PVOID)0x1400022B0, 0x12);Key = ExAllocatePool(PagedPool, 0x16);memcpy(Key, (PVOID)0x140002240, 0x16);KeUnstackDetachProcess(&apc);ObDereferenceObject(process);return TRUE;
}
主要逻辑就是,加载驱动后会查询有没有flag.exe这个进程,有的话就读取它的假flag和flag.txt到内存中
每当flag.txt被写入时,将写入的数据循环异或假flag
据此我们可以推断,没开地址随机化的Hint.exe的对应地址刚好有相符的数据,可以认定它被改名了,原名应该是flag.exe
而Unknown_data应该是flag.txt,将里面的内容再循环异或一次假flag即可得到真flag
这道题主要的难点应该就是不能动态调试
对于从来没有接触过内核驱动的人来说,加载驱动是一个难点,调试驱动又是一个难点
可以尝试纯静态分析
原神
首先,立正挨打
这道题把主办方的CPU跑满了,导致被迫临时下线。后来我们自己部署了一个静态的环境,才稍微好一点(然而机子还是炸了好几次)
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>
#include <unistd.h>const char* ch4[] = { "丽莎", "凯亚", "安柏", "香菱", "砂糖", "诺艾尔", "凝光", "北斗", "菲谢尔", "芭芭拉", "重云", "班尼特", "行秋", "雷泽" };
const char* ch5[] = { "刻晴", "七七", "琴", "温迪", "莫娜", "迪卢克" };
const char* wp3[] = { "弹弓", "鸦羽弓", "讨龙英杰谭", "黑缨枪", "沐浴龙血的剑", "飞天御剑", "冷刃", "神射手之誓", "翡玉法球", "魔导绪论", "以理服人", "铁影阔剑", "黎明神剑" };
const char* wp4[] = { "祭礼弓", "西风猎弓", "祭礼残章", "西风秘典", "匣里灭辰", "祭礼大剑", "西风大剑", "祭礼剑", "西风剑", "弓藏", "绝弦", "昭心", "流浪乐章", "西风长剑", "雨裁", "钟剑", "匣里龙吟", "笛剑" };
const char* wp5[] = { "天空之翼", "天空之卷", "天空之脊", "天空之傲", "风鹰剑", "阿莫斯之弓", "四风原典", "和璞鸢", "狼的末路", "天空之刃" };int ch4_n, ch5_n, wp3_n, wp4_n, wp5_n;void ck(int n) {int r;puts("抽卡结果如下:");for (int i = 0; i < n; ++i) {r = rand() % 1000;if (r < 3) {r = rand() % 6;printf("★★★★★ %s\n", ch5[r]);++ch5_n;}else if (r < 6) {r = rand() % 10;printf("★★★★★ %s\n", wp5[r]);++wp5_n;}else if (r < 106) {r = rand() % 14;printf("★★★★ %s\n", ch4[r]);++ch4_n;}else if (r < 206) {r = rand() % 18;printf("★★★★ %s\n", wp4[r]);++wp4_n;}else {r = rand() % 13;printf("★★★ %s\n", wp3[r]);++wp3_n;}}
}int main() {int n;char name[32];setbuf(stdin, 0);setbuf(stdout, 0);setbuf(stderr, 0);memset(name, 0, 32);srand(time(0));puts("欢迎使用原神抽卡模拟器!祝你好运~");while (1) {puts("请选择:[1]单抽 [2]十连 [3]结束抽卡");scanf("%d", &n);if (n == 1)ck(1);else if (n == 2)ck(10);else if (n == 3)break;elseputs("错误的选择!");}printf("恭喜你,一共抽到了%d个4星角色、%d个5星角色、%d个3星武器、%d个4星武器、%d个5星武器!\n请选择:[1]向好友炫耀 [2]退出\n", ch4_n, ch5_n, wp3_n, wp4_n, wp5_n);scanf("%d", &n);if (n == 1) {puts("请输入你的名字:");read(0, name, 88);puts("分享成功^_^");}system("echo Bye~!");close(1);
}
基础的栈溢出题,可以很明显的看到程序可以进行ROP
但由于程序本身没有/bin/sh,所以不能直接打system
因为最后关闭了标准输出流,因此leak不太现实
可以想到的是,int类型的 26739 和字符串类型的 “sh” 的二进制值是一模一样的,3星武器的值是最容易增长的,于是我们可以抽这么多的3星武器,然后把记录3星武器数量的变量的地址当作参数传入system即可拿到shell
之后由于标准输出流被关闭了,但是错误输出流没有被关闭,因此通过 cat flag 1>&2 重定向输出流从而读取flag
(不知道有没有非预期,逃ε=ε=ε=┏(゜ロ゜;)┛)
解题过程中用到的都是基础知识点,特别适合萌新
数据类型混淆是一个考点,要学会巧妙的构造可以利用的点
对于能拿到shell的人,我觉得最后cat flag应该不是问题
最后,希望大家都能在原神里单抽抽到自己想要的角色~
【UNCTF】逆向WriteUp以及出题笔记相关推荐
- 西普学院逆向writeup ---------你会吗??
西普学院逆向writeup ---------你会吗?? --------跳跳龙 明天我们学校就要ctf比赛,深夜前10几分钟正好解了一道逆向题,是西普学院的,昨天,在小强的提醒下,也解了一道,今天就 ...
- 180728 逆向-SMC出题笔记
在某黑哥的指示下给他加紧出两个题目~ 最先想到的就是34c3ctf时做过的SMC题目,那个随机数混杂着自解密真的是让我久久无法忘怀 SMC题目的核心就在于自解密时的key 如果key不提供,那么考点就 ...
- 《亚马逊逆向工作法》读书笔记
文章目录 书籍信息 构件:领导力准则与机制 亚马逊领导力准则 机制:强化领导力准则 年度计划:OP1与OP2 S-Team目标 亚马逊的薪酬制度:强化长期思维 招聘:亚马逊独特的抬杆者流程 抬杆者招聘 ...
- [逆向][Writeup]ISG2015 flagfinder - .NET程序逆向
这个题目同样是一道.NET的逆向题,.NET的逆向方法在之前的博文中已经介绍过,这里不做重复的说明.本题的源程序可以在我的github上下载:https://github.com/gsharpsh00 ...
- Android逆向文档阅读笔记-Android Application Fundamentals
Fundamentals Review Android应用程序是在APK格式的文件中的,APK是基于ZIP文件的(可以将APK后缀改成ZIP后缀,然后可以使用unzip去解压). APK文件内容: A ...
- Android逆向writeup,[原创]腾讯apk逆向系列WriteUp
0x00 前言 正在学习安卓逆向的萌新SR绝赞刷题中,昨天做了三道有意思的题目,感觉很适合入门,于是写了个wp发了出来 题目已上传至文章附件,想摸的dalao们可以看看( 0x01 APP1 工具:J ...
- 逆向破解_iOS_学习笔记_1
监测工具 Reveal.snoop-it.introspy 反汇编工具IDA.Hopper 开发工具iOSOpenDev.Theos OSX工具class-dump 然而出现了权限问题: cp: / ...
- 逆向-360逆向writeup
Crackme逆向分析 逆向第一题,很简单,分分钟能搞定.题目大致是说找一个key,提示success就行. Peid查一下没有任何壳,是vc写的程序,里面没有知名的算法.开始分析 错误的时候是这个样 ...
- Android逆向writeup,【技术分享】春秋杯逆向第一题writeup
最近被春秋杯逆向题坑了两天,记录一下解题过程. 程序一运行,就弹个控制台窗口,啥也没有,拖进IDA看了才知道,是在等待用户输入验证的key: 程序的主框架很简单,就是一运行后初始化一些数据,等待用户输 ...
最新文章
- 测序技术及常见的有几种平台类型
- 原创 | 比新基建还火,数字孪生究竟有哪些应用价值?
- cad文本改宋体字型lisp_CAD绘图员必须掌握的15个高能技巧,别人3天工作量你半天搞定!...
- vgh电压高了有什么_一文告诉你电压互感器的作用是什么?
- PHP简易网页访问统计源码
- 实验楼python挑战答案_python基础知识
- 用计算机得到圣诞树,圣诞树、标签系统和计算思维
- nginx 启动报错 “/var/run/nginx/nginx.pid failed” 解决方法
- Cmder安装并解决cmder here报错问题
- centos7 安装sogou输入法
- 微信公众号H5之微信分享常见错误和问题(the permission value is offline verifying)
- recovery输出log+recovery模式关闭selinux
- 许晓斌的《Maven实战》 maven镜像配置
- 【安装windows10 RTX3090 tensorflow的开发环境】
- 机器学习开源框架系列:Torch:1:简介与安装
- 城市名字 按a-z排序
- ubuntu 18.04下greenplum安装笔记(二)安装Greenplum的失败的尝试
- 抗DDOS产品性价比?
- 学计算机用酷一点的话怎么说,酷到让你窒息的句子说说简短一句话 很酷很拽的社会人专属说说...
- linux查看操作系统版本的命令
热门文章
- harmonyos1.0.1.23,鸿蒙OS 1.0.1版本,微内核或将是第二个iOS
- Django----将后端数据展示在前端页面(展示用户列表及详情页)
- 动态图片怎么制作 html,动态图片怎么做(照片视频制作教程)
- 通过java程序判断用户输入的是字符串还是整数
- html中css设置文本对齐,css文本对齐使用哪个属性
- “我还是想做医生。”于是这群北大清华学霸开了家烧烤店。
- git提交代码并合并
- 行为经济学第二章 参照点
- OpenSSh 下载地址
- 书论33 张怀瓘《书议》