一个人的角落,也许并没人会看到这里~~ 祝福你,工作顺心,学习快乐~
--:--

北理研究生CTF Reverse 之 mips

这次北理面向在读研究生举办CTF大赛,直接线下赛,时长12小时,其中选择填空1小时,关卡题2小时(之后加时2小时),9小时攻防,攻防赛题在先知安全有提到。

由于第一届,邀请的学校队伍属于中等偏下水平。最后,24支队伍只有9个分数为正,另有近一半左右队伍线下攻防没有拿分。

主办方声称原则上不提供外网环境。

逆向题只有一道,而且为mips架构第一次遇到。手上没有运行环境,只能静态调,花了一个多小时才搞清楚流程,之后时间不够只能做罢,后来发现当场也没有人做出。

赛后再分析一遍其实还是挺简单的。

先附上解密代码,回舍,日后有时间再补。

程序: mips

gdb-reverse-key

#include <stdint.h>
#include <stdio.h>


void dec(uint32_t *val, uint32_t *k)
{
	uint32_t delta = 0x9e3779B9;
	uint32_t sum = 0xb8ab04e8;
	uint32_t v1 = val[1], v0=val[0];
	for(int i = 0; i < 40; i++)
	{
		v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]);
		sum -= delta;
		v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]);
	}
	printf("0x%x~~~\n", sum);
	val[0] = v0;
	val[1] = v1;

	return ;

}
int main()
{
	uint32_t key[4]={
		0x2bc12411, 0x0deadbee, 0x0276630b, 0x014f53f5
		};
	uint32_t value[]={
		0x9a5e3731, 0x2ed28785,
		0xeb26da7d, 0x82b06241,
		0x21d9dcd4, 0x44bdda49,
		0x15f62308, 0x7b0546ee,
		0xf6b4a519, 0x71c0d531
		};
	for(int i =0; i< 10; i+=2)
	{
		dec(&value[i],key);
	}
	printf("%s\n", value);
	//printf("0x%8x, 0x%8x\n", value[0], value[1]);
	return 0;
	
}

Linux Sound Server

ArchLinux下使用DWM有两个月了,但是声音模块一直是很大的问题。

一开始以为DELL Vsion3668的声卡驱动不同寻常,毕竟之前在Windows下的驱动就装了老费劲了。

之前CTF校赛加上嵌入式论文讨论班一阵忙活,空出时间继续搞ArchLinux,没有声音一直很难受。

查文档发现原来是pulseaudio没有启动,这是Linux下的声音Server。

Linux下的声音系统比较杂,现今多数使用ALSA;

ALSA是一个集成了声卡驱动和底层接口的东西,而pulseaudio是声音服务器,应用程序要想输出声音需要往pulseaudio里边送。

于是:

vim ~/.xinitrc
pulseaudio --start

启动声间服务器。

参考:

https://wiki.archlinux.org/index.php/Sound_system https://wiki.archlinux.org/index.php/Advanced_Linux_Sound_Architecture

华师大CTF逆向题分析之Draw It(Monster)

Draw It(Monster)

程序打开运行界面:

draw_it_1

查壳发现为UPX压缩壳,UPX壳代码是开源的,加壳脱壳是同一个程序。

draw_it_2

手脱ESP定律,或者往下找popad接着一个大跳可以直接到程序OEP。 手脱比较节省时间,但是有一些区段或者其它信息没法还原。

draw_it_3

脱壳之后用IDA分析来到程序消息处理函数找到关键位置:

draw_it_4

通过分析,程序取所画图片的连续相同的点的个数与cmp数据做对比, 猜测cmp数据为Flag相关数据,存储了Flag图片的一些信息,即bitmap方式连续相同的点的个数。 因为位图是24位,每个像素点用3位来存储,所以比较的时候需要乘3。

导出数据:

draw_it_5

进一步验证,将数据全部相加得到30000(50*600),确定了之前的分析。

编写Win32程序,解析数据:

