2019强网杯线上赛RE-部分write up

这算是第一次参加正式比赛并且做出题目, 记录一下!

虽然只是做出了两道很简单的题目0.0…..

比赛后在Apeng大佬的帮助下做出了wasm。

我真的是, 太菜了……..膜拜Apeng大佬!人又帅又会打CTF!

  • 2019.6.15更新, 上传三道题的原题

JustRe

题目:JustRe

打开来是这个样子, 拖进IDA通过字符, 定位到主函数

int sub_401BD0()
{
char v1; // [esp+4h] [ebp-68h]

puts(" # ###### ");
puts(" # # # #### ##### # # ###### ");
puts(" # # # # # # # # ");
puts(" # # # #### # ###### ##### ");
puts("# # # # # # # # # ");
puts("# # # # # # # # # # ");
puts(" ##### #### #### # # # ###### ");
sub_401CE0("%s", (unsigned int)&v1);
if ( sub_401610(&v1) )
sub_4018A0(&v1);
puts("sorry..");
return 0;
}

重点在函数sub_401610()中

signed int __usercall sub_401610@<eax>(int a1@<ecx>, int a2@<ebp>)
{
int v2; // edi
unsigned int v3; // eax
int v4; // esi
char v5; // dh
unsigned __int8 v6; // dl
int v7; // eax
signed int v8; // ecx
__m128i v9; // xmm5
char v10; // dl
unsigned __int8 v11; // dh
unsigned __int8 v12; // cl
char v13; // ch
unsigned __int8 v14; // ch
char v15; // dl
unsigned __int8 v16; // dh
signed int v17; // esi
__m128i v18; // xmm0
__m128i v19; // xmm0
signed int v20; // esi
__m128i v21; // xmm4
int v22; // ecx
unsigned __int64 v23; // rax
HANDLE v24; // eax
unsigned __int64 v26; // [esp-54h] [ebp-60h]
__m128i v27; // [esp-20h] [ebp-2Ch]
int v28; // [esp-8h] [ebp-14h]
unsigned __int8 v29; // [esp-2h] [ebp-Eh]
unsigned __int8 v30; // [esp-1h] [ebp-Dh]
int v31; // [esp+0h] [ebp-Ch]
int v32; // [esp+4h] [ebp-8h]
int retaddr; // [esp+Ch] [ebp+0h]

v31 = a2;
v32 = retaddr;
v2 = a1;
v26 = __rdtsc();
v3 = 0;
v4 = 0;
while ( 1 )
{
v5 = *(_BYTE *)(v4 + a1);
if ( v5 >= 48 && v5 <= 57 )
{
v6 = v5 - 65;
goto LABEL_6;
}
v6 = v5 - 65;
if ( (unsigned __int8)(v5 - 65) > 0x19u )
break;
LABEL_6:
v3 *= 16;
if ( (unsigned __int8)(v5 - 48) <= 9u )
{
v7 = v3 - 48;
LABEL_10:
v3 = v5 + v7;
goto LABEL_11;
}
if ( v6 <= 0x19u )
{
v7 = v3 - 55;
goto LABEL_10;
}
LABEL_11:
if ( ++v4 >= 8 )
{
v8 = 1;
goto LABEL_14;
}
}
v8 = 0;
LABEL_14:
v9 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v3), 0);
if ( !v8 )
return 0;
v10 = *(_BYTE *)(v2 + 8);
v11 = 0;
if ( v10 >= 48 && v10 <= 57 )
{
v12 = v10 - 65;
v30 = v10 - 65;
goto LABEL_19;
}
v12 = v10 - 65;
v30 = v10 - 65;
if ( (unsigned __int8)(v10 - 65) > 0x19u )
{
LABEL_34:
v17 = 0;
goto LABEL_35;
}
LABEL_19:
v13 = *(_BYTE *)(v2 + 9);
v29 = v12;
if ( v13 < 48 )
{
v12 = v29;
}
else if ( v13 <= 57 )
{
v14 = v13 - 65;
goto LABEL_24;
}
v14 = v13 - 65;
v30 = v12;
if ( v14 > 0x19u )
goto LABEL_34;
LABEL_24:
if ( (unsigned __int8)(v10 - 48) > 9u )
{
if ( v30 > 0x19u )
v15 = 0;
else
v15 = 16 * (v10 - 7);
}
else
{
v15 = 16 * v10;
}
v16 = *(_BYTE *)(v2 + 9) - 48;
if ( v16 <= 9u )
goto LABEL_33;
if ( v14 > 0x19u )
{
v16 = 0;
LABEL_33:
v11 = v15 + v16;
v17 = 1;
goto LABEL_35;
}
v17 = 1;
v11 = v15 + *(_BYTE *)(v2 + 9) - 55;
LABEL_35:
v18 = _mm_cvtsi32_si128((char)v11);
v19 = _mm_unpacklo_epi8(v18, v18);
v27 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v19, v19), 0);
if ( v17 )
{
v20 = 0;
if ( dword_4053C4 >= 2 )
{
v20 = 16;
v21 = _mm_mullo_epi32(_mm_cvtepu8_epi32(_mm_cvtsi32_si128(v27.m128i_u32[0])), (__m128i)xmmword_404380);
xmmword_405018 = (__int128)_mm_xor_si128(
_mm_add_epi32((__m128i)xmmword_404340, v9), _mm_add_epi32(v21, (__m128i)xmmword_405018));
xmmword_405028 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404350, (__m128i)xmmword_404340), v9), _mm_add_epi32(v21, (__m128i)xmmword_405028));
xmmword_405038 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404360, (__m128i)xmmword_404340), v9), _mm_add_epi32(v21, (__m128i)xmmword_405038));
xmmword_405048 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404370, (__m128i)xmmword_404340), v9), _mm_add_epi32(v21, (__m128i)xmmword_405048));
}
do
{
*((_DWORD *)&xmmword_405018 + v20) = (v20 + v3) ^ (16843009 * v11 + *((_DWORD *)&xmmword_405018 + v20));
++v20;
}
while ( v20 < 24 );
v22 = 0;
while ( *((_BYTE *)&xmmword_405018 + v22) == *((_BYTE *)&loc_404148 + v22) )
{
if ( ++v22 >= 96 )
{
v28 = 0;
v23 = __rdtsc();
if ( HIDWORD(v23) > HIDWORD(v26) || (v28 = v23 - v26, (unsigned int)(v23 - v26) >= 0xFFFFFF) )
MEMORY[0] = 0;
v24 = GetCurrentProcess();
WriteProcessMemory(v24, sub_4018A0, &xmmword_405018, 0x60u, 0);
return 1;
}
}
}
return 0;
}

