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 <= )
{
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為位址值