逆向学习1

2018.7.25

where is your flag

把文件拖进IDA32位发现打不开,就知道文件是64位的,用IDA64位打开后,按shift+F12来到被解析文件区域,分析文件的名字,发现与所找的东西有关的话,就双击点开,也可以按ctrl+f来搜索有关文件,本题出现的解析文件区域如下图
这里写图片描述

发现where is your flag这个文件名字与所找有关,双击点开,再双击出现的区域上的箭头,再按F5,就会出现伪代码了,接下来分析伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  if ( argc <= 1 )
  {
    puts("where is your flag?");
  }
  else if ( (unsigned int)test((__int64)argv[1]) )
  {
    puts("you got it!");
  }
  else
  {
    puts("try again!");
  }
  return 0;
}

发现只要符合第二个条件,就可以得到flag,第二个条件中test()是个自定义的函数,点击test,出现另一个伪代码:

_BOOL8 __fastcall test(__int64 a1)
{
  signed int i; // [rsp+1Ch] [rbp-24h]
  __int64 s2; // [rsp+20h] [rbp-20h]
  __int64 v4; // [rsp+28h] [rbp-18h]
  __int16 v5; // [rsp+30h] [rbp-10h]
  char v6; // [rsp+32h] [rbp-Eh]
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  s2 = 4118914718243424768LL;
  v4 = 4112445312588908291LL;
  v5 = 5391;
  v6 = 27;
  if ( strlen((const char *)a1) != 19 )
    return 0LL;
  for ( i = 0; i <= 18; ++i )
    *((_BYTE *)&s2 + i) ^= 0x66u;
  return !memcmp((const void *)a1, &s2, 5uLL)
      && *(_BYTE *)(a1 + 18) == v6
      && *(_BYTE *)(a1 + 7) == *(_BYTE *)(a1 + 10)
      && *(_BYTE *)(a1 + 10) == *(_BYTE *)(a1 + 13)
      && *(char *)(a1 + 13) == SHIBYTE(s2) - 49
      && !memcmp((const void *)(a1 + 5), (char *)&v4 + 5, 2uLL)
      && !memcmp((const void *)(a1 + 8), &v5, 2uLL)
      && !memcmp((const void *)(a1 + 11), (char *)&s2 + 5, 2uLL)
      && !memcmp((const void *)(a1 + 14), &v4, 4uLL);
}

我们想要获得flag,就要test函数返回值为1,所以strlen((const char *)a1) == 19,a1的长度应该为19。所以可以用python写脚本,先写

flag=[0]*19

Int64是有符号 64 位整数数据类型, 等于long, 占8个字节. -9223372036854775808 9223372036854775807
Int32, 等于int, 占4个字节. -2147483648 2147483647
Int16, 等于short, 占2个字节. -32768 32767
java中内码(运行内存)中的char使用UTF16的方式编码,一个char占用两个字节,但是某些字符需要两个char来表示。所以,一个字符会占用2个或4个字节。在c语言中 char 占1个字节。

 signed int i; // [rsp+1Ch] [rbp-24h]
  __int64 s2; // [rsp+20h] [rbp-20h]
  __int64 v4; // [rsp+28h] [rbp-18h]
  __int16 v5; // [rsp+30h] [rbp-10h]
  char v6; // [rsp+32h] [rbp-Eh]

由这里可知由于s2、v4、v5和v6的地址是相连的,所以当从s2的起始地址开始进行19个字节的异或处理时,v4、v5和v6中的数值也会被处理。
由于s2占据了多个字节,在存储的过程中,一定存在着先分配的字节放在高地址还是低地址的问题。在s2中先分配的字节应该是放在了低地址处。
先把s2、v4、v5、v6转换成十六进制:

s2 = 0x3929531D01070A00LL;
  v4 = 0x391257391F150703LL;
  v5 = 0x150F;
  v6 = 0x1B;

然后对s2、v4、v5和v6中的元素按顺序写出,应为:

 s2 = 0x00,0x0A,0x07,0x01,0x1D,0x53,0x29,0x39;
 v4 = 0x03,0x07,0x15,0x1F,0x39,0x57,0x12,0x39;
 v5 = 0x0F,0x15;
 v6 = 0x1B;

所以可以在脚本中令

s2=[0x00,0x0a,0x07,0x01,0x1d,0x53,0x29,0x39,0x03,0x07,0x15,0x1f,0x39,0x57,0x12,0x39,0x0f,0x15,0x1b]