看不是特别懂, 但也能从中读到一点东西。

if ( ++v4 >= 8 )可知输入字符串至少为8位

之后就直接动态调试了, 这里用的是x32dbg

00401636  | 8A343E                      | mov dh, byte ptr ds:[esi+edi]                 |
00401639 | 80FE 30 | cmp dh, 30 | 30:'0'
0040163C | 7C 0C | jl justre.40164A |
0040163E | 80FE 39 | cmp dh, 39 | 39:'9'
00401641 | 7F 07 | jg justre.40164A |
00401643 | 8AD6 | mov dl, dh |
00401645 | 80EA 41 | sub dl, 41 |
00401648 | EB 0A | jmp justre.401654 |
0040164A | 8AD6 | mov dl, dh |
0040164C | 80EA 41 | sub dl, 41 |
0040164F | 80FA 19 | cmp dl, 19 |
00401652 | 77 2C | ja justre.401680 |
00401654 | 8ACE | mov cl, dh |
00401656 | C1E0 04 | shl eax, 4 |
00401659 | 80E9 30 | sub cl, 30 |
0040165C | 80F9 09 | cmp cl, 9 | 9:'\t'
0040165F | 77 05 | ja justre.401666 |
00401661 | 83C0 D0 | add eax, FFFFFFD0 |
00401664 | EB 08 | jmp justre.40166E |
00401666 | 80FA 19 | cmp dl, 19 |
00401669 | 77 08 | ja justre.401673 |
0040166B | 83C0 C9 | add eax, FFFFFFC9 |
0040166E | 0FBECE | movsx ecx, dh |
00401671 | 03C1 | add eax, ecx |
00401673 | 46 | inc esi |
00401674 | 83FE 08 | cmp esi, 8 |
00401677 | 7C BD | jl justre.401636 |

这里是判断函数的第一个循环, 用黑盒直接猜测, 这里是读取前8位字符到eax

1558934804066

00401682  | 66:0F6EC0                   | movd xmm0, eax                                |
00401686 | 66:0F70E8 00 | pshufd xmm5, xmm0, 0 |

