gcc -static xxx.c -o xxx
upx為壓縮殼,將可執行文件進行壓縮,當可執行文件過小時加殼會失敗,所以此處采用了靜態鏈接的方式進行編譯;
附上設計的源碼:
#include<stdio.h> #include<string.h> #include<windows.h> int main(){ int a,b,c,d; int i; char answer[27]; char seq1[]="are you a student at AEU?"; char seq2[]="enter 1 or 0:"; char seq3[]="welcome to the competition,do you want any hints?"; char seq4[]="do you think the examiner is handsome?"; char seq5[]="you are right!"; char seq6[]="I modified the spack,You should try to remove the pack manually"; char seq7[]="Listen!the flag is "; char seq00[]=" AEU{eeAmi3_"; char flag[27]="AEU{eeAmi3_6e74rt_handi43}"; char seq02[]="This is the first time for us to hold the competition,Looking forward to your performance\nHere's a present for you:"; char seq03[]="Never give up\nNever lose hope.\nAlways have faith,\nIt allows you to cope.\nTrying times will pass,\nAs they always do."; char seq04[]="Just have patience,\nYour dreams will come true. \nSo put on a smile,\nYou'll live through your pain.\nKnow it will pass, \nAnd strength you will gain"; for(i=0;i<strlen(seq1);i++){ printf("%c",seq1[i]); Sleep(90); } printf("\n"); puts("enter 1 or 0:"); scanf("%d",&a); if(a){ for(i=0;i<strlen(seq3);i++){ printf("%c",seq3[i]); Sleep(90); } printf("\n"); puts("enter 1 or 0:"); scanf("%d",&b); if(b){ for(i=0;i<strlen(seq4);i++){ printf("%c",seq4[i]); Sleep(90); } printf("\n"); puts("enter 1 or 0:"); scanf("%d",&c); if(c){ for(i=0;i<strlen(seq5);i++){ printf("%c",seq5[i]); Sleep(90); } printf("\n"); for(i=0;i<strlen(seq6);i++){ printf("%c",seq6[i]); Sleep(90); } printf("\n"); for(i=0;i<strlen(seq7);i++){ printf("%c",seq7[i]); Sleep(200); } for(i=0;i<strlen(seq00);i++){ printf("%c",seq00[i]); Sleep(500); } Sleep(6000); puts(" (*^_^*)"); puts("are you really want the flag?"); for(i=0;i<strlen(seq02);i++){ printf("%c",seq02[i]); Sleep(80); } printf("\n"); for(i=0;i<strlen(seq03);i++){ printf("%c",seq03[i]); Sleep(80); } printf("\n"); for(i=0;i<strlen(seq04);i++){ printf("%c",seq04[i]); Sleep(80); } printf("\n"); for(i=0;i<strlen(seq7);i++){ printf("%c",seq7[i]); Sleep(200); } for(i=0;i<strlen(flag);i++){ printf("%c",flag[i]); Sleep(900); } printf("\n"); puts("now,input your answer:"); scanf("%s",answer); for(i=0;i<strlen(flag);i++){ if(flag[i]=='e') flag[i]+=1; else if(flag[i]=='r') flag[i]-=1; } if(!strcmp(answer,flag)) puts("right!"); else puts("wrong,Is it really that simple?"); Sleep(6000); } else{ puts("You can't solve the problem"); } } else{ puts("I can't give you a hint"); } } else{ puts("sorry,identity failed"); } return 0; }
upx xxx.exe -o xxx.exe
使用upx工具完成加殼,正常情況下upx工具可以順利完成脫殼
upx -d xxx.exe
但對殼進行修改后該工具便失效。
一,首先認識upx:
upx的作用是壓縮程序代碼,比如可執行文件中的123456用a代替,還會在程序的開頭插入一段代碼(用來解壓縮);當執行該文件時插入的代碼會起作用,將文件解壓,但解壓只能在內存中完成,采用靜態的方式是沒辦法看到解壓后的可執行代碼的。所以通用方法是動態調試,一直追蹤到解壓后的可執行代碼,完成轉儲后也就是脫殼成功。
二、修改upx
本題設計采用修改upx的標志:
修改為00 00 00 00
保存后新的文件在upx -d命令下回出錯,此處不演示。設計意圖在於讓解題人手動脫殼,修改方式比較簡單,輔助工具應該也能完成,沒有做過實驗,不在贅述。
upx放脫殼的幾種方法總結:
1.修改區段名,查看一下加殼后的各個段:
修改掉區段的名字,另存后不影響執行,upx-d失敗;另外的區段名同理。
2.修改特征碼:
pe工具能夠識別出文件被加upx是因為加殼后upx自身的特征碼被識別,我們可以修改特征碼達到使工具識別步出upx的目的;
特征碼:60 BE ?? ?? ?? 00 8D BE ?? ?? ?? FF
(留坑)
3.添加區段:
順便學習了關於PE的相關知識,匯總如下:
可執行文件的代碼和數據分類存儲在各個塊之中,PE頭與區段之間是塊表(說明了各個塊的相關信息)
VA:虛擬地址,即PE文件映射到內存后的地址(虛擬空間 )
RVA:相對虛擬地址,相對於PE載入地址的偏移。 VA=基址+相對虛擬地址
物理地址/文件偏移地址:RAW Offset,某個數據相對於文件頭的偏移,,顯示的地址就是物理地址
主要分析塊表:塊表由IMAGE_SECTION_HEADERS結構體來定義,原形如下:
比較晦澀,結合實例分析:
第一個塊表標黑部分,40個字節,
55 50 58 30 00 00 00 00(8個字節):UPX0字符的十六進制,表示塊名
00 70 00 00(4個字節):VirtualSize,表示實際上的區塊大小(未對齊之前),與區段表中的VSize值剛好對應
00 10 00 00(4個字節):VA,即第一個塊在內存中的映射地址,對應Voffset
00 00 00 00 (4個字節):該塊在磁盤中占用的空間,對應RSize
00 02 00 00 (4個字節):該塊在磁盤中的偏移,對應Roffset
后面連續三段4字節參見結構體定義,最后的80 00 00 E0為塊的屬性標志,定義了是否可讀、可寫、可執行。
重點區分下文件偏移地址和相對虛擬地址,兩者都是相對於頭的偏移;文件偏移地址是塊在磁盤中相對於PE頭的偏移,RVA則是PE加載映射到內存之后相對於頭的偏移。
如圖
,如圖分析UPX1的RAWoffset為200,即在PE中地址200開始的地方,往后找大小E00,即為UPX1這個段的實際代碼。
使用lordPE工具添加一個區段,可以使工具不能識別出upx殼。(留坑待補)
三、解題:
脫殼部分采用ESP定律,此處不演示
進入IDA分析:
對比源碼,整體上結構非常簡單,反匯編后略微晦澀,只需要能看懂棧的布局就能明白變量之間的關系;
邏輯就是比較str1與str2的值,v30[-95]實際上也是str2,原因如下:
v30為int型,占據4個字節,v30[-95]也就是從v30的地址往前推95*4=380(17Ch)個字節,在棧的布局上來看,v30在棧中的地址是esp+33C,往棧減小的位置推算,33c-17c=1c0,即v30[-95]實際上esp+1c0,對比棧的空間分布,也就是Str2.
其余的變量推算同理。必須要明確的是變量a為int型(占4字節),那么a[i]表示含義是基址加上i個int型的偏移,即4*i個字節。
(32位/64位環境下)
char型數據則占據1字節;double 8字節 ;short 占2字節
#以上參考:
1.從做題到出題再到做題三部曲-UPX - 先知社區 (aliyun.com)
2.《加密解密》