2018/4/13实验室考核

编程题

I live2 in an ordinary family, but my parents3 give me all their love4, especially my mom. She takes care of me all the time and sometimes33 I think she2 is a little overprotective. I have already grown up and want to make my own decision, so I always want to tell44 my mom11 that she can let me make my own decision. I can be44 responsible for what I do.
第一题分解质数比较简单,C++的练习题也都有,属于送分题。
第二题:
利用下面的函数将文件中的数字,字符及其他(空格或者特殊字符)统计出来。
isalpha()
isdigit()
others()
要求:代码运行时显示文件中的内容,并显示各个内容的个数。

解题

string=file.read()
shuzi=""
zifu=""
others=""
for i in range(len(string)):
    if string[i].isalpha():
        zifu+=string[i]
    elif string[i].isdigit():
        shuzi+=string[i]
    else:
        others+=string[i]

print shuzi
print zifu
print others

编程题A.txt

A:请利用python编写一个二进制转换器,要求以函数(def)形式编写,可以将某个单词或者flag转换为相应二进制形式输出,用时也能反转换。
例子:输入:hello
      输出:0b1101000 0b1100101 0b1101100 0b1101100 0b1101111
      输入:0b1101000 0b1100101 0b1101100 0b1101100 0b1101111
      输出hello
a=raw_input()

for i in range(len(a)):
   print bin(ord(a[i])),

bin是转化为二进制符号,其实是用str来表示的。

分解.txt

将一个正整数分解质因数。
例如:
输入30,显示结果为2*3*5;
反之,输出“该整数无质因数”。(输入的数字为用户输入)
a=input()
flag=[]
for i in range(2,a):
    if a%i==0:
        a=a/i
        flag.append(i)
        i=2
print flag

逆向题

app10.apk
这里写图片描述
看main函数里面有个check函数。进去之后就是关键代码。

public class encode
{
  private static byte[] b = { 23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32 };

  public static boolean check(String paramString)
  {
    byte[] arrayOfByte1 = paramString.getBytes();
    byte[] arrayOfByte2 = new byte[16];
    for (int i = 0; i < 16; i++) {
      arrayOfByte2[i] = ((byte)(byte)((arrayOfByte1[i] + b[i]) % 61));
    }
    for (i = 0; i < 16; i++) {
      arrayOfByte2[i] = ((byte)(byte)(arrayOfByte2[i] * 2 - i));
    }
    return new String(arrayOfByte2).equals(paramString);
  }
}

这里是输入字符串进行了一些操作之后和它自己相等。

b = [ 23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32 ]
flag=[]
for i in range(16):
    for j in range(128):
        if ((j+b[i])%61)*2-i==j:
            print chr(j),

输出结果为

LOHILMNMLKHILKHI

IgniteMe.exe
这是main函数的一部分

 sub_402B30(&unk_446360, "Give me your flag:");
  sub_4013F0(sub_403670);
  sub_401440(v6, 127);
  if ( strlen(v6) < 0x1E && strlen(v6) > 4 )
  {
    strcpy(v5, "EIS{");
    for ( i = 0; i < strlen(v5); ++i )
    {
      if ( v6[i] != v5[i] )
      {
        sub_402B30(&unk_446360, "Sorry, keep trying! ");
        sub_4013F0(sub_403670);
        return 0;
      }
    }
    if ( v7 == 125 )
    {
      if ( sub_4011C0(v6) )
        sub_402B30(&unk_446360, "Congratulations! ");
      else
        sub_402B30(&unk_446360, "Sorry, keep trying! ");
      sub_4013F0(sub_403670);
      result = 0;
    }
    else
    {
      sub_402B30(&unk_446360, "Sorry, keep trying! ");
      sub_4013F0(sub_403670);
      result = 0;
    }
  }

这里可以看出来flag的格式EIS{}。sub_4011C0(v6)这个函数就是关键了。

