攻防世界reverse难度3
攻防世界Reverse3
哎,坐牢,哎,坐牢.
我从来没有觉得ctf有趣过.jpg
painful
secret-string-400
js虚拟机混淆
我理解错了,一直以为是所有代码翻译一遍.
结果发现是读取字节然后执行代码.
也就是说,它可以直接翻译成ascii码去掉无用的字节码.(还是看wp知道的,看的时候都懵了,为什么可以直接翻译,思维迪化了)
print(bytes([11, 1, 79, 98, 106, 101, 99, 116, 0, 12, 1, 120, 0, 114, 101, 116, 117, 114, 110, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 115, 66, 121, 84, 97, 103, 78, 97, 109, 101, 40, 39, 105, 110, 112, 117, 116, 39, 41, 91, 48, 93, 46, 118, 97, 108, 117, 101, 47, 47, 0, 15, 3, 1, 120, 0, 14, 3, 1, 117, 115, 101, 114, 105, 110, 112, 117, 116, 0, 12, 1, 121, 0, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 101, 110, 100, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 123, 116, 104, 105, 115, 46, 99, 111, 100, 101, 61, 91, 93, 59, 116, 104, 105, 115, 46, 80, 67, 61, 49, 55, 51, 125, 47, 47, 0, 15, 3, 1, 121, 0, 12, 1, 122, 0, 97, 108, 101, 114, 116, 40, 49, 41, 59, 47, 47, 11, 234, 79, 98, 106, 101, 99, 116, 255, 9, 255, 255, 255, 12, 10, 97, 108, 101, 114, 116, 40, 50, 41, 59, 47, 47, 12, 234, 120, 255, 118, 97, 114, 32, 102, 61, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 114, 101, 103, 105, 115, 116, 101, 114, 115, 91, 49, 93, 46, 117, 115, 101, 114, 105, 110, 112, 117, 116, 47, 47, 10, 118, 97, 114, 32, 105, 32, 61, 32, 102, 46, 108, 101, 110, 103, 116, 104, 47, 47, 10, 118, 97, 114, 32, 110, 111, 110, 99, 101, 32, 61, 32, 39, 103, 114, 111, 107, 101, 39, 59, 47, 47, 10, 118, 97, 114, 32, 106, 32, 61, 32, 48, 59, 47, 47, 10, 118, 97, 114, 32, 111, 117, 116, 32, 61, 32, 91, 93, 59, 47, 47, 10, 118, 97, 114, 32, 101, 113, 32, 61, 32, 116, 114, 117, 101, 59, 47, 47, 10, 119, 104, 105, 108, 101, 40, 106, 32, 60, 32, 105, 41, 123, 47, 47, 10, 111, 117, 116, 46, 112, 117, 115, 104, 40, 102, 46, 99, 104, 97, 114, 67, 111, 100, 101, 65, 116, 40, 106, 41, 32, 94, 32, 110, 111, 110, 99, 101, 46, 99, 104, 97, 114, 67, 111, 100, 101, 65, 116, 40, 106, 37, 53, 41, 41, 47, 47, 10, 106, 43, 43, 59, 47, 47, 10, 125, 47, 47, 10, 118, 97, 114, 32, 101, 120, 32, 61, 32, 32, 91, 49, 44, 32, 51, 48, 44, 32, 49, 52, 44, 32, 49, 50, 44, 32, 54, 57, 44, 32, 49, 52, 44, 32, 49, 44, 32, 56, 53, 44, 32, 55, 53, 44, 32, 53, 48, 44, 32, 52, 48, 44, 32, 51, 55, 44, 32, 52, 56, 44, 32, 50, 52, 44, 32, 49, 48, 44, 32, 53, 54, 44, 32, 53, 53, 44, 32, 52, 54, 44, 32, 53, 54, 44, 32, 54, 48, 93, 59, 47, 47, 10, 105, 102, 32, 40, 101, 120, 46, 108, 101, 110, 103, 116, 104, 32, 61, 61, 32, 111, 117, 116, 46, 108, 101, 110, 103, 116, 104, 41, 32, 123, 47, 47, 10, 106, 32, 61, 32, 48, 59, 47, 47, 10, 119, 104, 105, 108, 101, 40, 106, 32, 60, 32, 101, 120, 46, 108, 101, 110, 103, 116, 104, 41, 123, 47, 47, 10, 105, 102, 40, 101, 120, 91, 106, 93, 32, 33, 61, 32, 111, 117, 116, 91, 106, 93, 41, 47, 47, 10, 101, 113, 32, 61, 32, 102, 97, 108, 115, 101, 59, 47, 47, 10, 106, 32, 43, 61, 32, 49, 59, 47, 47, 10, 125, 47, 47, 10, 105, 102, 40, 101, 113, 41, 123, 47, 47, 10, 97, 108, 101, 114, 116, 40, 39, 89, 79, 85, 32, 87, 73, 78, 33, 39, 41, 59, 47, 47, 10, 125, 101, 108, 115, 101, 123, 10, 97, 108, 101, 114, 116, 40, 39, 78, 79, 80, 69, 33, 39, 41, 59, 10, 125, 125, 101, 108, 115, 101, 123, 97, 108, 101, 114, 116, 40, 39, 78, 79, 80, 69, 33, 39, 41, 59, 125, 47, 47, 255, 9, 255, 255, 255, 12, 10, 97, 108, 101, 114, 116, 40, 51, 41, 59, 47, 47, 15, 1, 234, 120, 255, 9, 255, 255, 255, 12, 10, 97, 108, 101, 114, 116, 40, 52, 41, 59, 47, 47, 10, 97, 108, 101, 114, 116, 40, 53, 41, 59, 47, 47, 10, 97, 108, 101, 114, 116, 40, 54, 41, 59, 47, 47, 10, 97, 108, 101, 114, 116, 40, 55, 41, 59, 47, 47, 0, 12, 1, 103, 0, 118, 97, 114, 32, 105, 32, 61, 48, 59, 119, 104, 105, 108, 101, 40, 105, 60, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 99, 111, 100, 101, 46, 108, 101, 110, 103, 116, 104, 41, 123, 105, 102, 40, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 99, 111, 100, 101, 91, 105, 93, 32, 61, 61, 32, 50, 53, 53, 32, 41, 32, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 99, 111, 100, 101, 91, 105, 93, 32, 61, 32, 48, 59, 105, 43, 43, 125, 47, 47, 0, 12, 1, 104, 0, 119, 105, 110, 100, 111, 119, 46, 109, 97, 99, 104, 105, 110, 101, 46, 80, 67, 61, 49, 55, 50, 47, 47, 0, 15, 0, 1, 103, 0, 15, 0, 1, 104, 0]).split(b'\x00'))
得
[b'\x0b\x01Object',
b'\x0c\x01x',
b"return document.getElementsByTagName('input')[0].value//",
b'\x0f\x03\x01x',
b'\x0e\x03\x01userinput',
b'\x0c\x01y',
b'window.machine.end = function(){this.code=[];this.PC=173}//',
b'\x0f\x03\x01y',
b'\x0c\x01z',
b"alert(1);//\x0b\xeaObject\xff\t\xff\xff\xff\x0c\nalert(2);//\x0c\xeax\xffvar
f=window.machine.registers[1].userinput//\nvar i = f.length//\nvar nonce =
'groke';//\nvar j = 0;//\nvar out = [];//\nvar eq = true;//\nwhile(j < i)
{//\nout.push(f.charCodeAt(j) ^ nonce.charCodeAt(j%5))//\nj++;//\n}//\nvar ex =
[1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46, 56,
60];//\nif (ex.length == out.length) {//\nj = 0;//\nwhile(j < ex.length)
{//\nif(ex[j] != out[j])//\neq = false;//\nj += 1;//\n}//\nif(eq){//\nalert('YOU
WIN!');//\n}else{\nalert('NOPE!');\n}}else{alert('NOPE!');}//\xff\t\xff\xff\xff\
x0c\nalert(3);//\x0f\x01\xeax\xff\t\xff\xff\xff\x0c\nalert(4);//\nalert(5);//\na
lert(6);//\nalert(7);//",
b'\x0c\x01g',
b'var i =0;while(i<window.machine.code.length){if(window.machine.code[i] == 255
) window.machine.code[i] = 0;i++}//',
b'\x0c\x01h',
b'window.machine.PC=172//',
b'\x0f',
b'\x01g',
b'\x0f',
b'\x01h',
b'']
整理可得
f = window.machine.registers[1].userinput//
var i = f.length
var nonce = 'groke';
var j = 0;
var out = [];
var eq = true;
while (j < i) {
out.push(f.charCodeAt(j) ^ nonce.charCodeAt(j % 5))
j++;
}
var ex = [1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46,
56, 60];
if (ex.length == out.length) {
j = 0;
while (j < ex.length) {
if (ex[j] != out[j])
eq = false;
j += 1;
}
if (eq) {
alert('YOU WIN!');
} else {
alert('NOPE!');
}
} else {
alert('NOPE!');
}
exp:
key1=[1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46, 56, 60]
key2="groke"
flag=""
for i in range(len(key1)):
flag+=chr(key1[i]^ord(key2[i%5]))
print(flag) # flag = WOW_so_EASY
Catch-me
要爆破
攻防世界逆向高手题之catch-me_catch-me 攻防世界-CSDN博客
照例下载附件,结果两次都提示是用7z
打开(解压),所以要解压两次才能得到真正文件:
最后得到的文件是一个64
位的ELF
文件:
照例运行一下程序查看主要回显信息:
(这里积累第一个经验)
啥也没输入就直接程序结束
了,回顾一下前面做的不用输入
的类型,那时检查文件中flag
的,而且没有对应文件
就闪退的。
那么这题也是不用输入的,这种不用输入的程序都是直接利用
程序外部
的一些东西来作为条件
继续执行的,所以我们这次也是要修改程序外
的一些东西,来符合程序的执行。
照例扔入64
位IDA
中查看main
函数逻辑:
(这里积累第二个经验)
积累一些函数的利用:
int _mm_cvtsi128_si32 (__m128i a);
返回值:r := a0
_mm_add_epi32(a, b);
返回值:a+b
__m128i _mm_slli_si128 (__m128i a, int imm);
4字节128位移位操作
返回值:r := a << (imm * 8)
__m128i _mm_unpackhi_epi16 (__m128i a, __m128i b);
将 a 中的高 4 个有符号或无符号 16 位整数与 b 中的高 4 个有符号或无符号 16 位整数交错。a在前,b在后。
返回值:
r0 := a4 ; r1 := b4
r2 := a5 ; r3 := b5
r4 := a6 ; r5 := b6
r6 := a7 ; r7 := b7
__m128i _mm_unpacklo_epi16 (__m128i a, __m128i b);
将 a 中的低 4 个有符号或无符号 16 位整数与 b 中的低 4 个有符号或无符号 16 位整数交错。a在前,b在后。
返回值:
r0 := a0 ; r1 := b0
r2 := a1 ; r3 := b1
r4 := a2 ; r5 := b2
r6 := a3 ; r7 := b3
__m128i _mm_unpacklo_epi8 (__m128i a, __m128i b);
将 a 中的低 8 个有符号或无符号 8 位整数与 b 中的低 8 个有符号或无符号 8 位整数交错。a在前,b在后。
返回值:
r0 := a0 ; r1 := b0
r2 := a1 ; r3 := b1
…
r14 := a7 ; r15 := b7
__m128i _mm_unpackhi_epi8 (__m128i a, __m128i b);
将 a 中的高 8 个有符号或无符号 8 位整数与 b 中的高 8 个有符号或无符号 8 位整数交错。a在前,b在后。
返回值:
r0 := a8 ; r1 := b8
r2 := a9 ; r3 := b9
…
r14 := a15 ; r15 := b15
__m128i _mm_load_si128 (__m128i *p);
加载 128 位值。
返回值:
返回加载到代表寄存器的变量中的值,地址 p 必须是 16 字节对齐的。
char *getenv(const char *name)函数:(get environment)
搜索 name 所指向的环境字符串,并返回相关的值。
(这里积累第三个经验)
所以在前面v3
是直接生成的,这里能获取程序外部
的只有getenv
函数,那么关键地方就是这里if ( getenv("ASIS") && (*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB )
v3
动态调试发现值是0xB11924E1
,那么我们要设置满足条件的ASIS
和CTF
环境变量才能执行if
语句,才有可能获取flag
。
这里其实很多其它博客都忽略了一点就是getenv("ASIS")
应该是要爆破
出来的,首先附上C语言
运算符的优先级
,从图中可以看出只要前面(*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB
满足,那么后面getenv("ASIS")
只要不是0就可以。
(注意这里&&
不要和&
位运算搞混了,&&
是逻辑与,&是位与。)
getenv(“CTF”)=0xFEEBFEEB^v3,可以算出等于0x4ff2da0a
,那么getenv("ASIS")
是多少呢?只能猜测它和getenv("CTF")
一样都是0x4ff2da0a
试一试了。
(这里积累第四个经验)
所以我们导入linux
的环境变量,这里也补充一些基本知识:(注意:export
只能在当前终端中有效,终端关闭就没用了)
(通过printf可以导入十六进制整数类型,值得积累)
export ASIS="$(printf "\x0a\xda\xf2\x4f")" #注意参数是从低位到高位的
export CTF="$(printf "\x0a\xda\xf2\x4f")"
然后直接运行,果不其然getenv("ASIS")
等于getenv("CTF")
,得出真实的flag
了:
(这里积累第5个经验)
另一种方法就是我们既然知道了getenv("ASIS")
的值,我们也可以直接自己生成flag
啊。
首先第一个要注意的是v3
的0xB11924E1
本来应该是小端顺序逆序存储在内存中的,可是这里红框中HIBYTE、BYTE2、BYTE1、BYTE
直接把v3按大端顺序
存储了,这点要特别注意,而且还经过了位与&
处理。
然后就是黄框中在v3数组后拼接
了小端
的getenv("ASIS")
,最后我们直接用Export data
直接导出haystack
原始的32位值进行处理即可。
附上脚本,注意源代码中是[i & 7]
差点看错成[i % 7]
了:
key1=0xFEEBFEEB key2=0xB11924E1key3=key1^key2 #0x4ff2da0a
print(hex(key3&(key3^key2)))
list1=[0xb1,0x19&0xfd,0x24&0xdf,0xe1&0xbf,0x0a,0xda,0xf2,0x4f]#v3按大端顺序被截取了
flag=[ 0x87, 0x29, 0x34, 0xC5, 0x55, 0xB0, 0xC2, 0x2D, 0xEE, 0x60, 0x34, 0xD4, 0x55, 0xEE, 0x80, 0x7C,0xEE, 0x2F, 0x37, 0x96, 0x3D, 0xEB,0x9C, 0x79, 0xEE, 0x2C, 0x33, 0x95, 0x78, 0xED, 0xC1, 0x2B]
for i in range(32):
flag[i]^=list1[i&7]print('ASIS{'+''.join(map(chr,flag))+'}')
结果:
总结:
1:
(这里积累第一个经验)
啥也没输入就直接程序结束
了,回顾一下前面做的不用输入
的类型,那时检查文件中flag
的,而且没有对应文件
就闪退的。
那么这题也是不用输入的,这种不用输入的程序都是直接利用
程序外部
的一些东西来作为条件
继续执行的,所以我们这次也是要修改程序外
的一些东西,来符合程序的执行。
2:
(这里积累第三个经验)
所以在前面v3
是直接生成的,这里能获取程序外部
的只有getenv
函数,那么关键地方就是这里if ( getenv("ASIS") && (*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB )
.
v3
动态调试发现值是0xB11924E1
,那么我们要设置满足条件的ASIS
和CTF
环境变量才能执行if
语句,才有可能获取flag
。
.
这里其实很多其它博客都忽略了一点就是getenv("ASIS")
应该是要爆破
出来的,首先附上C语言
运算符的优先级
,从图中可以看出只要前面(*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB
满足,那么后面getenv("ASIS")
只要不是0就可以。
.
(注意这里&&
不要和&
位运算搞混了,&&
是逻辑与,&是位与。)
.
3:
(这里积累第四个经验)
所以我们导入linux
的环境变量,这里也补充一些基本知识:(注意:expor
t只能在当前终端中有效,终端关闭就没用了)
(通过printf可以导入十六进制整数类型,值得积累)export ASIS="$(printf "\x0a\xda\xf2\x4f")" #注意参数是从低位到高位的export CTF="$(printf "\x0a\xda\xf2\x4f")" 1234
4:
(这里积累第5个经验)
另一种方法就是我们既然知道了getenv("ASIS")
的值,我们也可以直接自己生成flag
啊。
首先第一个要注意的是v3
的0xB11924E1
本来应该是小端顺序逆序存储在内存中的,可是这里红框中HIBYTE、BYTE2、BYTE1、BYTE
直接把v3按大端顺序
存储了,这点要特别注意,而且还经过了位与&
处理。
然后就是黄框中在v3数组后拼接
了小端
的getenv("ASIS")
,最后我们直接用Export data
直接导出haystack
原始的32位值进行处理即可。
解毕!敬礼!
re5-packed-movement
先脱壳
然后发现是mov混淆
然后在每一次出现Wrong flag的地方找字节码
ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
deedeedee
神经病题目,用一个叫做D语言写的,一个估计是源码,一个是二进制文件,然后flag就在源码里.
然后我看其他wp是靠动调做出来的,都是神人感觉这么好,我调了半天没搞出来,只能大概定位.
key
攻防世界逆向高手题的key_攻防世界逆向key题解-CSDN博客
主要是参考这篇,还是太急和经验不足了
首先遇到不需要输入的题,那就说明是内部解决的.
但是这题不是,应该先shift+f12的,可以看到一个字符串写着flag.txt
过去一看是打开文件,那么这题就是打开文件的获取输入.
接着我们就可以把关闭文件的地方找出来了
那么下面就肯定是比较了
这里就是加密和判断文件有无打开
然后有几个槽点,就是我这里的加密循环是少一个参数的,我怎么y都不出来,导致我看不懂它的逻辑.动调发现是异或和相加,但是我不知道为什么处理Block的去处理v29了,另一个也反过来了,太奇怪了.(解决了,出现三个参数后就好看多了,一下子明白了)
经验:
其实还有学到三点很重要.
一个就是要根据前面的点把一些代码段的功能猜出来,这样就减少范围了,很舒服.
还有就是追踪重要参数,我要改变我的习惯了,不要一开始追踪参数,后面就没有耐心了,应该是着重注意加密函数和比较函数的参数.
经过学长提示,这里的
void*
极有可能是结构体,我后面构造了一个,ida直接把这些void*
变成结构体了这题是好题,足够复杂.
2ex1
mips的架构,但是可以看出来是base64,dump出key,但是不知道为什么cyberchef不能用,估计是我理解错了做法,毕竟不熟悉base系列,所以最后还是copy别人的脚本获得flag,不过思路对了.
梅津美治郎
这题虽然做出来了,但是其实不是很理解,看了wp恍然大悟.
int __cdecl main(int argc, const char **argv, const char **envp)
{
const CHAR *v3; // ebx
HMODULE ModuleHandleA; // eax
void (__stdcall *ProcAddress)(HMODULE, LPCSTR); // eax
char Str1[20]; // [esp+1h] [ebp-157h] BYREF
char Str2[11]; // [esp+15h] [ebp-143h] BYREF
int v9; // [esp+20h] [ebp-138h] BYREF
_DWORD v10[6]; // [esp+26h] [ebp-132h] BYREF
__int16 v11; // [esp+3Eh] [ebp-11Ah]
_DWORD v12[7]; // [esp+40h] [ebp-118h] BYREF
_DWORD v13[4]; // [esp+5Ch] [ebp-FCh] BYREF
_DWORD v14[2]; // [esp+6Ch] [ebp-ECh] BYREF
char v15; // [esp+74h] [ebp-E4h]
_DWORD v16[8]; // [esp+75h] [ebp-E3h] BYREF
__int16 v17; // [esp+95h] [ebp-C3h]
_DWORD v18[8]; // [esp+97h] [ebp-C1h] BYREF
char v19; // [esp+B7h] [ebp-A1h]
int v20; // [esp+B8h] [ebp-A0h] BYREF
_DWORD v21[8]; // [esp+BEh] [ebp-9Ah] BYREF
char v22; // [esp+DEh] [ebp-7Ah]
_DWORD v23[8]; // [esp+DFh] [ebp-79h] BYREF
__int16 v24; // [esp+FFh] [ebp-59h]
int v25; // [esp+101h] [ebp-57h]
int v26; // [esp+105h] [ebp-53h]
char v27; // [esp+109h] [ebp-4Fh]
_DWORD v28[4]; // [esp+10Ah] [ebp-4Eh] BYREF
_DWORD v29[7]; // [esp+11Ah] [ebp-3Eh] BYREF
_DWORD v30[6]; // [esp+136h] [ebp-22h] BYREF
__int16 v31; // [esp+14Eh] [ebp-Ah]
int *p_argc; // [esp+150h] [ebp-8h]
p_argc = &argc;
sub_402940();
puts(
" . \n"
" _|_ ROBOTIC AUTHENTICATION SYSTEM\n"
" /\\/\\ (. .) /\n"
" `||' |#| \n"
" ||__.-\"-\"-.___ \n"
" `---| . . |--.\\ \n"
" | : : | ,||,\n"
" `..-..' \\/\\/\n"
" || || \n"
" || || \n"
" |__|__| \n");
v20 = 1337;
qmemcpy(v21, "Qmd`rd!douds!uid!ghsru!Q`rrvnse;", sizeof(v21));
v22 = 1;
qmemcpy(v18, "Qmd`rd!douds!uid!ghsru!Q`rrvnse;", sizeof(v18));
v19 = 1;
qmemcpy(v23, "Qmd`rd!douds!uid!rdbnoe!Q`rrvnse", sizeof(v23));
v24 = 315;
qmemcpy(v16, "Qmd`rd!douds!uid!rdbnoe!Q`rrvnse", sizeof(v16));
v17 = 315;
v25 = 1869557876;
v26 = 1718432615;
v27 = 3;
v14[0] = 1869557876;
v14[1] = 1718432615;
v15 = 3;
qmemcpy(v28, "jdsodm23/emm", 12);
v28[3] = 20906241;
qmemcpy(v13, "jdsodm23/emm", 12);
v13[3] = 20906241;
qmemcpy(v29, "@eeWdbunsdeDybdquhnoI`oe", 24);
v29[6] = 24339565;
qmemcpy(v12, "@eeWdbunsdeDybdquhnoI`oe", 24);
v12[6] = 24339565;
qmemcpy(v30, "Xnt!vho/!Bnofs`utm`uhnor", sizeof(v30));
v31 = 288;
qmemcpy(v10, "Xnt!vho/!Bnofs`utm`uhnor", sizeof(v10));
v11 = 288;
v9 = 1337;
strcpy(Str2, "r0b0RUlez!");
dword_40AD94 = (int)&v9;
dword_40ADA0 = (int)&v20;
dword_40AD8C = (char *)v18;
dword_40AD90 = (char *)v16;
dword_40AD98 = (int)v14;
lpProcName = (LPCSTR)v12;
lpModuleName = (LPCSTR)v13;
Buffer = (char *)v10;
sub_401500(0);
v3 = lpProcName;
ModuleHandleA = GetModuleHandleA(lpModuleName);
ProcAddress = (void (__stdcall *)(HMODULE, LPCSTR))GetProcAddress(ModuleHandleA, v3);
ProcAddress((HMODULE)1, (LPCSTR)sub_40157F);
puts(dword_40AD8C);
scanf("%20s", Str1);
if ( !strcmp(Str1, Str2) )
{
puts("You passed level1!");
sub_4015EA(0);
}
return 0;
开头可以看到一堆数字赋值,但是r了后并不是正常的字符串,然后我们能看到下面有个 sub_401500(0);
里面是:
int __cdecl sub_401500(int a1)
{
int result; // eax
_BYTE *i; // [esp+1Ch] [ebp-Ch]
if ( a1 <= 9 )
return sub_401500(a1 + 1);
for ( i = (_BYTE *)dword_40AD94; ; ++i )
{
result = dword_40ADA0;
if ( (unsigned int)i >= dword_40ADA0 )
break;
*i ^= 1u;
}
return result;
}
这里对字符串处理了,最后变成AddVectoredExceptionHandler
这是个异常处理函数,甚至比SEH优先级还高,接着的GetModuleHandleA
和GetProcAddress
获取到它的地址,然后执行ProcAddress((HMODULE)1, (LPCSTR)sub_40157F);
我当时还纳闷怎么变量还能当函数用,还是经验太少.这个sub_40157F
就是异常处理跳转到的函数,先不管.
接下来是第一个输入输出sub_4015EA
,直接copystr2
pass掉,看看函数里面:
int __cdecl sub_4015EA(int a1)
{
if ( a1 <= 9 )
return sub_4015EA(a1 + 1);
puts(dword_40AD90);
dword_40ADA8 = 4199961;
__debugbreak();
return 0;
}
有个__debugbreak()
用来触发异常,我还以为是反调试😓,然后就是sub_40157F
:
void __cdecl __noreturn sub_40157F(int a1)
{
_BYTE v1[20]; // [esp+18h] [ebp-20h] BYREF
int v2; // [esp+2Ch] [ebp-Ch]
v2 = *(_DWORD *)(*(_DWORD *)(a1 + 4) + 184);
if ( v2 == dword_40ADA8 + 6 )
{
scanf("%20s", v1);
if ( !sub_401547(v1, dword_40AD98) )
puts(Buffer);
}
ExitProcess(0);
}
接着把dword_40AD98
dump出来xor0x2就行了,得flag为flag{r0b0RUlez!_w3lld0ne}
经验:
- 莫名其妙的代码往往是动调.
- 变量作为函数说明是函数地址.
GetModuleHandleA
和GetProcAddress
作为好兄弟出现要高度注意kernel32.dll
模块库的AddVectoredExceptionHandler
为异常处理函数
BabyXor
开局smc,或者可以叫做加壳,反正去壳,dump出来(其实也可以直接动调)
然后得到
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t v3; // eax
int v5; // [esp+50h] [ebp-1Ch]
void *v6; // [esp+54h] [ebp-18h]
const char *Src; // [esp+58h] [ebp-14h]
int v8; // [esp+5Ch] [ebp-10h]
sub_4010B4((int)&unk_4395F0, "世界上最简单的Xor");
sub_40107D(sub_40102D);
if ( --File._cnt < 0 )
_filbuf(&File);
else
++File._ptr;
v8 = sub_40108C(&unk_435DC0, 56);
Src = (const char *)sub_401041((int)&unk_435DC0, (int)&dword_435DF8, 0x38u);
v6 = malloc(0x64u);
v3 = strlen(Src);
memcpy(v6, Src, v3);
v5 = sub_4010C3(&unk_435DC0, Src, &dword_435E30, 56);
sub_40101E(v8, Src, v5);
return 0;
}
一点符号都没有,丑死了,所幸只有这么一段
先注意看这个:
if ( --File._cnt < 0 )
_filbuf(&File);
else
++File._ptr;
其实这个好像就是fgec()
的宏展开,反正通过动调可以得知是输入.
但是这题更输入无关系,在动调中,可以从v3和Src获得一段flag碎片(v3我估计是eax作为返回值导致的,实际是v8)
这里我很傻逼,不理解为什么只有Src有,最后撑不住去看wp了.
发现三个都是flag的一部分v8
Src
v5
,其实我知道,看代码逻辑完全可以看出,但是我点击v8和v5没显示.后面研究了一下,发现ida是这样的,只有你明确告诉它这是char
型,它才会显示内存中被指向的东西,而你直接点进去,显示的是他本身的值,也就是地址
经验:
同上,就是明白了ida的显示问题.
Windows_Reverse1
先脱壳,是upx.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[1024]; // [esp+4h] [ebp-804h] BYREF
char v5[1024]; // [esp+404h] [ebp-404h] BYREF
memset(v5, 0, sizeof(v5));
memset(v4, 0, sizeof(v4));
printf("please input code:");
scanf("%s", v5);
sub_C21000(v5);
if ( !strcmp(v4, "DDCTF{reverseME}") )
printf("You've got it!!%s\n", v4);
else
printf("Try again later.\n");
return 0;
}
sub_C21000(v5);:
unsigned int __cdecl sub_C21000(const char *a1)
{
_BYTE *v1; // ecx
unsigned int v2; // edi
unsigned int result; // eax
const char *v4; // ebx
v2 = 0;
result = strlen(a1);
if ( result )
{
v4 = (const char *)(a1 - v1);
do
{
*v1 = byte_C22FF8[(char)v1[(_DWORD)v4]];
++v2;
++v1;
result = strlen(a1);
}
while ( v2 < result );
}
return result;
}
可以发现这里利用地址偏移值取值,长得很奇怪.
然后那个byte_C22FF8
是换表ascii码表,根据偏移值替换即可.
经验:
要熟悉偏移值,一开始还以为是ida出错了,动调了一下发现是偏移值
76号
无脱壳
有个花指令去花
有个check函数是最关键的(程序就是比对),而它长这样:
_BOOL4 __cdecl sub_8048580(int a1, int a2)
{
char v3; // al
_BOOL4 result; // eax
char v5[128]; // [esp+Ch] [ebp-A0h] BYREF
unsigned int v6; // [esp+8Ch] [ebp-20h]
v6 = __readgsdword(0x14u);
while ( 1 )
{
memset(v5, 0, sizeof(v5));
v3 = *(_BYTE *)(a1 + a2);
v5[(v3 + 64) % 128] = 1;
switch ( v3 )
{
case '\n':
return a2 == 13 && v5[74] != 0;
case '0':
if ( a2 || !v5[112] )
return 0;
a2 = 1;
continue;
case '1':
if ( a2 == 14 && v5[113] )
goto LABEL_12;
return 0;
case '2':
if ( a2 == 20 && v5[114] )
goto LABEL_15;
return 0;
case '3':
if ( a2 != 89 || !v5[115] )
return 0;
a2 = 90;
continue;
case '4':
if ( a2 != 15 || !v5[116] )
return 0;
a2 = 16;
continue;
case '5':
if ( a2 != 14 || !v5[117] )
return 0;
LABEL_12:
a2 = 15;
continue;
case '6':
if ( a2 != 12 || !v5[118] )
return 0;
a2 = 13;
continue;
case '7':
if ( a2 != 5 || !v5[119] )
return 0;
a2 = 6;
continue;
case '8':
result = 0;
if ( v5[121] )
return a2 == 33 || a2 == 2;
return result;
case '9':
if ( a2 != 1 || !v5[121] )
return 0;
a2 = 2;
continue;
case 'a':
if ( a2 != 35 || !v5[33] )
return 0;
a2 = 36;
continue;
case 'b':
if ( a2 != 11 || !v5[34] )
return 0;
a2 = 12;
continue;
case 'c':
if ( a2 != 32 || !v5[33] )
return 0;
a2 = 33;
continue;
case 'd':
if ( a2 != 3 || !v5[36] )
return 0;
a2 = 4;
continue;
case 'e':
if ( a2 != 7 || !v5[37] )
return 0;
a2 = 8;
continue;
case 'f':
if ( !v5[38] || a2 != 8 && a2 != 4 )
return 0;
goto LABEL_53;
case 'g':
return a2 == 12 && v5[52] != 0;
case 'h':
if ( a2 != 13 || !v5[39] )
return 0;
a2 = 14;
continue;
case 'i':
if ( a2 != 9 || !v5[41] )
return 0;
a2 = 10;
continue;
case 'j':
if ( a2 != 10 || !v5[42] )
return 0;
a2 = 11;
continue;
case 'k':
return a2 == 12 && v5[43] != 0;
case 'l':
if ( a2 != 19 || !v5[44] )
return 0;
a2 = 20;
continue;
case 'm':
if ( a2 != 17 || !v5[45] )
return 0;
a2 = 18;
continue;
case 'n':
return a2 == 18 && v5[45] != 0;
case 'o':
if ( !v5[46] || a2 != 6 && a2 != 28 )
return 0;
LABEL_53:
++a2;
continue;
case 'p':
if ( a2 != 30 || !v5[48] )
return 0;
a2 = 31;
break;
case 'q':
if ( a2 != 29 || !v5[49] )
return 0;
a2 = 30;
break;
case 'r':
if ( a2 != 20 || !v5[50] )
return 0;
LABEL_15:
a2 = 21;
break;
case 's':
if ( a2 != 25 || !v5[51] )
return 0;
a2 = 26;
break;
case 't':
return a2 == 24 && v5[50] != 0;
case 'u':
if ( a2 != 26 || !v5[53] )
return 0;
a2 = 27;
break;
case 'v':
if ( a2 != 2 || !v5[54] )
return 0;
a2 = 3;
break;
case 'w':
if ( a2 != 6 || !v5[55] )
return 0;
a2 = 7;
break;
case 'x':
if ( a2 != 22 || !v5[56] )
return 0;
a2 = 23;
break;
case 'y':
if ( a2 != 23 || !v5[57] )
return 0;
a2 = 24;
break;
case 'z':
return a2 == 21 && v5[33] != 0;
default:
return 0;
}
}
}
我以为是平坦化混淆,d810和default都用了,发现不行,也没看出规律.
后面看wp,发现r
一下后这些都是可见字符,其中为了保持这个循环下去,要一直找continue
,而a2的赋值也给我顺序了,可解flag.
经验:
别急着搜wp
这道题本身就是一种经验
echo-server
这道题主要是考花指令,让我更深入了解了花指令的一些做法.
就直接贴别的师傅的wp了:攻防世界逆向高手题之echo-server_攻防世界 echo-server-CSDN博客
经验:
去花爆红,看看地址如果在代码段中,说明有可能是数据.
对于相对跳转,比如+1,+4之类,说明有些垃圾数据,比如E9,00,直接nop.也可以先u再c,让ida先识别好.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)