upx加壳防脱
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.《加密解密》