攻防世界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; // ebxHMODULE ModuleHandleA; // eaxvoid (__stdcall *ProcAddress)(HMODULE, LPCSTR); // eaxchar Str1[20]; // [esp+1h] [ebp-157h] BYREFchar Str2[11]; // [esp+15h] [ebp-143h] BYREFint 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] BYREFchar 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] BYREFchar v19; // [esp+B7h] [ebp-A1h]int v20; // [esp+B8h] [ebp-A0h] BYREF_DWORD v21[8]; // [esp+BEh] [ebp-9Ah] BYREFchar 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] BYREFint 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; // eaxint 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] BYREFchar v5[1024]; // [esp+404h] [ebp-404h] BYREFmemset(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);elseprintf("Try again later.\n");return 0;
}
sub_C21000(v5);:
unsigned int __cdecl sub_C21000(const char *a1)
{_BYTE *v1; // ecxunsigned int v2; // ediunsigned int result; // eaxconst char *v4; // ebxv2 = 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; // eaxchar v5[128]; // [esp+Ch] [ebp-A0h] BYREFunsigned 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先识别好.