【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以及出题笔记相关推荐

  1. 西普学院逆向writeup ---------你会吗??

    西普学院逆向writeup ---------你会吗?? --------跳跳龙 明天我们学校就要ctf比赛,深夜前10几分钟正好解了一道逆向题,是西普学院的,昨天,在小强的提醒下,也解了一道,今天就 ...

  2. 180728 逆向-SMC出题笔记

    在某黑哥的指示下给他加紧出两个题目~ 最先想到的就是34c3ctf时做过的SMC题目,那个随机数混杂着自解密真的是让我久久无法忘怀 SMC题目的核心就在于自解密时的key 如果key不提供,那么考点就 ...

  3. 《亚马逊逆向工作法》读书笔记

    文章目录 书籍信息 构件:领导力准则与机制 亚马逊领导力准则 机制:强化领导力准则 年度计划:OP1与OP2 S-Team目标 亚马逊的薪酬制度:强化长期思维 招聘:亚马逊独特的抬杆者流程 抬杆者招聘 ...

  4. [逆向][Writeup]ISG2015 flagfinder - .NET程序逆向

    这个题目同样是一道.NET的逆向题,.NET的逆向方法在之前的博文中已经介绍过,这里不做重复的说明.本题的源程序可以在我的github上下载:https://github.com/gsharpsh00 ...

  5. Android逆向文档阅读笔记-Android Application Fundamentals

    Fundamentals Review Android应用程序是在APK格式的文件中的,APK是基于ZIP文件的(可以将APK后缀改成ZIP后缀,然后可以使用unzip去解压). APK文件内容: A ...

  6. Android逆向writeup,[原创]腾讯apk逆向系列WriteUp

    0x00 前言 正在学习安卓逆向的萌新SR绝赞刷题中,昨天做了三道有意思的题目,感觉很适合入门,于是写了个wp发了出来 题目已上传至文章附件,想摸的dalao们可以看看( 0x01 APP1 工具:J ...

  7. 逆向破解_iOS_学习笔记_1

    监测工具 Reveal.snoop-it.introspy 反汇编工具IDA.Hopper 开发工具iOSOpenDev.Theos  OSX工具class-dump 然而出现了权限问题: cp: / ...

  8. 逆向-360逆向writeup

    Crackme逆向分析 逆向第一题,很简单,分分钟能搞定.题目大致是说找一个key,提示success就行. Peid查一下没有任何壳,是vc写的程序,里面没有知名的算法.开始分析 错误的时候是这个样 ...

  9. Android逆向writeup,【技术分享】春秋杯逆向第一题writeup

    最近被春秋杯逆向题坑了两天,记录一下解题过程. 程序一运行,就弹个控制台窗口,啥也没有,拖进IDA看了才知道,是在等待用户输入验证的key: 程序的主框架很简单,就是一运行后初始化一些数据,等待用户输 ...

最新文章

  1. 测序技术及常见的有几种平台类型
  2. 原创 | 比新基建还火,数字孪生究竟有哪些应用价值?
  3. cad文本改宋体字型lisp_CAD绘图员必须掌握的15个高能技巧,别人3天工作量你半天搞定!...
  4. vgh电压高了有什么_一文告诉你电压互感器的作用是什么?
  5. PHP简易网页访问统计源码
  6. 实验楼python挑战答案_python基础知识
  7. 用计算机得到圣诞树,圣诞树、标签系统和计算思维
  8. nginx 启动报错 “/var/run/nginx/nginx.pid failed” 解决方法
  9. Cmder安装并解决cmder here报错问题
  10. centos7 安装sogou输入法
  11. 微信公众号H5之微信分享常见错误和问题(the permission value is offline verifying)
  12. recovery输出log+recovery模式关闭selinux
  13. 许晓斌的《Maven实战》 maven镜像配置
  14. 【安装windows10 RTX3090 tensorflow的开发环境】
  15. 机器学习开源框架系列:Torch:1:简介与安装
  16. 城市名字 按a-z排序
  17. ubuntu 18.04下greenplum安装笔记(二)安装Greenplum的失败的尝试
  18. 抗DDOS产品性价比?
  19. 学计算机用酷一点的话怎么说,酷到让你窒息的句子说说简短一句话 很酷很拽的社会人专属说说...
  20. linux查看操作系统版本的命令

热门文章

  1. harmonyos1.0.1.23,鸿蒙OS 1.0.1版本,微内核或将是第二个iOS
  2. Django----将后端数据展示在前端页面(展示用户列表及详情页)
  3. 动态图片怎么制作 html,动态图片怎么做(照片视频制作教程)
  4. 通过java程序判断用户输入的是字符串还是整数
  5. html中css设置文本对齐,css文本对齐使用哪个属性
  6. “我还是想做医生。”于是这群北大清华学霸开了家烧烤店。
  7. git提交代码并合并
  8. 行为经济学第二章 参照点
  9. OpenSSh 下载地址
  10. 书论33 张怀瓘《书议》