int sum=0,cmp_i=0,
for(int i=0;i<50;i++)
{
	for(int j=0;j<600;j++)
	{
		sum++;
		if(sum < cmp[cmp_i])
		{
			if(  odd )
				SetPixel(hdc,j, 50-i,RGB(255,0,0));//BMP从上到下,从左到右存贮
		}
		else
		{
			sum = 0;
			odd = !odd;
			all+=cmp[cmp_i] ;
			cmp_i++;
			
		}

	}
}

可以得到结果:

draw_it_6

华师大CTF逆向题分析之巴黎解密

预赛逆向题有三道 第一道为巴黎解密ctf文件 第二道为画出FLAG 第三道为Stephen

决赛逆向题有四道,只做出了前两题。 第一题为MFC程序跑不起来 第二道为Elf64、STL对素数表加密 第三道使用 VirtualProtect 反调试。 第四道使用了一个linux程序对数据进行封装,没时间做。 前两道相对简单

巴黎解密ctf文件

有个文件加密工具,能将一个文件加密到一个.ctf文件中去。

有一个犯罪分子将存有犯罪记录的一个名为“CTFtest.ctf” 的加密文件被删除了。现经过数据恢复,我们已经恢复了该文件。

但是很不幸,该文件头部的部分数据已经被覆盖掉了。这个.ctf文件已经不能正常打开了。 而且加密该文件的口令,犯罪分子也不愿意交代,我们只知道他惯用的口令是一个8位纯数字口令。

请选手下载http://ctf.ecnu.edu.cn/question/attachment1.zip

通过其中的.ctf文件以及解密程序最大限度地恢复出文件中的内容,flag就在里面。 Flag形式为大写32位md5。

题目给出了一个解密程序以及一个加密后丢失了一部分文件头的文件

如图:

final_re_1

目测丢失了前35bytes的数据

解密程序无壳无花,直接IDA,MFC静态库编译。

结合导入表以及程序运行来看,调用了CreateFile、ReadFile、MessageBox等参数,

顺利跟踪到了程序关键地址 0x402570 分析函数,局部变量比较多,

final_re_2

应该八九不十了。 接着分析:

final_re_3

程序打开文件,判断前4 bytes 内容为 0x33465443 转换为字符CTF3

final_re_4

接着读4bytes转为DWORD型,为接下来要读取的长度并存储。 接着读文件16字节,取密码框内容, 将密码进行两次MD5加密,将结果与取到的16字节进行对比。

final_re_5

接着读4bytes转为DWORD型,做为接下来要读取的长度,再次读取,然后关闭文件。

至此,加密文件全部读取完毕,小结一下文件结构:

4 bytes 为固定字符‘CTF3’
4 bytes 为len of head
{...}   为head
16 bytes 将密码经两次MD5加密的密文
4 bytes 为len of body
{...}   为body

此时回过头来分析.ctf加密文件内容。

final_re_6

发现偏移为0x29位置4bytes的内容为0x0000619C,这一部分正好对应为文件此后的长度, 可以推定为len_of_body存储位置。向前16bytes为MD5密文区域,并且只有6bytes保留。

根据提示,密码为8位数字,通过这一部分MD5,应该可以还原。 编写程序:

#include <stdio.h>
#include <windows.h>
#include "md5.h"

unsigned char data[6]={0x48,  0xB1,  0xED,  0x05,  0x8D,  0xF7 };

int main()
{
	unsigned char  a[100] = {0},b[17] = {0},c[17] = {0};
	int num = 0, i=0;

	for( num = 10000000; num < 99999999; num++){
		_itoa(num,(char *)a, 10);
		MD5_CTX md5;

		MD5Init(&md5);                
		MD5Update(&md5,a,strlen((char *)a));  
		MD5Final(&md5,b);
		
		MD5Init(&md5);                
		MD5Update(&md5,b,16);  
		MD5Final(&md5,c);

		i=0;
		while(data[i] == c[i+10])
		{
			i++;
			if(i>=6)
				goto l;
		}
	}
l:
	printf("%d", num);
	getchar();
	getchar();
	system("pause");
	return 1;

}