{
  size_t v2; // eax
  signed int v3; // [esp+50h] [ebp-B0h]
  char v4[32]; // [esp+54h] [ebp-ACh]
  int v5; // [esp+74h] [ebp-8Ch]
  int v6; // [esp+78h] [ebp-88h]
  size_t i; // [esp+7Ch] [ebp-84h]
  char v8[128]; // [esp+80h] [ebp-80h]

  if ( strlen(a1) <= 4 )
    return 0;
  i = 4;
  v6 = 0;
  while ( i < strlen(a1) - 1 )
    v8[v6++] = a1[i++];
  v8[v6] = 0;
  v5 = 0;
  v3 = 0;
  memset(v4, 0, 0x20u);
  for ( i = 0; ; ++i )
  {
    v2 = strlen(v8);
    if ( i >= v2 )
      break;
    if ( v8[i] >= 97 && v8[i] <= 122 )
    {
      v8[i] -= 32;
      v3 = 1;
    }
    if ( !v3 && v8[i] >= 65 && v8[i] <= 90 )
      v8[i] += 32;
    v4[i] = byte_4420B0[i] ^ sub_4013C0(v8[i]);
    v3 = 0;
  }
  return strcmp("GONDPHyGjPEKruv{{pj]X@rF", v4) == 0;
}