^=是C/C++的一个符合运算符。表示异或赋值。如:a^=b相当于:a=a^b;
异或就是两个数的二进制形式,按位对比,相同取0,不同取1
然后对其进行异或处理,s2、v4、v5和v6中的元素实际对应的字母为:

 s2 = "flag{50_";
 v4 = "easy_lt_";
 v5 = "is";
 v6 = "}";

在脚本可以这样写

for i in range(19):
    s2[i]=chr(s2[i]^0x66)

然后分析代码

 return !memcmp((const void *)a1, &s2, 5uLL)
      && *(_BYTE *)(a1 + 18) == v6
      && *(_BYTE *)(a1 + 7) == *(_BYTE *)(a1 + 10)
      && *(_BYTE *)(a1 + 10) == *(_BYTE *)(a1 + 13)
      && *(char *)(a1 + 13) == SHIBYTE(s2) - 49
      && !memcmp((const void *)(a1 + 5), (char *)&v4 + 5, 2uLL)
      && !memcmp((const void *)(a1 + 8), &v5, 2uLL)
      && !memcmp((const void *)(a1 + 11), (char *)&s2 + 5, 2uLL)
      && !memcmp((const void *)(a1 + 14), &v4, 4uLL);

memcmp是比较内存区域buf1和buf2的前count个字节。该函数是按字节比较的
基本原型
int memcmp(const void *buf1, const void *buf2, unsigned int count);
buf1 – 指向内存块的指针。
buf2 – 指向内存块的指针。
count – 要被比较的字节数。
返回值
当buf1小于buf2时,返回值<0
当buf1=buf2时,返回值=0
当buf1>buf2时,返回值>0
例如:
s1,s2为字符串时候memcmp(s1,s2,1)就是比较s1和s2的第一个字节的ascII码值;
memcmp(s1,s2,n)就是比较s1和s2的前n个字节的ascII码值;
如:char *s1=”abc”;
char *s2=”acd”;
int r=memcmp(s1,s2,3);
就是比较s1和s2的前3个字节,第一个字节相等,第二个字节比较中大小已经确定,不必继续比较第三字节了。所以r=-1.
所以要想返回值为1,memcmp((const void *)a1, &s2, 5uLL)就要为0
对于之后每一步,只要挨着分析即可:

a1前5位为’flag{‘
a1最后1位为’}’
a1第7、10、13位应该为’_’-49,即’.’
a1第5、6位为’lt’
a1第8、9位为’is’
a1第11、12位为‘50’
a1第14、15、16、17位为‘easy’
得到a1为:flag{lt.is.50.easy}

所以可以写脚本为

flag[0:5]=s2[0:5]
flag[18]=s2[18]
flag[13]=chr(ord(s2[7])-49)
flag[10]=flag[13]
flag[7]=flag[10]
flag[5]=s2[8+5]
flag[6]=s2[8+5+1]
flag[8]=s2[16]
flag[9]=s2[17]
flag[11]=s2[5]
flag[12]=s2[6]
flag[14:18]=s2[8:12]

最后得到flag

a=""
for i in range(len(s2)):

    a+=flag[i]
print (a)

1.GetDlgItemText :用于获取一个控件的文本
函数作用:该函数用来得到一个控件的Caption,或用来得到EDIT控件的文本内容。
函数原型:UINT GetDlgltemText(HWND hDlg,int nlDDlltem,LPTSTR IpString,int nMazCount);
GetDlgItemText函数里面的4个参数:
第一个指向含有控制的对话框的句柄。 API都得通过句柄操作,如果在MFC中,本项可以不写,默认为this。
第二个为ID,指定标题或文本将被检索的控制的标识符。,即你想要得到那个控件的ID。(如 IDC_EDIT1)
第三个指向获取标题或文本的缓冲器的指针。也就是需要一个用来存放读取到的内容的缓冲区, 你得先定义一个字符串用来获取该值(只能是数组或new开辟的空间,不能是字符指针,我测试过用字符指针读取不到值)
第四个为文本最大长度
例如:
TCHAR t_name[255]; //声明存字符串的地方(TCHAR类型的)
GetDlgItemText(hwnd,IDC_EDIT1,t_name,strlen(t_name)); //第一个参数是句柄,第二个参数是控件的ID,第三个参数是存字符串的地址,第四个参数是最大允许存入多大的数据。
MessageBox(hwnd,t_name,t_name,0); //把得到的字符串显示出来

2.char 在java中是2个字节。java采用unicode,2个字节(16位)来表示一个字符。
在c语言中 char :1个字节。

3.strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)。例如:

char str[20]="0123456789";
long a=strlen(str); //a=10; str为地址值
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值