程序跑出密码为20160610。 此时还剩11bytes的head内容不知道。

final_re_7

接着查找程序对head部分的引用,发现程序对head沁使用多字节字符转宽字符库函数。 进行字符串拼接之后,传入CreateFile。 由此可以断定,head部分内容为加密文件的文件名。

至此我们可以对加密文件的分析,基本结束,将文件头丢失部分恢复,Head部分随便指定一个文件名。 解密程序将文件恢复:

final_re_8

得到一个压缩包文件,查看内容为Word文件,打开得到Flag。

final_re_9

MSP432 Graphics Library

MSP432提供了对LCD屏操作的库函数,而从整个对LCD的操控来说,自下面上的分成了三层:HAL硬件抽象层,LCD屏的驱动层以及grlib上层图形库。

按照官方文档的介绍grlib库提供了一些上下文控制函数以及一系列简单的图表绘制函数:圆,位图,直线,矩形,字符串,按钮,图像按钮,单选框,多选框,以及位图数据转C格式数据工具。

HAL实现对MCU与LCD之间接口的配置,以及底层LCD控制器的串行通信,实现控制字符与数据字符的传输。 LCD驱动层利用HAL实现LCD的基本元素的绘制(点、直线等的绘制),为 grlib 提供调用接口。

以下为三层架构所对应的头文件,包含各自了所在层的函数原型,以及函数调用的方法

#include <ti/grlib/grlib.h>
#include "LcdDriver/Crystalfontz128x128_ST7735.h"
#include "LcdDriver/HAL_MSP_EXP432P401R_Crystalfontz128x128_ST7735.h"

LCD的初始化代码一般如下:

    /* Graphic library context */
    Graphics_Context g_sContext;  //全局上下文

    /* Initializes display */
    Crystalfontz128x128_Init();   //驱动层的LCD初始化代码

    /* Set default screen orientation */
    Crystalfontz128x128_SetOrientation(0);  //设置屏幕方向

    /* Initializes graphics context */
    Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128, &g_sCrystalfontz128x128_funcs); 
    Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_BLUE);
    Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE);
    GrContextFontSet(&g_sContext, &g_sFontFixed6x8);

可以看出初始化函数调用关系是逐层的,驱动层初始化通过调用HAL层进行硬件初始化,其中包括PortInit、SpiInit与LCD芯片初始化。 PortInit:为TI通用的端口复用配置,针对不同TI芯片电路进行端口的开启与复用,此部分代码同样为通用。

需要定制的只有HAL通信功能引脚的配置:

// Ports from MSP432 connected to LCD
#define LCD_SCK_PORT          GPIO_PORT_P1
#define LCD_SCK_PIN_FUNCTION  GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_MOSI_PORT         GPIO_PORT_P1
#define LCD_MOSI_PIN_FUNCTION GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_RST_PORT          GPIO_PORT_P5
#define LCD_CS_PORT           GPIO_PORT_P5
#define LCD_DC_PORT           GPIO_PORT_P3

// Pins from MSP432 connected to LCD
#define LCD_SCK_PIN           GPIO_PIN5
#define LCD_MOSI_PIN          GPIO_PIN6
#define LCD_RST_PIN           GPIO_PIN7
#define LCD_CS_PIN            GPIO_PIN0
#define LCD_DC_PIN            GPIO_PIN7

// Definition of USCI base address to be used for SPI communication
#define LCD_EUSCI_BASE        EUSCI_B0_BASE

grlib的初始化内容则是主要围绕一个g_sContext结构体进行, Graphics_Context 结构体定义如下:

typedef struct Graphics_Context
{
    int32_t size;                         //!< The size of this structure.
    const Graphics_Display *display;    //!< The screen onto which drawing operations are performed.
    Graphics_Rectangle clipRegion;        //!< The clipping region to be used when drawing onto the screen.
    uint32_t foreground;                 //!< The color used to draw primitives onto the screen.
    uint32_t background;                 //!< The background color used to draw primitives onto the screen.
    const Graphics_Font *font;            //!< The font used to render text onto the screen.
} Graphics_Context;

