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.《加密解密》

 

posted @ 2021-06-03 21:05  re0juren  阅读(1297)  评论(0编辑  收藏  举报