这两句, 将前八为扩展到xmm5中

xmm5                12345678123456781234567812345678

这一句, 读取了后两位, 所以从这里可以知道输入字符至少10位

00401693  | 8A57 08                     | mov dl, byte ptr ds:[edi+8]                   | edi+8:"AB"

继续黑盒, 可知, 在这几句之前的语句都是在执行将后两位读取到ecx中, 而这几句则是将其扩展到xmm0中

00401728  | 0FBECE                      | movsx ecx, dh                                 |
0040172B | 66:0F6EC1 | movd xmm0, ecx |
0040172F | 66:0F60C0 | punpcklbw xmm0, xmm0 |
00401733 | 66:0F61C0 | punpcklwd xmm0, xmm0 |
00401737 | 66:0F70C0 00 | pshufd xmm0, xmm0, 0 |
0040173C | 0F2945 E0 | movaps xmmword ptr ss:[ebp-20], xmm0 |

1558935387042

接下来由单步调试就可知, 将下面, 每次读取16个16进制字符进行运算(最后两组是分别进行, 每次读取4个16进制字符):

先分别加上后两位, 这里就是加上AB, 再每八位, 和前八位加上i的数异或后再送回[00405018]

例如:3E5F11D5FE0F0B1DEA90BD987BB39408加上ABABABABABABABABABABABABABABABAB, 在和12345678123456781234567812345678加上00000003000000020000000100000000后的1234567B1234567A1234567912345678异或后得F83EEBFBBB8EE0B284083F3A356B69CB, 然后送回[00405018]

00405010                            08 94 B3 7B 98 BD 90 EA  /.........³{.½.ê  
00405020 1D 0B 0F FE D5 11 5F 3E 37 FC CA 85 B4 F1 3A FC ...þÕ._>7üÊ.´ñ:ü
00405030 39 0D 16 EE 35 75 50 3E 3B 6D CF 3D FC 0C 5D 07 9..î5uP>;mÏ=ü.].
00405040 2E 51 EB 29 EF BB 16 58 3C 85 50 3E 40 2F 16 B0 .Që)ï».X<.P>@/.°
00405050 FD F0 4E 07 32 4E 19 64 03 95 4B 22 00 9A 8B 22 ýðN.2N.d..K"..."
00405060 91 0E 0F FE 1C 0D 5F 07 38 51 EB F9 A0 65 16 FE ...þ.._.8Qëù e.þ
00405070 49 33 4F 89 C6 F1 56 FC I3O.ÆñVü
_____________________________________________________________________________
00405010 CB 69 6B 35 3A 3F 08 84 /.......Ëik5:?..
00405020 B2 E0 8E BB FB EB 3E F8 37 FC CA 85 B4 F1 3A FC ²à.»ûë>ø7üÊ.´ñ:ü
00405030 39 0D 16 EE 35 75 50 3E 3B 6D CF 3D FC 0C 5D 07 9..î5uP>;mÏ=ü.].
00405040 2E 51 EB 29 EF BB 16 58 3C 85 50 3E 40 2F 16 B0 .Që)ï».X<.P>@/.°
00405050 FD F0 4E 07 32 4E 19 64 03 95 4B 22 00 9A 8B 22 ýðN.2N.d..K"..."
00405060 91 0E 0F FE 1C 0D 5F 07 38 51 EB F9 A0 65 16 FE ...þ.._.8Qëù e.þ
00405070 49 33 4F 89 C6 F1 56 FC I3O.ÆñVü
00401831  | 8A81 18504000               | mov al, byte ptr ds:[ecx+405018]              |
00401837 | 3A81 48414000 | cmp al, byte ptr ds:[ecx+404148] |
0040183D | 75 4F | jne justre.40188E |
0040183F | 41 | inc ecx |
00401840 | 83F9 60 | cmp ecx, 60 |
00401843 | 7C EC | jl justre.401831 |

上面这几句则是将运算后的字符和[404148]处的字符进行对比, 即是主函数中的(一开始IDA给识别成了语句, 直接undifine即可)