这里先进行了大小写转化+(-)32。再经过sub_4013C0(v8[i]这个函数操作之后,然后跟一个数组异或,之后的值要等于“GONDPHyGjPEKruv{{pj]X@rF“。
接下来就是看一下sub_4013C0(v8[i]这个函数进行了什么操作吧。

int __cdecl sub_4013C0(int a1)
{
  return (a1 ^ 0x55) + 72;
}

先异或再加。
现在整理一下思路

(serial[i]^0x55+72)^byte_4420B0=GONDPHyGjPEKruv{{pj]X@rF 

解出来为

(byte_4420B0^GONDPHyGjPEKruv{{pj]X@rF-72)^0x55
a=[0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02,0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F, 0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C, 0x22, 0x04]
print len(a)
b="GONDPHyGjPEKruv{{pj]X@rF"
b=list(b)
print len(b)
for i in range(len(b)):
    print chr(((a[i]^ord(b[i]))-72)^0x55),

#wadx_tdgk_aihc_ihkn_pjlm
#EIS{wadx_tdgk_aihc_ihkn_pjlm}

python里面有一点需要注意:是先加减再异或也就是注意要加括号。

ReverseMe.exe
查找字符串找到了它congratulations, your input is the flag ^_^。
进入这里


  sub_401AD0();
  v5 = 1177698609;
  v6 = 1127429177;
  v0 = 0;
  v7 = 1127760948;
  v8 = 1161183797;
  v9 = 960705345;
  v10 = 1145127746;
  v11 = 17719;
  v12 = 0;
  do
  {
    v13[v0] = 0;
    ++v0;
  }
  while ( v0 < 8 );
  puts("input your key:");
  scanf("%s", v13);
  v1 = strlen((const char *)v13);
  if ( v1 <= 19 )
  {
    printf("too short!");
    result = -1;
  }
  else if ( v1 > 30 )
  {
    printf("too long!");
    result = -1;
  }
  else
  {
    if ( sub_4014A0((int)v13, (int)&v5, v1) )
      printf("congratulations, your input is the flag ^_^");
    else
      printf("try agian");
    v2 = (FILE *)((char *)iob[1] - 1);
    iob[1] = v2;
    if ( (signed int)v2 < 0 )
    {
      filbuf(iob[0]);
      v2 = iob[1];

从上面这段代码里面我们能得到的信息如下:

  • 输入的字符串存到了v13
  • sub_4014A0(v13, &v5, v1)这个函数是验证函数。
  • flag长度在20到29之内
    下面看一下这个函数的内容吧
signed int __cdecl sub_4014A0(int a1, int a2, int a3)
{
  unsigned int v3; // ebx
  int v5; // eax
  int v6; // ebx
  char v7; // dl
  int i; // eax
  char v9; // [esp+Ah] [ebp-4Ah]
  char v10; // [esp+Bh] [ebp-49h]
  char v11; // [esp+Ch] [ebp-48h]
  char v12; // [esp+Dh] [ebp-47h]
  char v13; // [esp+Eh] [ebp-46h]
  char v14; // [esp+Fh] [ebp-45h]
  char v15; // [esp+10h] [ebp-44h]
  char v16; // [esp+11h] [ebp-43h]
  char v17; // [esp+12h] [ebp-42h]
  char v18; // [esp+13h] [ebp-41h]
  char v19; // [esp+14h] [ebp-40h]
  char v20; // [esp+15h] [ebp-3Fh]
  char v21; // [esp+16h] [ebp-3Eh]
  char v22; // [esp+17h] [ebp-3Dh]
  char v23; // [esp+18h] [ebp-3Ch]
  char v24; // [esp+19h] [ebp-3Bh]
  char v25; // [esp+1Ah] [ebp-3Ah]
  char v26; // [esp+1Bh] [ebp-39h]
  char v27; // [esp+1Ch] [ebp-38h]
  char v28; // [esp+1Dh] [ebp-37h]
  char v29; // [esp+1Eh] [ebp-36h]
  char v30; // [esp+1Fh] [ebp-35h]
  char v31; // [esp+20h] [ebp-34h]
  char v32; // [esp+21h] [ebp-33h]
  char v33; // [esp+22h] [ebp-32h]
  int v34; // [esp+24h] [ebp-30h]
  char v35[44]; // [esp+28h] [ebp-2Ch]

  v3 = 0;
  v34 = 0;
  do
  {
    *(_DWORD *)(&v11 + v3) = 0;
    v3 += 4;
  }
  while ( v3 < ((&v9 - &v11 + 30) & 0xFFFFFFFC) );
  v9 = 15;
  v10 = -121;
  v11 = 98;
  v12 = 20;
  v13 = 1;
  v14 = -58;
  v15 = -16;
  v16 = 33;
  v17 = 48;
  v18 = 17;
  v19 = 80;
  v20 = -48;
  v21 = -126;
  v22 = 35;
  v23 = -82;
  v24 = 35;
  v25 = -18;
  v26 = -87;
  v27 = -76;
  v28 = 82;
  v29 = 120;
  v30 = 87;
  v31 = 12;
  v32 = -122;
  v33 = -117;
  if ( a3 == 25 )
  {
    v5 = 0;
    do
    {
      v35[v5] = __ROL1__(*(_BYTE *)(a1 + v5), 2);
      ++v5;
    }
    while ( v5 != 25 );
    v6 = 0;
    do
    {
      v35[v6] ^= sub_401460(a2, v6);
      ++v6;
    }
    while ( v6 != 25 );
    v7 = 15;
    for ( i = 0; v35[i] == v7; v7 = *(&v9 + i) )
    {
      if ( ++i == 25 )
        return 1;
    }
  }
  return 0;
}

先看一下她的三个参数,第一个是输入的字符串,第二个是v5这个数组。第三个参数是输入字符串的长度。
进行了一个v35[v5] = ROL1((_BYTE )(a1 + v5), 2);运算,这个本来不知道干嘛,不过后来用od跟一下就知道了,这个叫循环左移,跟左移是有区别的。

sub_401460(a2, v6)这个函数也是一个关键,不过注意一下就会发现,这个函数没有对serial进行操作,也就是说在OD里面完全可以得到它得出的结果,当然,完全可以还原函数,就是照抄一下。函数的内容也不多。

int __cdecl sub_401460(int a1, int a2)
{
  char v2; // al
  char v3; // cl
  int v4; // eax
  int v5; // edx

  v2 = *(_BYTE *)(a1 + a2);
  v3 = *(_BYTE *)(a1 + a2 + 1);
  if ( (unsigned __int8)(v2 - 48) > 9u )
    v2 -= 55;
  v4 = v2 & 0xF;
  v5 = (v3 - 55) & 0xF;
  if ( (unsigned __int8)(v3 - 48) <= 9u )
    v5 = v3 & 0xF;
  return v5 | 16 * v4;
}

可以用python等价

a1=[0x31,0x41,0x32,0x46,0x39,0x34,0x33,0x43,0x34,0x44,0x38,0x43,0x35,0x42,0x36,0x45,0x41,0x33,0x43,0x39,0x42,0x43,0x41,0x44,0x04,0x37,0x45,0x00,0x00]
def func(a1,a2):
    v2=a1[a2]
    v3=a1[a2+1]
    if (v2-0x30)&0xff>9:
        v2=(v2-0x37)&0xff
    v4 = v2 & 0xF
    v5 = (v3 - 0x37) & 0xF
    if (v3-0x30)&0xff<=9:
        v5=v3&0xf
    return v5 | 16*v4

```![ ](https://img-blog.csdn.net/20180413180230859?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjA0NDgx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
这里call的返回值,跟几次就会发现是





<div class="se-preview-section-delimiter"></div>

key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]
“`

key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]

然后异或异或后的结果要等于v9这个数组。

整理一下思路:(serial rol 2)^key==v9
接下来直接爆破就好。
不过需要注意的是找了一圈好像没有找到python里面的循环左移,只有字节写了

def rol(serial):
    a=serial<<2
    b=serial&0xc0
    b=b>>6
    serial=a|b
    return serial&0xff

解题脚本

key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]
flag=[0x0F,0x87,0x62 ,0x14,0x01,0xC6,0xF0,0x21,0x30,0x11,0x50,0xD0,0x82,0x23,0xAE,0x23,0xEE,0xA9,0xB4,0x52,0x78,0x57,0x0C,0x86,0x8B]
for i in range(25):
    for a in range(300):
        if key[i]^(rol(a))==flag[i]:
            print chr(a),

99.exe
先看main函数

v31 = 0;
  v9 = 1;
  v10 = 4;
  v11 = 14;
  v12 = 10;
  v13 = 5;
  v14 = 36;
  v15 = 23;
  v16 = 42;
  v17 = 13;
  v18 = 19;
  v19 = 28;
  v20 = 13;
  v21 = 27;
  v22 = 39;
  v23 = 48;
  v24 = 41;
  v25 = 42;
  v26 = 26;
  v27 = 20;
  v28 = 59;
  v29 = 4;
  v30 = 0;
  printf("please enter flag:");
  while ( 1 )
  {
    v6 = getch();
    v32[v31] = v6;
    if ( !(_BYTE)v6 || v32[v31] == 13 )
      break;
    if ( v32[v31] == 8 )
    {
      printf("\b\b");
      --v31;
    }
    else
    {
      printf("%c", v32[v31++]);
    }
  }
  v8 = 0;
  for ( i = 0; i < 17; ++i )
  {
    if ( v32[i] != byte_415768[*(&v9 + i)] )
      v8 = 1;
  }
  if ( v33 != 49 || v34 != 48 || v35 != 50 || v36 != 52 || v37 != 125 )
    v8 = 1;
  v32[v31] = 0;
  printf("\r\n");
  if ( v8 )
  {
    printf("wrong\n");
    main(v3, v4, v5);
  }
  else
  {
    printf("success\n");
  }

打眼一看,不知道谁是输入的字符,可以逆向推一下,决定success输出的是v8的值,决定v8的值得就是下面的代码了。

if ( v33 != '1' || v34 != '0' || v35 != '2' || v36 != '4' || v37 != '}' )

这里可以推断出最后几个字符1024}

 v8 = 0;
  for ( i = 0; i < 17; ++i )
  {
    if ( v32[i] != byte_415768[*(&v9 + i)] )
      v8 = 1;
  }

上面这段可以判断出就是决定程序的关键了。
byte_415768[*(&v9 + i)]这里是把v9数组作为下标来取byte_415768中的值。
贴上脚本吧

a=[0x73, 0x4B, 0x66, 0x78, 0x45, 0x65, 0x66, 0x74, 0x7D, 0x66,
  0x7B, 0x67, 0x79, 0x72, 0x59, 0x67, 0x74, 0x68, 0x74, 0x79,
  0x68, 0x69, 0x66, 0x73, 0x6A, 0x65, 0x69, 0x35, 0x33, 0x55,
  0x55, 0x72, 0x72, 0x72, 0x5F, 0x74, 0x32, 0x63, 0x64, 0x73,
  0x65, 0x66, 0x36, 0x36, 0x32, 0x34, 0x36, 0x30, 0x38, 0x37,0x31, 0x33, 0x38, 0x5C, 0x30, 0x30, 0x38, 0x37, 0x31, 0x33, 0x38]
b=[1,4,14,10,5,36,23,42,13,19,28,13,27,39,48,41,42,26,20,59,4,0]


for i in range(17):
    print chr(a[b[i]]),
    #KET{e2s6ry3r5s8f61024}

总结,pwn还是没做出来,扎心。发现了一些被忽略的小错误,总是卡在那些错误上。还需要更多的实践吧。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/79931013