天天看點

逆向學習1

2018.7.25

where is your flag

把檔案拖進IDA32位發現打不開,就知道檔案是64位的,用IDA64位打開後,按shift+F12來到被解析檔案區域,分析檔案的名字,發現與所找的東西有關的話,就輕按兩下點開,也可以按ctrl+f來搜尋有關檔案,本題出現的解析檔案區域如下圖

逆向學習1

發現where is your flag這個檔案名字與所找有關,輕按兩下點開,再輕按兩下出現的區域上的箭頭,再按F5,就會出現僞代碼了,接下來分析僞代碼:

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

發現隻要符合第二個條件,就可以得到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(u);
  s2 = L;
  v4 = L;
  v5 = ;
  v6 = ;
  if ( strlen((const char *)a1) !=  )
    return L;
  for ( i = ; i <= ; ++i )
    *((_BYTE *)&s2 + i) ^= u;
  return !memcmp((const void *)a1, &s2, LL)
      && *(_BYTE *)(a1 + ) == v6
      && *(_BYTE *)(a1 + ) == *(_BYTE *)(a1 + )
      && *(_BYTE *)(a1 + ) == *(_BYTE *)(a1 + )
      && *(char *)(a1 + ) == SHIBYTE(s2) - 
      && !memcmp((const void *)(a1 + ), (char *)&v4 + , LL)
      && !memcmp((const void *)(a1 + ), &v5, LL)
      && !memcmp((const void *)(a1 + ), (char *)&s2 + , LL)
      && !memcmp((const void *)(a1 + ), &v4, LL);
}
           

我們想要獲得flag,就要test函數傳回值為1,是以strlen((const char *)a1) == 19,a1的長度應該為19。是以可以用python寫腳本,先寫

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;
           

是以可以在腳本中令

^=是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():
    s2[i]=chr(s2[i]^)
           

然後分析代碼

return !memcmp((const void *)a1, &s2, LL)
      && *(_BYTE *)(a1 + ) == v6
      && *(_BYTE *)(a1 + ) == *(_BYTE *)(a1 + )
      && *(_BYTE *)(a1 + ) == *(_BYTE *)(a1 + )
      && *(char *)(a1 + ) == SHIBYTE(s2) - 
      && !memcmp((const void *)(a1 + ), (char *)&v4 + , LL)
      && !memcmp((const void *)(a1 + ), &v5, LL)
      && !memcmp((const void *)(a1 + ), (char *)&s2 + , LL)
      && !memcmp((const void *)(a1 + ), &v4, LL);
           

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[]="0123456789";
long a=strlen(str); //a=10; str為位址值