-
-
[原创]迷失丛林 Writeup by or4nge
-
2021-11-17 02:07 11044
-
迷失丛林 Writeup by or4nge
很容易定位到程序的输入
输入长度为32,需要通过 sub_4014A0
, sub_401580
的验证
4014A0
较为简单,是个经典的hexstr转成char存到 4041F0
这个地址,最后的16是计算转换后的长度,所以输入就是 [0-9A-F]{32}
随后将输入的前八字节存入 404000
中,剩下的部分传参进 sub_401580
要想让该函数返回1,需要先通过一组if验证
1 | if ( v21 = = (char) 169 && v22 = = (char) 0xAC && v23 = = (char) 0xA7 && v24 > 0xC8u ) |
看一下使用的变量,应该是对前八字节的输入进行的验证
结合动调发现大概就是根据404000数组,构成一个 <value, index>
的结构,两两存放到404420当中
分析了一下404000数组的作用和特征,发现这个数组应该是构成一个环状的结构(以当前数值作为索引寻找下一个数),猜测不能有重复的数字,否则可能会构成小循环之类的,用脚本验证了一下发现后面248个数字果然没有重复,于是将前八字节的取值可能锁定到了 0x1e, 0x28, 0x4b, 0x6d, 0x8c, 0xa3, 0xd2, 0xfb
中,总共有 $8!=40320$ 种可能,完全可以爆破
把ida代码复制下来改一改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #include <stdio.h> #include <stdint.h> #include <algorithm> #include <cstring> unsigned char byte_404000[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xA2 , 0x9B , 0xF4 , 0xDF , 0xAC , 0x7C , 0xA1 , 0xC6 , 0x16 , 0xD0 , 0x0F , 0xDD , 0xDC , 0x73 , 0xC5 , 0x6B , 0xD1 , 0x96 , 0x47 , 0xC2 , 0x26 , 0x67 , 0x4E , 0x41 , 0x82 , 0x20 , 0x56 , 0x9A , 0x6E , 0x33 , 0x92 , 0x88 , 0x29 , 0xB5 , 0xB4 , 0x71 , 0xA9 , 0xCE , 0xC3 , 0x34 , 0x50 , 0x59 , 0xBF , 0x2D , 0x57 , 0x22 , 0xA6 , 0x30 , 0x04 , 0xB2 , 0xCD , 0x36 , 0xD5 , 0x68 , 0x4D , 0x5B , 0x45 , 0x9E , 0x85 , 0xCF , 0x9D , 0xCC , 0x61 , 0x78 , 0x32 , 0x76 , 0x31 , 0xE3 , 0x80 , 0xAD , 0x39 , 0x4F , 0xFA , 0x72 , 0x83 , 0x4C , 0x86 , 0x60 , 0xB7 , 0xD7 , 0x63 , 0x0C , 0x44 , 0x35 , 0xB3 , 0x7B , 0x19 , 0xD4 , 0x69 , 0x08 , 0x0B , 0x1F , 0x3D , 0x11 , 0x79 , 0xD3 , 0xEE , 0x93 , 0x42 , 0xDE , 0x23 , 0x3B , 0x5D , 0x8D , 0xA5 , 0x77 , 0x5F , 0x58 , 0xDB , 0x97 , 0xF6 , 0x7A , 0x18 , 0x52 , 0x15 , 0x74 , 0x25 , 0x62 , 0x2C , 0x05 , 0xE8 , 0x0D , 0x98 , 0x2A , 0x43 , 0xE2 , 0xEF , 0x48 , 0x87 , 0x49 , 0x1C , 0xCA , 0x2B , 0xA7 , 0x8A , 0x09 , 0x81 , 0xE7 , 0x53 , 0xAA , 0xFF , 0x6F , 0x8E , 0x91 , 0xF1 , 0xF0 , 0xA4 , 0x46 , 0x3A , 0x7D , 0x54 , 0xEB , 0x2F , 0xC1 , 0xC0 , 0x0E , 0xBD , 0xE1 , 0x6C , 0x64 , 0xBE , 0xE4 , 0x02 , 0x3C , 0x5A , 0xA8 , 0x9F , 0x37 , 0xAF , 0xA0 , 0x13 , 0xED , 0x1B , 0xEC , 0x8B , 0x3E , 0x7E , 0x27 , 0x99 , 0x75 , 0xAB , 0xFE , 0xD9 , 0x3F , 0xF3 , 0xEA , 0x70 , 0xF7 , 0x95 , 0xBA , 0x1D , 0x40 , 0xB0 , 0xF9 , 0xE5 , 0xF8 , 0x06 , 0xBC , 0xB6 , 0x03 , 0xC9 , 0x10 , 0x9C , 0x2E , 0x89 , 0x5C , 0x7F , 0xB1 , 0x1A , 0xD6 , 0x90 , 0xAE , 0xDA , 0xE6 , 0x5E , 0xB9 , 0x84 , 0xE9 , 0x55 , 0xBB , 0xC7 , 0x0A , 0xE0 , 0x66 , 0xF2 , 0xD8 , 0xCB , 0x00 , 0x12 , 0xB8 , 0x17 , 0x94 , 0x6A , 0x4A , 0x01 , 0x24 , 0x14 , 0x51 , 0x07 , 0x65 , 0x21 , 0xC8 , 0x38 , 0xFD , 0x8F , 0xC4 , 0xF5 , 0xFC }; unsigned char byte_404220[ 520 ]; unsigned char byte_404420[ 65537 ]; int dword_404100[] = { 2 , 4 , 8 , 0x10 , 0x20 , 0x40 , 0x80 , 0 }; using namespace std; int sub_401580(unsigned char * a2) { int v2; / / ebp unsigned char * v3; / / eax int * v4; / / esi unsigned char * v5; / / ecx int v6; / / edi unsigned char * v7; / / ecx int v8; / / edx unsigned char * v9; / / eax int v10; / / ecx int v11; / / esi int v12; / / eax unsigned char v13; / / dl int v14; / / edi int v15; / / eax int v16; / / ecx int v17; / / esi int i; / / eax char v19; / / dl unsigned char v21; / / [esp + 10h ] [ebp - Ch] unsigned char v22; / / [esp + 11h ] [ebp - Bh] unsigned char v23; / / [esp + 12h ] [ebp - Ah] unsigned char v24; / / [esp + 13h ] [ebp - 9h ] unsigned char * v25; / / [esp + 14h ] [ebp - 8h ] v21 = 0 ; v22 = 0 ; v23 = 0 ; v24 = 0 ; v2 = 1 ; v25 = byte_404420; for (i = 0 ; i < 8 ; i + + ) byte_404000[i] = a2[i]; do { byte_404220[ 0 ] = byte_404000[v2 - 1 ]; byte_404220[ 1 ] = v2; v3 = byte_404220; v4 = dword_404100; v5 = &byte_404220[dword_404100[ 0 ]]; do { v6 = * v4; / / 2 , 4 , 8 , 10h , 20h , 40h , 80h if ( * v4 > 0 ) { do { v7 = v5 + 1 ; * (v7 - 1 ) = byte_404000[ * v3]; * v7 = * v3 + 1 ; v5 = v7 + 1 ; + + v3; - - v6; } while ( v6 ); } + + v4; } while ( v4 < &(dword_404100[ 7 ]) ); v8 = 256 ; do { + + v25[ * v3 + + ]; - - v8; } while ( v8 ); + + v2; v25 + = 256 ; } while ( v2 - 1 < 256 ); v9 = &byte_404420[ 0x28 ]; v10 = 256 ; do { if ( * (v9 - 40 ) ) + + v21; if ( * (v9 - 26 ) ) + + v22; if ( * v9 ) + + v23; if ( v9[ 39 ] ) + + v24; v9 + = 256 ; - - v10; } while ( v10 ); if ( v21 = = 0xA9 && v22 = = 0xAC && v23 = = 0xA7 && v24 > 0xC8u ){ for (i = 0 ; i < 8 ; i + + ){ printf( "%hhX" , a2[i]); } } return 0 ; } int main(){ unsigned char flag[] = { 0x1e , 0x28 , 0x4b , 0x6d , 0x8c , 0xa3 , 0xd2 , 0xfb }; do{ memset(byte_404420, 0 , 65536 ); sub_401580(flag); } while (next_permutation(flag, flag + 8 )); return 0 ; } |
转换一下得到前八字节 B4D682C8BF2DE13A
确定前八字节后,只需要关注和参数(后八字节)有关的部分了,中间全部动调跳过
这一部分程序相较来说就简单了不少,主要是根据404000开头的八个字节作为初始值,每个字节单独与输入的八个字节进行运算,根据末尾bit决定是+1还是找索引,最终目的是凑成 GoodJob~
这个字符串(sub_4024C0
是个字符串比较)
可以使用搜索算法求解,但考虑到每个字节是单独运算的,常规爆破也只需要 0x800
的运算量,所以还是直接爆破了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #include <stdio.h> unsigned char byte_414420[] = { 0xC1 , 0x9B , 0x7F , 0x58 , 0x64 , 0xD5 , 0x77 , 0x21 }; unsigned char byte_404000[] = { 0xC1 , 0x9B , 0x7F , 0x58 , 0x64 , 0xD5 , 0x77 , 0x21 , 0x74 , 0xEB , 0x14 , 0xBF , 0xDF , 0x25 , 0x5A , 0x37 , 0x85 , 0x2C , 0xAF , 0x8C , 0xDA , 0x26 , 0xE2 , 0x7A , 0x87 , 0x4C , 0x60 , 0x99 , 0x54 , 0x3C , 0x95 , 0xC0 , 0xB9 , 0x0C , 0xBC , 0x0E , 0xE7 , 0x2D , 0x86 , 0xBE , 0x67 , 0xD3 , 0xD8 , 0xFC , 0x30 , 0xB6 , 0xC8 , 0x57 , 0x1E , 0x62 , 0x3E , 0xCE , 0xA0 , 0xCD , 0xF5 , 0xEE , 0xA7 , 0xCF , 0x45 , 0xFE , 0xD0 , 0x80 , 0x05 , 0xAD , 0x13 , 0xF3 , 0xB7 , 0x6B , 0x22 , 0x2B , 0xBD , 0x69 , 0x42 , 0x4B , 0xA5 , 0xEA , 0xA6 , 0xD2 , 0x6F , 0x4F , 0x4E , 0x07 , 0xE1 , 0x36 , 0x01 , 0xB5 , 0xAA , 0xB1 , 0x94 , 0x0B , 0x35 , 0x3A , 0xC7 , 0x49 , 0x53 , 0x82 , 0xC3 , 0x7B , 0x32 , 0xFF , 0x19 , 0xC4 , 0xF1 , 0xC9 , 0xE8 , 0xF7 , 0x56 , 0x15 , 0xA3 , 0x46 , 0x89 , 0x43 , 0x9D , 0x8F , 0x20 , 0xEF , 0xBB , 0x2A , 0xCB , 0x09 , 0x93 , 0x4A , 0x1C , 0xE3 , 0x33 , 0xD1 , 0xE0 , 0x1D , 0x72 , 0x7C , 0x27 , 0xE9 , 0x17 , 0x28 , 0x6D , 0x6A , 0xD9 , 0x00 , 0x9A , 0xE5 , 0x63 , 0xDE , 0x23 , 0x9F , 0x0D , 0x47 , 0x3B , 0x65 , 0x08 , 0x84 , 0x6C , 0x1A , 0x88 , 0x12 , 0xA1 , 0xA4 , 0xB3 , 0x18 , 0x24 , 0x1B , 0xD7 , 0x44 , 0xDB , 0xAC , 0x6E , 0x7D , 0x51 , 0x5E , 0xED , 0x50 , 0xD6 , 0x11 , 0x5B , 0x9C , 0xB4 , 0x68 , 0x3D , 0x2F , 0x03 , 0x40 , 0xBA , 0x2E , 0xCA , 0x02 , 0xE6 , 0xA8 , 0xEC , 0x83 , 0x06 , 0x5D , 0xB8 , 0x4D , 0x97 , 0x66 , 0xF0 , 0xFB , 0x8A , 0x55 , 0xAB , 0xB2 , 0x04 , 0xFA , 0x0A , 0x31 , 0x71 , 0xCC , 0x8B , 0x73 , 0xA9 , 0x48 , 0x5C , 0xF9 , 0x98 , 0xE4 , 0xC6 , 0x34 , 0xC5 , 0x7E , 0x81 , 0x75 , 0x90 , 0x1F , 0x92 , 0x3F , 0x9E , 0x10 , 0x29 , 0x52 , 0x39 , 0xF4 , 0x41 , 0x78 , 0x5F , 0x16 , 0x79 , 0xC2 , 0xB0 , 0xDD , 0xF2 , 0x61 , 0x0F , 0x70 , 0xD4 , 0x91 , 0xDC , 0xF6 , 0xF8 , 0xFD , 0x59 , 0x38 , 0x8D , 0x96 , 0xAE , 0x8E , 0x76 , 0xA2 }; int calc(unsigned char a2, int i){ int v17; / / esi char v19; / / dl v17 = 0 ; do { if ( v17 > = 8 ) { if ( !i || i = = 7 ) - - byte_414420[i]; } else { if ( (a2 & 1 ) ! = 0 ) v19 = byte_414420[i] + 1 ; else v19 = byte_404000[byte_414420[i]]; byte_414420[i] = v19; a2 >> = 1 ; } + + v17; } while ( v17 < 9 ); return byte_414420[i]; } int main(){ int i = 0 ; unsigned char a = 0 ; unsigned char b[] = "GoodJob~" ; for ( int j = 0 ; j < 8 ; j + + ){ for (i = 0 ; i < 0x100 ; i + + ){ a = i & 0xff ; for ( int k = 0 ; k < 8 ; k + + ){ byte_414420[k] = byte_404000[k]; } calc(a, j); if (byte_414420[j] = = b[j]) printf( "%d %x\n" , j, a); } } return 0 ; } |
最后得到下半段验证码 D9B6AEF24A80CB22
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法