攻防世界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的,而且没有对应文件就闪退的。
在这里插入图片描述
那么这题也是不用输入的,这种不用输入的程序都是直接利用程序外部的一些东西来作为条件继续执行的,所以我们这次也是要修改程序外的一些东西,来符合程序的执行。

照例扔入64IDA中查看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,那么我们要设置满足条件的ASISCTF环境变量才能执行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啊。

首先第一个要注意的是v30xB11924E1本来应该是小端顺序逆序存储在内存中的,可是这里红框中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,那么我们要设置满足条件的ASISCTF环境变量才能执行if语句,才有可能获取flag
.
这里其实很多其它博客都忽略了一点就是getenv("ASIS")应该是要爆破出来的,首先附上C语言运算符的优先级,从图中可以看出只要前面(*(_DWORD *)getenv("CTF") ^ v3) == 0xFEEBFEEB满足,那么后面getenv("ASIS")只要不是0就可以。
.
(注意这里&&不要和&位运算搞混了,&&是逻辑与,&是位与。)
.在这里插入图片描述

3:
(这里积累第四个经验)
所以我们导入linux的环境变量,这里也补充一些基本知识:(注意:export只能在当前终端中有效,终端关闭就没用了)

在这里插入图片描述

(通过printf可以导入十六进制整数类型,值得积累)export ASIS="$(printf "\x0a\xda\xf2\x4f")" #注意参数是从低位到高位的export CTF="$(printf "\x0a\xda\xf2\x4f")" 1234

4:
(这里积累第5个经验)
另一种方法就是我们既然知道了getenv("ASIS")的值,我们也可以直接自己生成flag啊。
首先第一个要注意的是v30xB11924E1本来应该是小端顺序逆序存储在内存中的,可是这里红框中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

过去一看是打开文件,那么这题就是打开文件的获取输入.

接着我们就可以把关闭文件的地方找出来了

image-20250124184623918

那么下面就肯定是比较了

image-20250124184644895

这里就是加密和判断文件有无打开

image-20250124184721529

然后有几个槽点,就是我这里的加密循环是少一个参数的,我怎么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优先级还高,接着的GetModuleHandleAGetProcAddress获取到它的地址,然后执行ProcAddress((HMODULE)1, (LPCSTR)sub_40157F);我当时还纳闷怎么变量还能当函数用,还是经验太少.这个sub_40157F就是异常处理跳转到的函数,先不管.

接下来是第一个输入输出sub_4015EA,直接copystr2pass掉,看看函数里面:

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_40AD98dump出来xor0x2就行了,得flag为flag{r0b0RUlez!_w3lld0ne}

经验:

  1. 莫名其妙的代码往往是动调.
  2. 变量作为函数说明是函数地址.
  3. GetModuleHandleAGetProcAddress作为好兄弟出现要高度注意
  4. 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先识别好.

posted @   T0fV404  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示