; _BYTE byte_404148[96]
.rdata:00404148 byte_404148 db 55h, 8Bh, 0ECh, 83h, 0E4h, 0F0h, 81h, 0ECh, 78h, 2
.rdata:00404148 ; DATA XREF: sub_401610+227↑r
.rdata:00404148 db 2 dup(0), 0A1h, 4, 50h, 40h, 0, 33h, 0C4h, 89h, 84h
.rdata:00404148 db 24h, 74h, 2, 2 dup(0), 0Fh, 10h, 5, 0A8h, 41h, 40h
.rdata:00404148 db 0, 0A0h, 0C0h, 41h, 40h, 0, 56h, 0Fh, 11h, 44h, 24h
.rdata:00404148 db 2Ch, 57h, 0F3h, 0Fh, 7Eh, 5, 0B8h, 41h, 40h, 0, 66h
.rdata:00404148 db 0Fh, 0D6h, 44h, 24h, 40h, 0Fh, 10h, 41h, 0Ah, 6Ah, 40h
.rdata:00404148 db 88h, 44h, 24h, 4Ch, 8Dh, 84h, 24h, 0FCh, 1, 2 dup(0)
.rdata:00404148 db 6Ah, 0, 50h, 0Fh, 11h, 44h, 24h, 1Ch, 0E8h, 58h, 0Fh
.rdata:00404148 db 2 dup(0), 6Ah, 40h, 8Dh, 84h, 24h, 48h, 2
.rdata:004041A8 xmmword 4E434158435843594445434641534641h
.rdata:004041B8 dq 43585143444B4644h
.rdata:004041C0 db 0

知道了运算过程, 那就可以来解出前10位:我这里通过先求出前八位, 再求出后两位的方法

因为前八位, 对相邻的4个16进制数操作相差为1, 则可通过最初的两位求出满足条件的8位数, 再相减求出满足相差为1的且参与运算的后两位是相同的前八位, 再通过已知前八位来算出后两位

s = [0x55, 0x8B, 0xEC, 0x83]

aa = []

key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]

for i in xrange(0xFF):
s1 = [0x08, 0x94, 0xB3, 0x7B]
for j in xrange(4):
s1[j] += i
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
if 0XFFFFFFF < (key ^ key2) <= 0xFFFFFFFF:
aa.append(key ^ key2)
else:
aa.append(0)

s = [0xE4, 0xF0, 0x81, 0xEC]

bb = []

key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]

for i in xrange(0xFF):
s1 = [0x98, 0xBD, 0x90, 0xEA]
for j in xrange(4):
s1[j] += i
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
if 0XFFFFFFF < (key ^ key2) <= 0xFFFFFFFF:
bb.append(key ^ key2)
else:
bb.append(0)

for i in xrange(len(aa)):
if aa[i] != 0 and bb[i] != 0:
if bb[i] - aa[i] == 1:
print hex(aa[i])
break

s = [0xE4, 0xF0, 0x81, 0xEC]
s1 = [0x98, 0xBD, 0x90, 0xEA]
key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
print hex((key ^ bb[i]) - key2)

最终得到:

0x13242248L
0x15151515L

即前10位为1324224815, 但输进去还是sorry, 那就是后面还有一些东西

继续调试:

00401862  | 8900                        | mov dword ptr ds:[eax], eax                   |

if ( HIDWORD(v23) > HIDWORD(v26) || (v28 = v23 - v26, (unsigned int)(v23 - v26) >= 0xFFFFFF) )
MEMORY[0] = 0;

调试到这里程序会崩溃, 根据IDA这里MEMORY[0] = 0也是分析不出来的, 直接设下一句为新EIP即可

接着开始瞎猜模式…….

