网鼎杯jocker&signal(angr)

jocker

这道题我原来写过,但是当时半生不熟的写完了,现在再来一遍感觉非常丝滑

堆栈不平衡

进入程序后一个大大的报错

sp寄存器不平衡,原来只是知道碰见这个报错应该怎么办,正好借着这次分享的机会研究一下

先打开ida左上角的Options然后勾选上Stack pointer就可以看到

在调用一个函数之后sp就变为负数了

这是因为push和pop不匹配 为什么不匹配呢? 进入这个函数看看

看了这个函数我们可以知道是这个函数出现了问题,它retf的数据为 0x4904 正常函数执行之后要返回调用这个函数的下一条指令,这样我们就知道了这里为什么会出现错误

如何修复

回到main函数部分,在出现错误的指令的前一条指令处alt+k修改堆栈

改为0即可

如法炮制几次即可修复,这样我们就得到了完美的main函数开始分析

分析main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char input[50]; // [esp+12h] [ebp-96h] BYREF
  char Destination[80]; // [esp+44h] [ebp-64h] BYREF
  DWORD flOldProtect; // [esp+94h] [ebp-14h] BYREF
  size_t input_len; // [esp+98h] [ebp-10h]
  int i; // [esp+9Ch] [ebp-Ch]

  __main();
  puts("please input you flag:");
  if ( !VirtualProtect(encrypt, 200u, 4u, &flOldProtect) )
    exit(1);
  scanf("%40s", input);
  input_len = strlen(input);
  if ( input_len != 24 )        输入长度要为24
  {
    puts("Wrong!");
    exit(0);
  }
  strcpy(Destination, input);
  wrong(input);
  omg(input);
  for ( i = 0; i <= 186; ++i )                 
    *((_BYTE *)encrypt + i) ^= 0x41u;    将entry指向的区域每个字节进行异或(smc) 
  if ( encrypt(Destination) )
    finally(Destination);
  return 0;
}

VirtualProtect函数用于更改指定进程的虚拟内存区域的保护属性。它可以使指定的虚拟内存区域变为可读,可写,可执行,也可设置为不可访问,从而保护该区域

BOOL VirtualProtect(
  [in]  LPVOID lpAddress,
  [in]  SIZE_T dwSize,
  [in]  DWORD  flNewProtect,
  [out] PDWORD lpflOldProtect
);

第一个参数为要更改保护属性的起始页地址

第二个参数为更改的大小

第三个为内存保护选项、

第四个为指向变量的指针,这个变量接受指定区域的第一页的先前访问保护值

这个函数在这里就是确保指向的区域的保护属性已经被去除(如果在保护会返回1)

char *__cdecl wrong(char *a1)
{
  char *result; // eax
  int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i <= 23; ++i )
  {
    result = &a1[i];
    if ( (i & 1) != 0 )
      a1[i] -= i;             如果为当前数字编号为奇数
    else
      a1[i] ^= i;             为偶数
  }
  return result;
}

值得一提的是这里前面就把字符串的地址赋予result是有用(某种情况下)

int __cdecl omg(char *a1)
{
  int v2[24]; // [esp+18h] [ebp-80h] BYREF
  int i; // [esp+78h] [ebp-20h]
  int v4; // [esp+7Ch] [ebp-1Ch]

  v4 = 1;
  qmemcpy(v2, &unk_4030C0, sizeof(v2));
  for ( i = 0; i <= 23; ++i )
  {
    if ( a1[i] != v2[i] )         更改后的input要与v2的一致
      v4 = 0;
  }
  if ( v4 == 1 )
    return puts("hahahaha_do_you_find_me?");
  else
    return puts("wrong ~~ But seems a little program");
}

unsigned char ida_chars[] =
{
  0x66,0x6B,0x63,0x64,0x7F,0x61,0x67,0x64, 0x3B,0x56,0x6B,0x61,0x7B, 
  0x26,0x3B,0x50,0x63,0x5F,0x4D,0x5A,0x71,0x0C,0x37 0x66
};

脚本:

#include<stdio.h>

