장 - 12
프로그래밍을 사용하여 MBR 읽기 및 변경
마스터 부트 레코드(MBR) 또는 마스터 파티션 테이블(MPT)
MBR(마스터 부트 레코드) 또는 MPT(마스터 파티션 테이블)는 FDISK.EXE DOS 명령을 실행하여 하드 디스크에 생성됩니다.
MBR에는 하드 디스크에서 활성(또는 부팅) 파티션을 로드하고 실행하기 위한 작은 프로그램이 들어 있습니다. 마스터 부트 레코드에는 시작 섹터, 끝 섹터, 파티션 크기 등 하드 디스크의 4개 기본 파티션에 대한 정보가 포함되어 있습니다.
MBR은 절대 섹터 0에 위치하거나 실린더 0, 헤드 0, 섹터 1에 위치한다고 할 수 있습니다. 디스크에 두 개 이상의 파티션이 있는 경우 확장 파티션의 각 볼륨의 시작 부분에 확장 마스터 부트 레코드가 있습니다.
자세한 설명은 이 책의 앞 부분에서 설명한 "디스크와 OS에 대한 논리적 접근 방식" 장을 참조하세요.
마스터 부트 레코드 형식
하드 디스크를 여러 개의 논리 드라이브로 분할할 수 있으며, DOS는 일반적으로 각 드라이브에 고유한 드라이브 문자를 할당합니다. 한 번에 하나의 파티션만 활성(또는 부팅) 파티션으로 표시할 수 있습니다.

마스터 부트 레코드는 기본 파티션 테이블에 최대 4개의 항목으로 제한됩니다. 그러나 확장된 MBR의 위치는 확장된 파티션 테이블이 포함된 MBR을 사용하여 얻을 수 있습니다. 이 확장된 파티션 테이블의 형식은 기본 파티션 테이블과 동일하지만 부팅 코드가 포함되지 않고 일반적으로 446바이트의 공간이 부팅 코드용으로 예약되어 비어 있게 됩니다.
마스터 부트 레코드의 전체 512바이트는 표에 표시된 대로 분류됩니다.

모든 확장 파티션은 확장 파티션 항목으로 예약된 공간에 존재해야 합니다. 확장 파티션은 두 개만 사용하도록 되어 있는데, 첫 번째는 일반 파티션으로, 두 번째는 다른 확장 파티션(있는 경우)으로 사용됩니다.
따라서 하나의 마스터 파티션 테이블을 사용하면 바로 옆에 다른 확장 마스터 파티션 테이블이 있는 경우 해당 테이블의 위치를 알아낼 수 있습니다.
파티션 테이블 항목 형식
다음 표는 MBR의 모든 파티션에 대한 파티션 테이블 항목 형식을 보여줍니다. 모든 MBR의 각 파티션 항목은 특정 의미를 갖는 다음 바이트로 구분될 수 있습니다.