00401949  | 8B8424 F8010000             | mov eax, dword ptr ss:[esp+1F8]               |
00401950 | 8D9424 70010000 | lea edx, dword ptr ss:[esp+170] |
00401957 | 83C4 18 | add esp, 18 |
0040195A | 898424 D8010000 | mov dword ptr ss:[esp+1D8], eax |
00401961 | 8B8424 E4010000 | mov eax, dword ptr ss:[esp+1E4] |
00401968 | 8D8C24 D8010000 | lea ecx, dword ptr ss:[esp+1D8] |
0040196F | 898424 DC010000 | mov dword ptr ss:[esp+1DC], eax |
00401976 | E8 85F6FFFF | call <justre.sub_401000> |
0040197B | 8B8424 E8010000 | mov eax, dword ptr ss:[esp+1E8] |
00401982 | 8D9424 D8000000 | lea edx, dword ptr ss:[esp+D8] |
00401989 | 898424 D8010000 | mov dword ptr ss:[esp+1D8], eax |
00401990 | 8D8C24 D8010000 | lea ecx, dword ptr ss:[esp+1D8] |
00401997 | 8B8424 EC010000 | mov eax, dword ptr ss:[esp+1EC] |
0040199E | 898424 DC010000 | mov dword ptr ss:[esp+1DC], eax |
004019A5 | E8 56F6FFFF | call <justre.sub_401000> |
004019AA | 8B8424 F0010000 | mov eax, dword ptr ss:[esp+1F0] |
004019B1 | 8D5424 58 | lea edx, dword ptr ss:[esp+58] |
004019B5 | 898424 D8010000 | mov dword ptr ss:[esp+1D8], eax |
004019BC | 8D8C24 D8010000 | lea ecx, dword ptr ss:[esp+1D8] |
004019C3 | 8B8424 F4010000 | mov eax, dword ptr ss:[esp+1F4] |
004019CA | 898424 DC010000 | mov dword ptr ss:[esp+1DC], eax |
004019D1 | E8 2AF6FFFF | call <justre.sub_401000> |
004019D6 | 8D7424 10 | lea esi, dword ptr ss:[esp+10] |
004019DA | 8D4E 01 | lea ecx, dword ptr ds:[esi+1] |

通过这几个语句, 将读进来的AFSAFCEDYCXCXACNDFKDCQXC分成三部分, 并每部分都往前填充成一个新的字符串

"AFSAFCEDAFSAFCEDYCXCXACNDFKDCQXC"
"YCXCFACNAFSAFCEDYCXCXACNDFKDCQXC"
"DFKDCQXCAFSAFCEDYCXCXACNDFKDCQXC"

中间这里, 在前十位后由读进来了16个字符, 并转化成16个16进制数, 通过call <justre.sub_401500>加密, 并且是分成两块进行加密, 每次加密64位, 加密运算后存储

00401A50  | 0FB68434 F9010000           | movzx eax, byte ptr ss:[esp+esi+1F9]          |
00401A58 | 0FB68C34 F8010000 | movzx ecx, byte ptr ss:[esp+esi+1F8] |
00401A60 | 0FB69434 FC010000 | movzx edx, byte ptr ss:[esp+esi+1FC] |
00401A68 | C1E0 08 | shl eax, 8 |
00401A6B | 0BC8 | or ecx, eax |
00401A6D | 0FB68434 FA010000 | movzx eax, byte ptr ss:[esp+esi+1FA] |
00401A75 | C1E0 10 | shl eax, 10 |
00401A78 | 0BC8 | or ecx, eax |
00401A7A | 0FB68434 FB010000 | movzx eax, byte ptr ss:[esp+esi+1FB] |
00401A82 | C1E0 18 | shl eax, 18 |
00401A85 | 0BC8 | or ecx, eax |
00401A87 | 0FB68434 FD010000 | movzx eax, byte ptr ss:[esp+esi+1FD] |
00401A8F | 898C24 D8010000 | mov dword ptr ss:[esp+1D8], ecx |
00401A96 | 0FB68C34 FF010000 | movzx ecx, byte ptr ss:[esp+esi+1FF] |
00401A9E | C1E0 08 | shl eax, 8 |
00401AA1 | 0BD0 | or edx, eax |
00401AA3 | C1E1 08 | shl ecx, 8 |
00401AA6 | 0FB68434 FE010000 | movzx eax, byte ptr ss:[esp+esi+1FE] |
00401AAE | 0BC8 | or ecx, eax |
00401AB0 | 8D4424 58 | lea eax, dword ptr ss:[esp+58] |
00401AB4 | C1E1 10 | shl ecx, 10 |
00401AB7 | 0BCA | or ecx, edx |
00401AB9 | 8D9424 58010000 | lea edx, dword ptr ss:[esp+158] |
00401AC0 | 50 | push eax |
00401AC1 | 8D8424 DC000000 | lea eax, dword ptr ss:[esp+DC] |
00401AC8 | 898C24 E0010000 | mov dword ptr ss:[esp+1E0], ecx |
00401ACF | 50 | push eax |
00401AD0 | 8D8C24 E0010000 | lea ecx, dword ptr ss:[esp+1E0] |
00401AD7 | E8 24FAFFFF | call <justre.sub_401500> | 前面是取, 后面是存储, 函数用来加密运算
00401ADC | 8B8C24 E0010000 | mov ecx, dword ptr ss:[esp+1E0] |
00401AE3 | 83C4 08 | add esp, 8 |
00401AE6 | 888C34 38020000 | mov byte ptr ss:[esp+esi+238], cl |
00401AED | 8BC1 | mov eax, ecx |
00401AEF | C1E8 08 | shr eax, 8 |
00401AF2 | 888434 39020000 | mov byte ptr ss:[esp+esi+239], al |
00401AF9 | 8BC1 | mov eax, ecx |
00401AFB | C1E8 10 | shr eax, 10 |
00401AFE | 888434 3A020000 | mov byte ptr ss:[esp+esi+23A], al |
00401B05 | C1E9 18 | shr ecx, 18 |
00401B08 | 888C34 3B020000 | mov byte ptr ss:[esp+esi+23B], cl |
00401B0F | 8B8C24 DC010000 | mov ecx, dword ptr ss:[esp+1DC] |
00401B16 | 8BC1 | mov eax, ecx |
00401B18 | 888C34 3C020000 | mov byte ptr ss:[esp+esi+23C], cl |
00401B1F | C1E8 08 | shr eax, 8 |
00401B22 | 888434 3D020000 | mov byte ptr ss:[esp+esi+23D], al |
00401B29 | 8BC1 | mov eax, ecx |
00401B2B | C1E8 10 | shr eax, 10 |
00401B2E | C1E9 18 | shr ecx, 18 |
00401B31 | 888434 3E020000 | mov byte ptr ss:[esp+esi+23E], al |
00401B38 | 888C34 3F020000 | mov byte ptr ss:[esp+esi+23F], cl |
00401B3F | 83C6 08 | add esi, 8 |
00401B42 | 3BF7 | cmp esi, edi | edi:&"D:\\CTF\\Games\\2019\\QWB\\task_JustRe5\\JustRe5\\JustRe.exe"
00401B44 | 0F8C 06FFFFFF | jl justre.401A50 |