int main()
{
    char ida[24] =
    {
        0x66,0x6B,0x63,0x64,0x7F,0x61,0x67,0x64, 0x3B,0x56,0x6B,0x61,0x7B, 0x26,0x3B,0x50,0x63,0x5F,0x4D,0x5A,0x71,0x0C,0x37,0x66
        };
    int i;
    int j;
    for(i=0;i<24;i++)
    {
        if(i%2==0)    //即为偶数 
        {
            ida[i] ^= i; 
        }
        else
        {
            ida[i] += i;  
        }

    }

    for(j=0;j<24;j++)
    {
        printf("%c",ida[j]);
    }
    return 0;
}

得到flag

flag{fak3_alw35_sp_me!!} 假的

下个断点让smc自己解除

smc:我自己的理解就是对某段代码继续加密(最常见的就是异或),一般可以直接动调解除

还可以用idapython

sea = ScreenEA()
c = 1
for i in range(0,186):
      c = Byte(sea+i)^0x41
      PatchByte(sea+i,c)

在这里下个断点然后输入假flag

重新创建函数

这里的Buffer是 hahahaha_do_you_find_me?

还有finall函数

找到403040里面是

unsigned char ida_chars[] =
{
  0x0E,  0x0D, 0x09, 
  0x06, , 0x13,  
  0x05, 0x58,  0x56, 
  , 0x3E, , 0x06,
  0x0C, 0x3C, 0x1F,
   0x57, 0x14,
  0x6B,  0x57 0x59,
  
};

解密entry里面的 脚本:

#include<stdio.h>

int main()
{
    char ida[18] =
{
   0x0E,0x0D,0x09,0x06,0x13,0x05,0x58,0x56,0x3E,0x06,0x0C,0x3C, 0x1F,0x57,0x14,0x6B,0x57,0x59
};
char ha[18]="hahahaha_do_you_find_me?"; 
char flag[18]={0};
    int i;
    int j;
for(i=0;i<18;i++)
{
  flag[i]=ha[i]^ida[i];
	
}

for(j=0;j<18;j++)
{
	printf("%c",flag[j]);
}
    return 0;
}

flag{d07abccf8a410

提示还有一部分flag 查看finally函数

int __cdecl sub_40159A(char *a1)
{
  unsigned int v1; // eax
  char v3[9]; // [esp+13h] [ebp-15h] BYREF
  int v4; // [esp+1Ch] [ebp-Ch]

  strcpy(v3, "%tp&:");
  v1 = time(0);
  srand(v1);
  v4 = rand() % 100;
  v3[6] = 0;
  *&v3[7] = 0;
  if ( (v3[v3[5]] != a1[v3[5]]) == v4 )
    return puts("Really??? Did you find it?OMG!!!");
  else
    return puts("I hide the last part, you will not succeed!!!");
}

坏了,这是真的随机加密

看了一下别人的wp发现是纯脑洞 根据flag的最后1位为} 再猜测是异或然后解密

singal

这道题用angr执行会非常快,angr我认为就是把程序的可能都来一遍看看什么可以

不介绍angr直接使用

脚本:

import angr
p=angr.Project("./signal.exe")         
state=p.factory.entry_state()          
sm=p.factory.simgr(state)             
sm.explore(find=0x4017A5,avoid=0x4016ED)
if sm.found:                         
    find_state=sm.found[0]             
    print(find_state.posix.dumps(0))    

没错就是这么神奇!

就这样结束感觉太水了,再来点angr的笔记吧

angr

angr是什么

就是帮你寻找flag的工具(bushi)

angr是一个支持多处理架构用于二进制分析的工具,它提供了动态符号执行的能力和多种静态分析的能力。

符号执行,初始参数用变量替换,模拟程序的执行流程,维护执行到各个位置时的状态

什么是符号执行?

符号执行就是利用符号状态和符号执行来得到我们的目标单位

一句话概括:angr可以利用自身特性约束求解最终走到我们想要的位置

那么如何约束求解呢?

就比如如果你想要走到print("Path-2")的位置 就需要满足

自身特性是什么?

利用符号替代自身就是angr的特性

剩下的就自己去学习吧haha

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值