MBR 파티션 테이블을 읽는 프로그램 작성
아래는 MBR 파티션 테이블에서 네 개의 파티션 항목을 모두 읽는 프로그램입니다. 이 프로그램은 MBR 파티션 테이블에 기록된 파티션 정보의 모든 매개변수를 표시합니다.
프로그램 인코딩은 다음과 같습니다.
/* MBR 파티션 테이블을 읽는 프로그램 */
# <bios.h>를 포함합니다.
/* 파티션 테이블에서 파티션 항목을 읽기 위한 구조 */
구조 단면
{
unsigned char 부팅 가능; /* 섹션의 활성 바이트 */
unsigned char start_side ;/* 시작 헤드 */
부호 없는 int 시작_초_실시간; /* 조합
시작 섹터 및
실린더 번호 */
unsigned char 부분 유형; /* 파일 시스템
표시기 바이트 */
부호 없는 char end_side; /* 헤더 끝 */
부호 없는 정수 end_sec_cyl ; /* 조합
시작 섹터 및
실린더 번호 */
부호 없는 긴 part_beg ; /* 상대 섹터
숫자 */
부호 없는 긴 정수; /* 섹션 길이
섹터 */
} ;
/* MBR 읽기 구조 */
구조의 일부
{
부호 없는 char master_boot[446] ; /* IPL(초기
프로그램 로더)*/
구조체 파티션 pt[4] ; /* 파티션 테이블 */
int 마지막 두 개 ; /* 마법의 숫자 */
} ;
구조 p의 일부;
잘못된 main()
{
영어: clrscr();
/* 첫 번째 하드 드라이브의 첫 번째 섹터 읽기 */
바이오스디스크(2, 0x80, 0, 0, 1, 1, &p);
표시하다(); /* MBR 정보 표시
파티션 테이블 */
얻다();
}
/* MBR 파티션 테이블에 대한 정보를 표시하는 함수 */
표시하다()
{
부호 없는 정수 s_sec, s_trk, e_sec, e_trk, i, t1, t2;
문자 유형[20], 부팅[5] ;
printf("\n\nPart. 초기 로딩 위치
최종 위치 상대 수량");
printf("\n타입 사이드 실린더 섹터
사이드 실린더 섹터 섹터 섹터\n");
i = 0 ; i <= 3 ; i++ 에 대하여
{
(p.pt[i].부팅 가능 == 0x80)
strcpy(부팅, "예");
또 다른
strcpy(부팅, "아니요");
스위치(p.pt[i].parttype)
{
케이스 0x00 :
strcpy(type, "사용되지 않음"); break;
케이스 0x1 :
strcpy(유형, "FAT12"); 중단;
케이스 0x2 :
strcpy(유형, "Xenix"); break;
케이스 0x3 :
strcpy(유형, "Xenix:usr"); 중단;
케이스 0x4 :
strcpy(유형, "FAT16<32M" ); break;
케이스 0x5 :
strcpy(type, "DOS-Ext."); break;
케이스 0x6 :
strcpy(유형, "FAT16>32M"); 중단;
케이스 0x7 :
strcpy(유형, "NTFS"); 중단;
케이스 0x0b :
strcpy(유형, "FAT32"); 중단;
케이스 0x0c :
strcpy(유형, "FAT32-LBA"); 중단;
케이스 0x0d :
strcpy(유형, "VFAT16"); 중단;
케이스 0x0e :
strcpy(유형, "VFAT16-LBA"); 중단;
케이스 0x0f :
strcpy(유형, "VFAT EXT"); break;
케이스 0x17 :
strcpy(유형, "HPFS"); 중단;
케이스 0x81 :
strcpy(type, "Old LINUX"); break;
케이스 0x82 :
strcpy(유형, "LinuxSwap"); break;
케이스 0x83 :
strcpy(유형, "LinuxNative"); 중단;
케이스 0x85 :
strcpy(type, "Linux Ext."); break;
기본 :
strcpy(유형, "알 수 없음"); break;
}
s_sec = ( p.pt[i].start_sec_cyl & 0x3f ) ; /* 시작
의 부문
파티션 */
t1 = ( p.pt[i].시작_초_실린더 & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].시작_초_실린더 & 0x00c0 ) << 2 ;
s_trk = t1 | t2 ; /* 시작 실린더 */
e_sec = ( p.pt[i].end_sec_cyl & 0x3f ) ; /*종료 섹터 */
t1 = ( p.pt[i].end_sec_cyl & 0xff00 ) >> 8 ;
t2 = ( p.pt[i].초당_시간_한계 & 0x00c0 ) << 2 ;
e_trk = t1 | t2 ; /* 실린더 종료 */
printf("\n%6s %3s", type, 부팅) ;
printf("%4d %6d %8d", p.pt[i].시작측면, s_trk,s_sec);
printf("%7d %6u %8u", p.pt[i].end_side, e_trk, e_sec);
printf ( " %10lu %10lu", p.pt[i].part_beg,
p.pt[i].plen ) ;
}
0을 반환합니다.
}
프로그램 출력에서 제공되는 정보는 아래와 비슷하게 표시됩니다.

코딩에 대한 의견:
구조 파티션은 MBR의 파티션 테이블에서 파티션 항목의 다양한 매개변수를 읽는 데 사용됩니다. 구조 부분은 MBR 정보를 읽는 데 사용됩니다.
display() 함수는 MBR 파티션 테이블 매개변수의 정보를 화면에 표시합니다. 프로그램의 출력을 보면 시작 및 종료 실린더와 섹터 번호가 다음과 같이 표시됩니다.
시작 섹터 = 1
시작 실린더 = 0
종료 섹터 = 63
종료 실린더 = 701
이러한 섹터 및 실린더 번호는 두 바이트의 조합에서 계산됩니다. 다음 표는 이러한 번호가 어떻게 계산되는지 보여줍니다.

따라서 파티션의 시작 CHS는 0-0-1입니다.
마찬가지로, 파티션의 종료 실린더와 섹터 번호에 대한 인코딩은 다음 표에 나와 있습니다.

따라서 분할의 마지막 CHS는 701-254-63입니다.
모든 논리 파티션과 해당 정보를 찾는 프로그램
우리가 이전에 논의한 프로그램은 MBR의 파티션 테이블에서 파티션 정보를 읽는 것이었습니다. 하지만 MBR만 읽는다고 해서 디스크의 확장 파티션에 있는 다른 논리적 파티션의 정보를 얻을 수는 없습니다.
우리는 이미 마스터 부트 레코드 가 마스터 파티션 테이블 에서 4개의 항목으로 제한된다는 것을 논의했습니다 . 그러나 확장 마스터 부트 레코드의 위치는 확장 파티션 테이블이 포함된 마스터 부트 레코드의 도움으로 얻을 수 있으며 , 그 형식은 주 파티션 테이블과 정확히 동일합니다.
모든 확장 파티션은 확장 파티션 항목에 예약된 공간 내에 존재해야 합니다. 확장 파티션 중 두 개만 사용하도록 되어 있으며, 첫 번째는 일반 파티션으로, 두 번째는 다른 확장 파티션(존재하는 경우)으로 사용됩니다.
따라서 하나의 마스터 파티션 테이블의 도움으로 바로 옆에 다른 확장 마스터 파티션 테이블이 있는 경우, 이 테이블의 위치를 알아낼 수 있습니다.
다음 프로그램은 모든 논리적 파티션과 해당 파티션 항목 정보를 찾고 디스크에서 MBR과 확장 MBR을 읽는 것 입니다. 프로그램의 코딩은 다음과 같습니다.
/* 디스크에 있는 모든 논리 파티션의 매개변수를 읽는 프로그램 */
#include<dos.h>
버퍼[512], report_par[20]이 있습니다.
부호 없는 드라이브 번호 = 0x80;
부호 없는 긴 star_sec[20], 초;
/* readabsolutesectors 함수에서 사용되는 디스크 주소 패킷 형식의 구조 */
구조체 디스크 애더 패킷
{
char packetsize ; /* 패킷 크기, 일반적으로 10H */
char reserved ; /* 예약됨 (0) */
int blockcount ; /* 전송할 블록 수 */
char far *bufferaddress ; /* 전송할 주소
버퍼 */
unsigned long blocknumber[2] ; /* 절대 시작
블록 번호 */
} ;
void 메인()
{
int 짝수가 아님,i;
영어: clrscr();
짝수없음 = 0;
모든_파티션_정보(star_sec,&no_par,&sec, 버퍼,
보고서_구문);
printf(" \n\n 디스크의 총 파티션 = %d\n ",
짝수없음);
i=0;i<no_par;i++에 대하여
{
printf("\n 파티션 %d의 시작 섹터 번호 =
%lu " , i+1, star_sec[i]);
}
printf("\n");
getch();
}
프로그램의 출력은 다음과 비슷하게 표시됩니다.
파티션 1 - FAT32
파티션 2 - FAT32
파티션 3 - FAT32
디스크의 총 파티션 = 3
파티션 1의 시작 섹터 번호 = 63
파티션 2의 시작 섹터 번호 = 11277693
파티션 3의 시작 섹터 번호 = 25623738
코딩에 대한 의견:
구조 diskaddrpacket은 readabsolutesectors 함수에서 사용되는 디스크 주소 패킷 형식을 읽는 데 사용됩니다.
All_partition_information( ) 함수는 파티션 항목에서 모든 파티션의 모든 매개변수를 찾는 데 사용됩니다.
이 프로그램에서는 디스크에 있는 모든 사용 가능한 논리 파티션의 파일 시스템과 상대적인 섹터 정보만 표시했지만, All_partition_information() 함수 와 printf를 함께 사용하면 파티션 정보의 다른 매개변수에 대한 정보도 출력할 수 있습니다.
함수의 코딩은 다음과 같습니다.
/* 파티션 항목을 읽어 모든 논리 파티션의 정보를 찾는 함수 */
모든_파티션_정보( unsigned long *star_sec,
서명되지 않은 *no_par,
long *sec, char *buffer,
부호 없는 char *report_par)
{
부호 없는 긴 fat_check;
부호 없는 long *sectors_part;
정적 긴 se_p;
int temp_var1, 활성 오프셋, 활성 위치=0, i, 확장 위치=0, partloc1;
부호 없는 long b_sec,se;
부호 없는 char 활성_파;
긴 상대 초;
긴 no_sectors;
if(*초==0 || *초==1)
se_p=0;
하다{
se=*초;
/ * *sec로 지정된 절대 섹터를 읽습니다. */
절대 섹터 읽기(드라이브 번호, *초, 1, 버퍼)
/* ***** 활성 파티션 확인 ***** */
if(*sec==se && *no_par==0) /*기본인 경우
파티션 */
{
*초=se=0;
활성 오프셋 = 446; 활성 오프셋 < = 494; 활성 오프셋 + = 16)
{
활성_파트=버퍼[활성_오프셋];
if(active_par==0x80) /* 활성 여부를 확인
파티션 */
부서지다;
또 다른
active_pos++; /* 활성 위치
파티션 */
}
/* 확장 파티션용 */
활성 오프셋 = 450; 활성 오프셋 < = 511; 활성 오프셋 + = 16)
{
활성_파트=버퍼[활성_오프셋];
if(활성_파라미터==0x05 | 활성_파라미터==0x0F)
/*확장 파티션 확인 */
부서지다;
또 다른
extended_pos++; /* 확장된 위치
파티션 */
}
활성_위치==4인 경우
활성_위치=1;
if(확장된_위치==4)
확장된 위치=1;
partloc1=0x1C0+확장_위치*16;
}
또 다른
{
활성_위치=0;
확장된 위치=1;
partloc1=0x1D0;
만약(se_p!=0)
{
*sec=se=se_p; /* 확장 시작
파티션 */
}
}
/* 파티션의 상대 섹터 */
상대 초 = *(unsigned long *)(버퍼 + 454 + 활성_포지션 * 16);
/* 파티션의 섹터 수 */
섹터 없음=*(long *)(버퍼+458+활성_위치*16);
/* 파일 시스템 표시기 바이트 식별 */
버퍼[0x1C2+활성_포지션*16]==0x04 ||
버퍼[0x1C2+활성_위치*16]==0x05 ||
버퍼[0x1C2+활성_위치*16]==0x06 ||
버퍼[0x1C2+활성_위치*16]==0x0B ||
버퍼[0x1C2+활성_위치*16]==0x0C ||
버퍼[0x1C2+활성_위치*16]==0x0E ||
버퍼[0x1C2+활성_위치*16]==0x0F ||
버퍼[0x1C2+활성_포지션*16]==0x07)
{
스위치(버퍼[0x1C2+활성_위치*16])
{
/* NTFS 파티션의 경우 */
사례 0x07: report_par[*no_par]='N';
printf("\n 파티션 -%d = NTFS",
*짝수+1이 아닙니다);
부서지다;
/* FAT32 파티션의 경우 */
케이스 0x0B:
사례 0x0C: report_par[*no_par]='3';
printf("\n 파티션 -%d = FAT32",
*짝수+1이 아닙니다);
부서지다;
/* FAT16 파티션의 경우 */
케이스 0x04:
케이스 0x06:
사례 0x0E: report_par[*no_par]='1';
printf("\n 파티션 -%d = FAT16",
*짝수+1이 아닙니다);
부서지다;
} // 스위치 종료
b_초=*초+상대초;
sectors_part[*no_par]=no_sectors; /* 파티션의 섹터 개수를 저장하는 배열 */
} // if 조건의 끝
또 다른
{ /* 파티션 표시기가 일치하지 않는 경우 */
if(*초==0)
{ no_par=0;
부서지다;
}
if((fat_check!=0x3631)&&(fat_check!=0x3233))
b_초=*초=0;
}
if((b_sec!=0)&&(초!=0))
{
별_초[*no_par]=b_초;
(*짝수 아님)++;
}
또 다른
부서지다;
/* 확장 파티션이 존재하는지 확인 */
버퍼[0x1C2+확장_위치*16]==0x05 ||
버퍼[0x1C2+확장_포지션*16]==0x0F )
{
temp_var1=(unsigned)버퍼[partloc1];
*sec=temp_var1 & 0x003F; /* 섹터
펼친
파티션 */
if(*초!=0)
{
se_p=se+상대초+섹터 없음;
*초=se_p;
}
또 다른
{ *초=-1;
부서지다;
}
} //if 문의 닫기
또 다른
{
(*초>0)이면
*초=-1;
부서지다;
}
} while(1); // do–while 루프 닫기
/* 섹터 0에서 다른 비활성 기본 파티션을 확인합니다. */
if(*초==0)
{
i=0;i<4;i++에 대하여
{
활성_파=버퍼[446+i*16];
/* 파일 시스템 표시기 바이트 식별 */
if((버퍼[0x1C2+i*16]==(문자)0x06 ||
버퍼[0x1C2+i*16]==(문자)0x0B ||
버퍼[0x1C2+i*16]==(문자)0x0C ||
버퍼[0x1C2+i*16]==(문자)0x07 ||
버퍼[0x1C2+i*16]==(문자)0x0E ||
버퍼[0x1C2+i*16]==(문자)0x04) && 활성_파!=0x80)
{
스위치(버퍼[0x1C2+활성_위치*16])
{
/* NTFS 파티션의 경우 */
사례 0x07: report_par[*no_par]='N';
printf("\n 파티션 -%d = NTFS",
*짝수+1이 아닙니다);
부서지다;
/* FAT32 파티션의 경우 */
케이스 0x0B:
사례 0x0C: report_par[*no_par]='3';
printf("\n 파티션 -%d = FAT32",
*짝수+1이 아닙니다);
부서지다;
/* FAT16 파티션의 경우 */
케이스 0x04:
케이스 0x06:
사례 0x0E: report_par[*no_par]='1';
printf("\n 파티션 -%d = FAT16",
*짝수+1이 아닙니다);
부서지다;
} // 스위치 종료
/* 상대 섹터 파티션 번호 */
상대 초=*(long *)(버퍼+454+i*16);
no_sectors=*(long *)(buffer+458+i*16); /* 개수
섹터들
분할*/
sectors_part[*no_par]=no_sectors; /* 저장할 배열
의 수
의 부문
파티션 */
*초=별초[*없음]=상대초;
(*짝수 아님)++;
}
} //for(i=0;i<4;i++) 루프 닫기
} // if(*sec==0) 루프 닫기
반품;
}
코딩에 대한 의견:
이 함수는 MBR에서 파티션 정보를 읽기 시작한 다음 필요한 경우 확장 MBR을 읽습니다 . readabsolutesectors 함수는 *sec로 지정된 절대 섹터를 읽습니다.
sectors_part[*no_par]는 파티션의 섹터 수를 저장하는 배열입니다. 파티션 번호는 0부터 시작하여 *no_par로 지정됩니다.
no_sectors는 파티션의 섹터 수이고 relative_sec은 해당 파티션의 상대적 섹터 번호입니다.
star_sec[*no_par]는 파티션의 시작 섹터 번호를 저장하는 배열입니다. 파티션 번호는 0부터 시작하여 *no_par로 지정됩니다.
star_cyl, star_hea 및 star_sec는 CHS를 기준으로 각 파티션의 시작 정보를 보관하는 배열입니다. star_cyl은 시작 실린더의 정보를 저장하고, star_hea는 시작 헤드의 정보를 저장하고, star_sec은 파티션의 시작 섹터의 정보를 저장합니다.
readabsolutesectors 함수에 대한 설명은 이 책의 앞부분에 나와 있는 장을 참조하세요.
프로그래밍으로 MBR 수정
MBR 파티션 테이블 항목 의 값을 수정하는 방법을 보여주는 샘플 프로그램이 아래에 나와 있습니다. 이 프로그램은 MBR 파티션 테이블의 두 번째 파티션 항목 값을 수정합니다 .
프로그램의 코딩은 아래와 같습니다.
/* MBR의 파티션 테이블 항목 값을 수정하는 프로그램 */
# <bios.h>를 포함합니다.
/* 파티션 테이블에서 파티션 항목을 읽는 구조 */
구조 파티션
{
unsigned char bootable ; /* 활성 파티션
바이트 */
unsigned char start_side ; /* 시작 헤드 */
unsigned int start_sec_cyl ; /* 조합
시작 섹터 및
실린더 번호 */
unsigned char parttype ; /* 파일 시스템
표시기 바이트 */
unsigned char end_side ; /* 끝 헤드 */
unsigned int end_sec_cyl ; /* 조합
시작 섹터 및
실린더 번호 */
unsigned long part_beg ; /* 상대 섹터
숫자 */
unsigned long plen ; /* 파티션 길이
섹터 */
} ;
/* MBR을 읽는 구조 */
구조 부분
{
unsigned char master_boot[446] ; /* IPL(초기)
프로그램 로더)*/
struct partition pt[4] ; /* 파티션 테이블*/
int lasttwo ; /* 매직 넘버 */
} ;
구조체 부분 p;
void 메인()
{
부호 없는 int t1,t2;
영어: clrscr();
바이오스디스크(2, 0x80, 0, 0, 1, 1, &p);
display(); /* 파티션을 표시합니다
테이블 정보 */
getch();
/* MBR의 파티션 테이블에서 두 번째 파티션 항목의 파티션 정보를 이러한 값으로 수정하고 싶다고 가정해 보겠습니다 . */
p.pt[1].bootable = 0x80; /* 활성 부트 파티션 */
p.pt[1].parttype = 0x7; /* NTFS 파티션 */
p.pt[1].start_side = 0; /* 시작 헤드 = 0 */
p.pt[1].끝_측면 = 31; /* 헤더 끝 == 31 */
p.pt[1].part_beg = 808416;/* 상대 섹터 = 808416 */
p.pt[1].전체 = 405216; /* 파티션의 총 섹터 수 = 405216 */
/* MBR에 새로운 정보를 씁니다 . *\
/* MBR 파티션 테이블에 값을 쓰려면 아래 biosdisk 함수의 주석 처리를 제거합니다 . */
// biosdisk(3, 0x80, 0, 0, 1, 1, &p);
표시하다(); /* 디스플레이가 변경되었습니다
정보 */
얻다();
}
코딩 주석:
위 프로그램은 MBR 파티션 테이블 항목 값을 변경하는 방법을 보여주는 예제 프로그램입니다 . 확장 파티션 에 있는 이러한 논리 파티션 의 파티션 항목 값을 변경하려면 확장 MBR 파티션 테이블 의 값을 변경해야 합니다 .
여기에 주어진 값을 사용해 파티션 테이블 항목을 변경하는 방법은 단지 변경 방법을 보여주기 위한 것입니다. 잘못되었거나 비논리적인 값으로 파티션 테이블을 수정하지 마십시오. 결과적으로 해당 섹션 전체에 접근이 불가능해질 수 있습니다.
구조 섹션은 파티션 테이블에서 파티션 레코드를 읽는 데 사용되고, 구조 부분은 MBR을 읽는 데 사용됩니다. 파티션 테이블을 변경하려면 biosdisk() 함수의 주석 처리를 제거합니다 .
파티션의 시작 및 종료 값, 섹터 및 실린더 번호를 변경하려면 이 장의 시작 부분에서 설명한 MBR 파티션 테이블을 읽고 표시하는 프로그램에 대한 주석에 따라 값을 계산합니다.