最后这一部分是对比, 而且对比的只有前4组, 后两组数是固定不变的

00401B4A  | C74424 10 507CA9E6          | mov dword ptr ss:[esp+10], E6A97C50           |
00401B52 | 33C0 | xor eax, eax |
00401B54 | C74424 14 8709CEFA | mov dword ptr ss:[esp+14], FACE0987 |
00401B5C | C74424 18 20D50DCF | mov dword ptr ss:[esp+18], CF0DD520 |
00401B64 | C74424 1C 90BB976C | mov dword ptr ss:[esp+1C], 6C97BB90 |
00401B6C | C74424 20 9090F6B0 | mov dword ptr ss:[esp+20], B0F69090 |
00401B74 | C74424 24 7BA6A4E8 | mov dword ptr ss:[esp+24], E8A4A67B |

00401B80  | 8A4C04 10                   | mov cl, byte ptr ss:[esp+eax+10]              |
00401B84 | 3A8C04 38020000 | cmp cl, byte ptr ss:[esp+eax+238] |
00401B8B | 75 1F | jne justre.401BAC |

根据上面的分析, 猜测这是一个64位的分块加密算法, 查了一下发现是DES3

  • 三重 DES 的加解密方式如下
    
    C=Ek3(Dk2(Ek1(P)))C=Ek3(Dk2(Ek1(P)))
    
    P=Dk1(Ek2(Dk3(C)))P=Dk1(Ek2(Dk3(C)))
    
    在选择密钥时, 可以有两种方法
    
    - 3 个不同的密钥, k1, k2, k3 互相独立, 一共 168 比特。
    - 2 个不同的密钥, k1 与 k2 独立, k3=k1, 112 比特。
    
    

    最后直接通过pyCrypto模块导入DES3, 解出后16位, 脚本如下如下

    ```python
    key = "AFSAFCEDYCXCXACNDFKDCQXC"
    cipher = [0x50, 0x7C, 0xA9, 0xE6, 0x87, 0x09, 0xCE, 0xFA, 0x20, 0xD5, 0x0D, 0xCF, 0x90, 0xBB, 0x97, 0x6C]
    flag2 = ''.join(map(chr, cipher))
    des3 = DES3.new(key)

    print des3.decrypt(flag2)

最终脚本:

from Crypto.Cipher import DES3
s = [0x55, 0x8B, 0xEC, 0x83]

aa = []

key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]

for i in xrange(0xFF):
s1 = [0x08, 0x94, 0xB3, 0x7B]
for j in xrange(4):
s1[j] += i
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
if 0XFFFFFFF < (key ^ key2) <= 0xFFFFFFFF:
aa.append(key ^ key2)
else:
aa.append(0)

s = [0xE4, 0xF0, 0x81, 0xEC]

bb = []

key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]

for i in xrange(0xFF):
s1 = [0x98, 0xBD, 0x90, 0xEA]
for j in xrange(4):
s1[j] += i
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
if 0XFFFFFFF < (key ^ key2) <= 0xFFFFFFFF:
bb.append(key ^ key2)
else:
bb.append(0)

for i in xrange(len(aa)):
if aa[i] != 0 and bb[i] != 0:
if bb[i] - aa[i] == 1:
print hex(aa[i])
break

s = [0xE4, 0xF0, 0x81, 0xEC]
s1 = [0x98, 0xBD, 0x90, 0xEA]
key = (s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0]
key2 = (s1[3] << 24) + (s1[2] << 16) + (s1[1] << 8) + s1[0]
print hex((key ^ bb[i]) - key2)

a = (key ^ bb[i]) - key2

key = "AFSAFCEDYCXCXACNDFKDCQXC"
cipher = [0x50, 0x7C, 0xA9, 0xE6, 0x87, 0x09, 0xCE, 0xFA, 0x20, 0xD5, 0x0D, 0xCF, 0x90, 0xBB, 0x97, 0x6C]
flag2 = ''.join(map(chr, cipher))
des3 = DES3.new(key)

print hex(aa[i])[2:10] + hex(a)[2:4] + des3.decrypt(flag2)


得flag{13242248150dcc509a6f75849b}

强网先锋_AD

题目:强网先锋_AD

这道题就是一道水题0.0

下载后拖进IDA里打开, 直接找到mian函数

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4[128]; // [rsp+0h] [rbp-150h]
char key[45]; // [rsp+80h] [rbp-D0h]
__int64 v6; // [rsp+B0h] [rbp-A0h]
__int64 v7; // [rsp+B8h] [rbp-98h]
__int64 v8; // [rsp+C0h] [rbp-90h]
__int16 v9; // [rsp+C8h] [rbp-88h]
char v10; // [rsp+D0h] [rbp-80h]
char *v11; // [rsp+140h] [rbp-10h]
int i; // [rsp+14Ch] [rbp-4h]

puts("=== Strong Network Pioneer === \n\n");
__isoc99_scanf("%s", &v10);
v6 = 7953769703030221169LL;
v7 = 7954876941086586983LL;
v8 = 7956005061827062375LL;
v9 = 105;
key[0] = 0x5A;
key[1] = 0x6D;
key[2] = 0x78;
key[3] = 104;
key[4] = 90;
key[5] = 51;
key[6] = 116;
key[7] = 116;
key[8] = 89;
key[9] = 87;
key[10] = 90;
key[11] = 104;
key[12] = 97;
key[13] = 51;
key[14] = 86;
key[15] = 104;
key[16] = 97;
key[17] = 87;
key[18] = 120;
key[19] = 104;
key[20] = 97;
key[21] = 88;
key[22] = 70;
key[23] = 112;
key[24] = 89;
key[25] = 87;
key[26] = 53;
key[27] = 107;
key[28] = 89;
key[29] = 87;
key[30] = 57;
key[31] = 105;
key[32] = 102;
key[33] = 81;
key[34] = 61;
key[35] = 61;
v11 = &v10;
sub_4005B7(&v10, (__int64)v4);
for ( i = 0; i <= 44; ++i )
{
if ( v4[i] != (unsigned __int8)key[i] )
{
puts("you're not\n");
return 0LL;
}
}
puts("yes, you are!\n");
return 0LL;
}

最下面的循环是判断, 也就是说我们输入的v4要和key相等

进入sub_4005B7

__int64 __fastcall sub_4005B7(const char *a1, __int64 a2)
{
int v2; // eax
int v3; // eax
int v4; // eax
int v5; // ST18_4
int v6; // eax
int v7; // eax
int v8; // eax
int v10; // [rsp+10h] [rbp-10h]
unsigned __int8 v11; // [rsp+17h] [rbp-9h]
unsigned __int8 v12; // [rsp+17h] [rbp-9h]
int v13; // [rsp+18h] [rbp-8h]
int v14; // [rsp+18h] [rbp-8h]
int v15; // [rsp+18h] [rbp-8h]
int v16; // [rsp+1Ch] [rbp-4h]

v16 = 0;
v13 = 0;
v10 = strlen(a1);
while ( v16 < v10 )
{
v2 = v13;
v14 = v13 + 1;
*(_BYTE *)(a2 + v2) = base64[(const unsigned __int8)a1[v16] >> 2];
v11 = 16 * a1[v16] & 0x30;
if ( v10 <= v16 + 1 )
{
v4 = v14;
v5 = v14 + 1;
*(_BYTE *)(a2 + v4) = base64[v11];
*(_BYTE *)(v5 + a2) = 61;
v6 = v5 + 1;
v13 = v5 + 2;
*(_BYTE *)(v6 + a2) = 61;
break;
}
v3 = v14;
v15 = v14 + 1;
*(_BYTE *)(a2 + v3) = base64[((const unsigned __int8)a1[v16 + 1] >> 4) | v11];
v12 = 4 * a1[v16 + 1] & 0x3C;
if ( v10 <= v16 + 2 )
{
*(_BYTE *)(a2 + v15) = base64[v12];
v8 = v15 + 1;
v13 = v15 + 2;
*(_BYTE *)(v8 + a2) = 61;
break;
}
*(_BYTE *)(a2 + v15) = base64[((const unsigned __int8)a1[v16 + 2] >> 6) | v12];
v7 = v15 + 1;
v13 = v15 + 2;
*(_BYTE *)(a2 + v7) = base64[a1[v16 + 2] & 0x3F];
v16 += 3;
}
*(_BYTE *)(v13 + a2) = 0;
return 0LL;
}

看着很熟悉….直接就能找到base64加密table表

1558933633691

写个脚本跑一下, flag即出

import base64
key = [0 for i in xrange(45)]

key[0] = 0x5A
key[1] = 0x6D
key[2] = 0x78
key[3] = 104
key[4] = 90
key[5] = 51
key[6] = 116
key[7] = 116
key[8] = 89
key[9] = 87
key[10] = 90
key[11] = 104
key[12] = 97
key[13] = 51
key[14] = 86
key[15] = 104
key[16] = 97
key[17] = 87
key[18] = 120
key[19] = 104
key[20] = 97
key[21] = 88
key[22] = 70
key[23] = 112
key[24] = 89
key[25] = 87
key[26] = 53
key[27] = 107
key[28] = 89
key[29] = 87
key[30] = 57
key[31] = 105
key[32] = 102
key[33] = 81
key[34] = 61
key[35] = 61

flag = ""

for i in xrange(len(key)):
flag += chr(key[i])

flag = base64.b64decode(flag)

print flag

flag{mafakuailaiqiandaob}

webassembly

题目:webassembly

这是我第一次接触wasm, 记录一下0.0

题目下下来只有三个文件, 分别是.js , .wasm, .html

wasm介绍就不在这里介绍了, 有兴趣可以到这里看看

wasm就是一堆二进制文件, 就很头秃。但可以用wabt工具转成.c.h文件

$./wasm2c webassembly.wasm -o webassembly.c

不过还是看不懂, 代码行数贼恐怖18567行…………..后来Apeng大佬提供了一种优化方法:用gcc编译后在用ida反编译

将之前反编译出来的wasm.c, wasm.h, 以及wabt项目内的wasm-rt.h, wasm-rt-impl.c, wasm-rt-impl.h三个文件放到同一个文件夹。

直接gcc wasm.c会报错, 因为很多wasm的函数没有具体的实现。但是我们可以只编译不链接, 我们关心的只是程序本身的逻辑, 不需要真正编译出能运行的elf来。

$gcc -c wasm.c -o wasm.o

得到的还未连接的elf文件wasm.o

现在就可以丢进ida来分析了, 比之前的wasm.c友好很多。

分析出来是, XTEA加密, 解密脚本(Apeng大佬太强了0.0)如下:

#include "pch.h"
#include <iostream>
using namespace std;

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
for (i = 0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
}
v[0] = v0; v[1] = v1;
}


void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;
for (i = 0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}

int main()
{

int i;
unsigned int v[10] = { 2586051617L, 2764128737L, 1835204653, 2601945884L, 95579550, 993221996, 1624609160, 781843991, 879125044, 0x7D61 };
unsigned int k[4] = { 0, 0, 0, 0 };
for (i = 0; i < 8; i += 2)
decipher(32, &v[i], k);
cout << (char *)v << endl;
}