Graphics_Display *display结构体包括了特定LCD的宽高信息以及驱动函数指针。由此可以看出,LCD屏与上grlib也是分离的,当硬件更换为不同的LCD时,只需修改对应LCD驱动层函数指针,甚至可以实现LCD热更换。

但三层架构的设计架构移植性较高,牺牲了一部分屏幕控制器的硬件性能。

MSP432 launchpad User Application Port UART

串口通信的配置

串口的配置 主要包括两个方面:

  • 初始化结构体的配置
  • 串口模块中断的开启

以下是基于DriverLib的初始化代码

    const eUSCI_UART_Config uartConfig =
    {
            EUSCI_A_UART_CLOCKSOURCE_SMCLK,        // SMCLK Clock Source 48Mhz
            312,                                   // BRDIV = 312
            8,                                     // UCxBRF = 8
            0xAA,                                  // UCxBRS = 0xAA
            EUSCI_A_UART_NO_PARITY,                // No Parity
            EUSCI_A_UART_LSB_FIRST,                // MSB First
            EUSCI_A_UART_ONE_STOP_BIT,             // One stop bit
            EUSCI_A_UART_MODE,                     // UART mode
            EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION  // overSampling Mode
    };

    /* Configuring UART Module */
     UART_initModule(EUSCI_A0_BASE, &uartConfig);

    /* Enable UART module */
     UART_enableModule(EUSCI_A0_BASE);

     UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT);
     Interrupt_enableInterrupt(INT_EUSCIA0);

     MAP_Interrupt_enableSleepOnIsrExit(); //低功耗模式

比较困难的地方在于波特率的设置主要涉及到BRDIV,UCxBRF,UCxBRS, overSampling Mode这几个寄存器参数。

DriverLib手册中提到了一个参数计算器 http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html

如果上位机可以接收到FF FE之类的乱码一般是波特率设置不正确导致,不同的时钟源频率需要将其分离成所需要的频率,具体的计算公式在Reference Manual 的22.3.10小节有详细说明,将计算出来的波特率只要与上位机设置的相差不远也是可以进行通信的。

此外,网上有一部分相关帖子提到中断名的问题在CCS里边,系统会为所有中断定义一个默认中断,如果程序没有定义相关中断,在程序中又开启了此中断,中断触发后将会陷入到默认中断中。这里的默认中断内容是一个死循环,中断向量表在startup_msp432p401r_ccs.c中有定义。

  //Configure UART pins

  P1SEL0 |=BIT2 | BIT3;                  // set2-UART pin as second function



 __enable_interrupt();

 NVIC->ISER[0] = 1 << ((EUSCIA0_IRQn) & 31); // EnableeUSCIA0 interrupt in NVIC module

  //Configure UART

  UCA0CTLW0|= UCSWRST;
  UCA0CTLW0|= UCSSEL__SMCLK;             // PuteUSCI in reset

  // BaudRate calculation
  //12000000/(16*9600) = 78.125
  //Fractional portion = 0.125
  // User'sGuide Table 21-4: UCBRSx = 0x10
  // UCBRFx =int ( (78.125-78)*16) = 2

  UCA0BR0 =78;                           //12000000/16/9600
  UCA0BR1 =0x00;
  UCA0MCTLW= 0x1000 | UCOS16 | 0x0020;    //注意这一行的设定

  UCA0CTLW0&= ~UCSWRST;                  //Initialize eUSCI

  UCA0IE |=UCRXIE;                       // EnableUSCI_A0 RX interrupt

近几年来使用DriverLib开发的越来越多了,个人认为DriverLib可以在一定程度上简化开发流程,对新手友好,上手快,但是由于432官方对DriverLib手册并不是非常的友好,很多函数仅仅是一带而过,函数说明甚至没有源码注释写得好,这一使得学习432的内容需要齐头并进,能够简单易懂的用DriverLib难一些的还是要参考寄存器配置。

以下是串口中断处理函数,利用寄存器操作,将收到的串口数据进行回传。

