13 Security Lab

다양한 타겟 OS 크로스컴파일 빌드 / 메모리 구조체 매핑 문제 해결하기 본문

Computer Science/Programming

다양한 타겟 OS 크로스컴파일 빌드 / 메모리 구조체 매핑 문제 해결하기

Maj0r Tom 2017. 11. 13. 01:37

컴파일 할때 타겟 인텔, HP, ARM 등등 시스템 아키텍처가 여러개일 경우 그 환경에 따라서 고려하지 못한 문제가 발생할 수 있다.

 

대표적으로 Little Endian / Big Endian 과 x86 / x64 OS 환경별로 달라지는 요소들을 생각해 볼 수 있다.

어떤 것인지는 알지만 막상 컴파일해서 결과를 보기전까지는 또는 결과를 보더라도 바이너리 내부까지 보이는 것은 아니므로 알 수 없는 버그가 터지기도 한다.

..

 

1. 메모리 구조체 포인터 매핑 사용 문제

 

메모리에 읽어 놓은 바이너리 데이터 성격에 맞춰 구조체를 선언하여 매핑하려고 했었으나 x64비트 환경에서 예상치 못한 문제가 발생하였다.

나의 경우에는 아래의 코드 형태에서 문제가 발생했었다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct DataSet {
    unsigned long crcData
    unsigned char key
*stctData = NULL;
 
unsigned long crcData;
 
// ..
// ..
crcData = getCrc32() // 4bytes value
 
// 메모리에서 읽은 데이터를 구조체로 매핑
stctData = (struct DataSet *)memReadData; //assign Data from memory read
if(crcData == stctData->crcData)
{
    printf("do something");
}
 
cs

 

 

위 코드에서 if 문이 일치 되지않아 로그로 찍어봤으나 동일한 값으로 나왔지만 왜인지 알수 없게도 if문을 타지않았다.

1
if(crcData == stctData->crcData)
cs

 

 

결국 찾은 원인은 x64환경에 따른 자료형의 크기가 달라져 구조체 매핑이 잘못이루어진 것이 문제였다.

변수와 구조체멤버로 각각 선언한 "crcData" 값이 32비트 환경에서는 4바이트, 64비트 환겨에서는 8바이트 이기 때문에 

실제로는 4바이트를 넘어서 가져오게 되었고 메모리에 매핑 된 구조체의 멤버가 예상한 크기 이상을 가리키게 되었다.

 

메모리에는 아래와 같이 5바이트 의미를 가지는 값이 들어있었고

x86환경에서는 그에 맞춰 5바이트 크기의 구조체를 통해 4바이트 1바이트 멤버변수로 매핑하였다.

 

 

그러나 x64환경에서는 아래와 같이 8바이트 1바이트로 매핑되어 5바이트 뒤에 알수없는 값들까지 들어가고

두번째 멤버변수가 가리키는 값도 알수없게되었다.

 

 

 

 

 

따라서 비교했을 때 일반변수인 crcData는 0x0000000012345678을 갖고, stctData->crcData는 0x??????FF12345678을 갖기 때문에 같은 값이 될 수 없었던 것이다.

 

그러나, 로그로 찍었을 때는 각각

1
2
printf("crcData: %x", crcData)
printf("stctData->crcData: %x", stctData->crcData)
cs

으로 찍었었는데.. 포맷스트링 %x로 찍으니.. 4바이트까지밖에 출력이 안되었었다.. -> 8바이트 찍히도록 포맷트링을 변경해야 한다.

 

+ 이문제를 피하기 위해서는 구조체 포인터로 매핑 시 아래와 같이 해주는 것이 좋다.

1
2
3
4
5
6
7
8
9
// 이전
stctData = (struct DataSet *)memReadData;
 
// 수정
// 환경문제 없애려면 멤버별로 할당
struct DataSet * stctData = malloc(sizeof(struct DataSet));
//멤버별로 각각
memcpy(&(stctData->crcData), memReadData, 4); //첫번째 멤버
stctData->key = memReadData[4]; //두번쨰 멤버
cs

 

 

 

2. "#pragma pack(1)"

그 외에 의심이 되었던 부분은 구조체 멤버크기에 따른 Bytes Align 문제였는데..

구조체 선언 시 CPU 처리 단위에 맞춰서 Bytes Align (바이트 패딩)을 붙여 주게 되어 환경별로 달라지기 떄문에 발생하는 문제이다.

 

사용 예시)

 

1
2
3
4
5
6
7
//pragma pack 범위 내 1바이트 단위 처리하도록 지정
#pragma pack(1//범위 시작
struct DataSet {
    unsigned long crcData
    unsigned char key
*stctData = NULL
#pragma pack() //범위 
cs

 

 

- 일반적인 선언

 

아래 처럼 1바이트 멤버 byKey 뒤에는 바이트 패딩이 붙음

 

 

- pragma pack 명시

 

아래 처럼 1바이트 멤버 byKey 뒤에 바이트 패딩이 붙지 않음

 

 

 

3. Endian 설정

Raw Binary(File)에서 가져온 값 또는 메모리에서 가져온 값 사용 시 Big Endian / Little Endian 시스템을 고려하여 엔디안을 설정해주는 것이 필요하다.

컴파일 시 다양한 타겟 환경을 가질 경우에 일괄적으로 시스템에 맞춘 엔디안 설정을 해두는 것이 이롭다.

 

Comments