/*
 * EUSCI A0 UART interrupt handler.
 */
void EUSCIA0_IRQHandler(void)
{
    if(UCA0IFG & UCRXIFG)
    {
     while(!(UCA0IFG&UCTXIFG));
           UCA0TXBUF = UCA0RXBUF;
     __no_operation();

    }
}

而DriverLib版:

    int receiveByte;
    receiveByte = UART_receiveData(EUSCI_A0_BASE);
    UART_transmitData(EUSCI_A0_BASE,receiveByte);

DriverLib的底层实现也是通过寄存器来完成的,这样可以通过学习源代码的方式了解某些寄存器的功能,省略某些技术细节,随着处理器执行效率的提高,代码越来越复杂,这样的库函数操作将会非常常见。

MSP432 使用Timer_A输出PWM

Timer_A 输出 PWM的问题

在官方例程中给出的是一种直接操作寄存器的方式启动的,这让我们这些刚刚接触MSP432这种嵌入式芯片的来说是很难看懂的,然而PWM的输出是可以重新映射到其它端口输出的,例程中又掺杂了PMAP GPIO端口的映射,这更使得学习无从下手。

通过寄存器操作的方式输出PWM波形

    TIMER_A0->CCR[0] = PWM_PERIOD;
    TIMER_A0->CCTL[1] = TIMER_A_CCTLN_OUTMOD_7;      // CCR1 Reset/set
    TIMER_A0->CCR[1] = PWM_PERIOD * (55 / 100.0);    // CCR1 PWM duty cycle
    TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK // 使用SMCLK时钟源
                  | TIMER_A_CTL_MC__UP   
                  | TIMER_A_CTL_CLR;  // SMCLK, up mode, clear TAR

引用msp432p401r.h头文件定义:

#define TIMER_A_CTL_MC__UP    ((uint16_t)0x0010)    /*!< Up mode: Timer counts up to TAxCCR0 */

通过 动态修改 TIMER_A0->CCR[1] 内容可以改变PWM间隔时间

通过DriverLib操作的方式输出PWM波形

/* Timer_A PWM Configuration Parameter */
Timer_A_PWMConfig pwmConfig =
{
    TIMER_A_CLOCKSOURCE_SMCLK,
    TIMER_A_CLOCKSOURCE_DIVIDER_1,
    32000,
    TIMER_A_CAPTURECOMPARE_REGISTER_1,
    TIMER_A_OUTPUTMODE_RESET_SET,
    3200
};
Timer_A_generatePWM(TIMER_A1_BASE, &pwmConfig);

Timer_A_setCompareValue(TIMER_A2_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1, PWM_PERIOD * (50/100.0f)); //动态设置PWM 间隔时间

而timer_a.C文件中对Timer_A_generatePWM函数定义如下

void Timer_A_generatePWM(uint32_t timer, const Timer_A_PWMConfig *config)
{
    //此处省略一部分,参数合法判断...

    privateTimer_AProcessClockSourceDivider(timer, config->clockSourceDivider);

    TIMER_A_CMSIS(timer)->CTL &=
            ~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK + TIMER_A_UPDOWN_MODE
                    + TIMER_A_DO_CLEAR + TIMER_A_TAIE_INTERRUPT_ENABLE);

    TIMER_A_CMSIS(timer)->CTL |= (config->clockSource + TIMER_A_UP_MODE
            + TIMER_A_DO_CLEAR);

    TIMER_A_CMSIS(timer)->CCR[0] = config->timerPeriod;

    TIMER_A_CMSIS(timer)->CCTL[0] &= ~(TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE
            + TIMER_A_OUTPUTMODE_RESET_SET);

    uint8_t idx = (config->compareRegister>>1) - 1;
    TIMER_A_CMSIS(timer)->CCTL[idx] |= config->compareOutputMode;
    TIMER_A_CMSIS(timer)->CCR[idx] = config->dutyCycle;
}

可以看到相关操作与寄存器类似,但一次只能修改一路PWM输出。难怪官方例程中直接使用寄存器操作,多路输出更加容易控制。