[19] 제목 : [초급-보충] 논리연산에 대해서...
올린이 : 까망벌레(정태식 ) 94/12/12 23:53 읽음 : 303 관련자료 없음
강좌가 늦어진 점에 대해 사과 드립니다.
제가 곧 군대를 가기 때문에 이런 저런 일 좀 처리하다 보니
2주일 정도 전혀 못들어 오게 됬었습니다.
그럼 계속 해서...
이번에는 논리 연산 명령에 대해 알아 보도록
하겠습니다.
아마 대부분 여러분들은 고등학교 과정에서 논리 연산에 대해 간략히 배워
보셨을 줄로 압니다.
즉 논리 연산에는 다음과 같은 명령들이 있는데...
AND
OR
NOT
XOR
위와 같은 네개가 컴퓨터에서 대표적으로 쓰이는 논리연산 명령어들 입니다.
그럼 우선 하나하나 자세하게 설명을 해 나가겠습니다.
학교에서 배울때는 'T,F'라는 것을 많이 사용 하였는데요, PC에서는 이 'T,F'
라는 것 대신에 프로세서가 알아 먹을 수 있는 내용인 숫자로 바꾸어 연산이
되어 집니다. 즉 프로세서가 알 수 있는 숫자는 2진수 밖에 없으므로 이
'T,F'를 2진수로 대치시켜 사용 하도록 합니다.
논리 연산에서는 'T,F'두개인 것과 같이 2진수 또한 '0,1'이라는 두개의 숫자만이
존재 합니다. 그럼 쉽죠?
T ( TRUE ) = 1
F ( FULSE ) = 0
위와 같이 정의를 하면 논리 연산도 식은죽 먹기...
첫째로 AND에 대해서...
우선 AND연산에 대한 진리표를 작성해 보도록 하겠습니다.
;-----------------;
; X ; Y ; X AND Y ;
;-----------------;
; F ; F ; F ;
; F ; T ; F ;
; T ; F ; F ;
; T ; T ; T ;
;-----------------;
위와 같은 진리표를 얻습니다.
( 헤..다 아신다구 해도 혹시나 까먹으셨을지도 모르시는 분들을 위해... )
AND 라는 것은 위에서 보면 알 수 있듯이 두개의 오퍼랜드 ( 피연산자 )를 필요로
하는 논리 연산 명령어 입니다. 위의 진리표에서는 두 개의 피 연산자가 바로
'X' 와 'Y'가 되는 것이겠죠. 이 AND라는 것은 두 개의 오퍼랜드 모두 참 값
( 'T'라는 값 ) 을 가져야만 결과값이 참( T )이 되는 것을 알 수 있습니다.
이제 기억이 조금씩 되살아 나실라나 모르겠네요.
그럼 이 진리표를 조금 바꾸어서 이를 2진수의 숫자 0 과 1 이라는 숫자로 바꾸어
다시 그려 보도록 하겠습니다.
;-----------------;
; X ; Y ; X AND Y ;
;-----------------;
; 0 ; 0 ; 0 ;
; 0 ; 1 ; 0 ;
; 1 ; 0 ; 0 ;
; 1 ; 1 ; 1 ;
;-----------------;
위와 같이 변환이 되어 집니다. 즉 전에 말씀 드렸다 시피
T 는 1
F 는 0
으로 각각 바꾸어 주기만 하면 됩니다.
그럼 이제 이 AND 연산 명령이 PC 상에서 어떻게 사용 되며 어떠한 처리를 하는지,
그리고 어느때 이러한 연산 명령이 필요한지 간단히 알아 보도록 하겠습니다.
이제까지 연산 처리 명령은 모두 1비트 크기의 연산 명령으로 제한된 예를 보였
습니다. 하지만 이 AND 연산이라는 것이 1비트 연산만을 하기 위한 것이라고
한다면... '푸~~~' 하고 한숨만 나오겠죠...
그럼 다음과 같은 것을 봅시다.
X = 00101101
Y = 10110110
( 이 숫자들의 값은 모두 2진수 입니다.
즉, X 의 값은 원래 45 이고 Y는 182 인데, 이 값들을 모두 2진수로 바꾸어
논리 연산을 하는데 알아보기 편하도록 했습니다. )
위와 같이 X,Y 의 값을 정의 하였다고 해 보죠.
그럼 학교 수학 시간에 배운 개념을 날려 버리고 새롭게 PC차원의 논리 연산으로
들어가서 위의 두 변수의 값들을 AND연산을 해 보도록 하겠습니다.
AND X,Y
앗! 위의 것은 무슨 뜻이냐고요?
원래의 의미는 'X AND Y'와 같은 의미를 지닙니다. 하지만 PC상에서 사용하는
문법을 따르기 위해 위와 같은 표기를 하게 된 것입니다.
즉 'X 값과 Y 두개의 값을 서로 AND 연산 시켜라'라는 명령어가 되는 것입니다.
그럼 결과 값은 어떤게 나오는지 살펴 보죠.
00101101 <-- X 값
10110110 <-- Y 값
AND )________
결과값
( 후후...이거 결과 값 찾는데 어지간히 뜸을 들이는군요.. )
위와 같이 자리수를 서로 맞추어 다시 써 보았습니다.
좀 더 편한 설명을 위해 각각의 자리수에 번호를 메겨 부르기 편하도록 해 보죠
위의 변수들의 값들은 모두 1바이트의 크기를 갖습니다. 즉 비트수로 따지면
8개의 비트수를 갖는 숫자라고 할 수 있습니다.
그럼 각각의 비트마다 번호 ( 순번 )을 매겨 보도록 하겠습니다.
;-----------------------------------------------;
; 0 ; 0 ; 1 ; 0 ; 1 ; 1 ; 0 ; 1 ; <--- X값
;-----------------------------------------------;
7번 6번 5번 4번 3번 2번 1번 0번 비트
위와 같이 매길 수 있습니다. ( 매길 수 있습니다가 아니고 사실 위와 같이
매기도록 되어 있습니다.)
조금 핵갈리실지도 모르지만 가장 죄측의 비트 번호가 '8'이 아닌 '7'이 된다는
사실에 주의를 해 주세요. 사실 비트수가 모두 8개 이어서 가장 높은 자리의 비트
번호가 '8'이어야 할 꺼 같지만 ( 1 부터 8 까지 ) PC 상에서는 이 '0' 이라는
숫자가 더 끼어들게 되어 가장 낮은 자리의 비트 번호는 '0'이 되고 가장 높은
자리의 비트수가 바로 '7'이 되어 집니다. ( 0 부터 7 까지 모두 8개의 비트 )
그럼 위의 연산 결과값을 하나하나 찾아 나가보도록 하겠습니다.
X,Y 두개의 변수에서 각각의 자리수를 이미 맞추어 놓았습니다. 즉, X의 7번 비트는
Y의 7번 비트, X의 5번 비트는 Y의 5번 비트....등등 각각의 비트자릿수를 맞추어
놓습니다.
그리고 이 맞추어진 것에 따라 하나씩 (1 비트씩) 따로따로 연산을 하여 결과값도
마찬가지로 각각 비트의 자릿수를 맞추어서 써 주면 됩니다.
알아 보기 쉽도록 표를 하나 그려 보도록 하겠습니다.
;--------------------------------;
; 비트 번호 ; X ; Y ; AND X,Y ;
;--------------------------------;
; 0 ; 1 ; 0 ; 0 ;
; 1 ; 0 ; 1 ; 0 ;
; 2 ; 1 ; 1 ; 1 ;
; 3 ; 1 ; 0 ; 0 ;
; 4 ; 0 ; 1 ; 0 ;
; 5 ; 1 ; 1 ; 1 ;
; 6 ; 0 ; 0 ; 0 ;
; 7 ; 0 ; 1 ; 0 ;
;--------------------------------;
위와 같이 됩니다. 각각의 비트번호 대로 1비트씩 따로 짤라내어 하나씩 연산을
한 결과를 얻게 되는 것이지요.
그래서 위와 같은 예의 결과 값은 바로
00100100
이 되는 것입니다.
그럼 이와 같은 것을 직접 어셈블리 명령어를 사용해서 프로그래밍을 해 보도록
하겠습니다.
X = 00101101
Y = 10110110
X AND Y = ??
'??'라는 값을 얻기위한 어셈블리 프로그래밍은,
MOV AX, 00101101B
MOV BX, 10110110B
AND AX, BX
간단하죠?
MOV 에 의해 AX에는 원래의 X값을, BX에는 원래의 Y값을 넣어주었습니다.
그리고 다음의 'AND'명령에 의해 AX와 BX의 값을 서로 AND 시켜 주었습니다.
앗! 근데 연산의 결과 값이 과연 어디에 들어가나요?
그건 역시나 'AX'레지스터에 들어가게 됩니다.
즉 'AND AX,BX'는 AX레지스터와 BX레지스터에 들어 있는 값들을 서로 논리연산
'AND'를 수행 한 후에 그 결과 값을 다시 AX레지스터에 넣어라! 라는 뜻이 됩니다.
만약 'AND BX,AX'라고 한다면 똥 같은 값이지만 그 결과 값을 AX레지스터가 아닌
BX레지스터에 넣게 되는 것이지요.
그럼 과연 이러한 연산이 어디에 쓰이는가.
간단한 한 예를 보도록 하겠습니다.
인터럽트 11(16진수 11임)을 호출할 때를 보도록 하겠습니다.
( 인터럽트에 관한 설명은 나중에 따로 하겠습니다. 여기서는 그냥 그려러니...하구
넘어 가세요 )
이 인터럽트를 호출하게 되면 프로세서는 지금 PC의 주변기기의 연결상태를
체크하여 그 정보를 넘겨 줍니다. 그 정보는 AX레지스터로 넘겨 주게 됩니다.
사실 연결장치의 정보는 한가지가 아닌 여러가지의 연결장치들의 상태를
넘겨주게 되는데, AX레지스터에 이 모든 정보들이 다아 담겨져 있습니다.
플로피 드라이브의 설치 유무
코프로 세서의 유무
16K RAM 블럭의 수
비디오 모드
플로피 드라이브 갯수
DMA 칩(CHIP)의 사용
RS - 232 카드의 수
게임 포트 수
직렬 프린터의 설치
모뎀 설치
설치된 프린터의 수
위와 같은 11개에 대한 정보가 하나의 레지스터인 AX레지스터에 모두 담겨져 나오게
됩니다. 그럼 과연 그것이 어떻게 가능 할까요? 그것은 바로 위와 같은 정보들이
따로따로 각각의 지정된 고유의 비트들을 차지하여 정보를 전달하게 됩니다.
비트 번호
0 플로피 드라이브의 설치 유무
1 코프로 세서의 유무
2 - 3 16K RAM 블럭의 수
4 - 5 비디오 모드
6 - 7 플로피 드라이브 갯수
8 DMA 칩(CHIP)의 사용
9 - 11 RS - 232 카드의 수
12 게임 포트 수
13 직렬 프린터의 설치
13 모뎀 설치
14 - 15 설치된 프린터의 수
위와 같이 각각의 비트들에 그 주변 기기들의 상태들에 대한 정보를 넣게 됩니다.
가령 인터럽트 11번을 사용하여 얻어낸 AX레지스터의 값이 다음과 같다고 해 보죠.
0110000010101111 ( 2진수 입니다. 16진수로는 60AF 가 됩니다. )
이걸 좀 더 알아보기 편하도록 박스를 그려 구분해 보도록 하겠습니다.
;---------------------------------------------------------------;
; 0 ; 1 ; 1 ; 0 ; 0 ; 0 ; 0 ; 0 ; 1 ; 0 ; 1 ; 0 ; 1 ; 1 ; 1 ; 1 ;
;---------------------------------------------------------------;
F E D C B A 9 8 7 6 5 4 3 2 1 0 <- 비트 번호
( 위의 비트 번호에서 영문자는 16진수 표기를 따랐기 때문 입니다. )
위와 같이 정리 될 수 있습니다.
그럼 이제 이 값들에서 필요한 정보를 알아 내도록 해 보겠습니다.
;--------------------------------------------------------------;
;비트번호; 내 용 ; 값 ; 결과의 내용 ;
;--------------------------------------------------------------;
; 0 ;플로피 드라이브의 설치 유무 ; 1 ; 있음 ;
; 1 ;코프로 세서의 유무 ; 1 ; 있음 ;
; 2 - 3 ;16K RAM 블럭의 수 ; 11 ; 3개 ;
; 4 - 5 ;비디오 모드 ; 10 ; 80X25 COLOR MODE ;
; 6 - 7 ;플로피 드라이브 갯수 ; 10 ; 2개 ;
; 8 ;DMA 칩(CHIP)의 사용 ; 0 ; 없음 ;
; 9 - B ;RS - 232 카드의 수 ; 0 ; 없음 ;
; C ;게임 포트 수 ; 0 ; 없음 ;
; D ;직렬 프린터의 설치 ; 1 ; 있음 ;
; D ;모뎀 설치 ; 1 ; 있음 ;
; E - F ;설치된 프린터의 수 ; 01 ; 1개 ;
;--------------------------------------------------------------;
이와 같습니다.
즉, 비트 단위로 그 결과를 알아 낼 수 있는 것입니다.
하지만 여기서 만약 프로피 드라이브의 갯수를 알고 싶은데, 과연 어떻게
이리저리 섞여 있는 정보들 중에 원하는 정보를 따로 분리해 뽑아 낼수 있을까요.
다시 말해 AX레지스터는 전부 16개의 비트로 구성이 되어 있는데 이 중에서
우리가 원하는 비트 번호인 6번 부터 7번 까지의 비트안에 있는 내용을 뽑아 올 수
있겠습니까?
여기서 바로 AND라는 연산 명령이 사용 됩니다.
다음과 같이 해 보도록 하죠.
AND AX, 0000000011000000B
그럼 원래의 AX레지스터에는 0110000010101111 이라는 2진수 값이 들어 있었습니다.
0110000010101111 <-- 인터럽트 호출로 얻은 정보 값
0000000011000000
AND )________________
결과 값
이와 같이 하게 되면 결과 값은
0000000010000000
이 되는 것을 쉽게 알 수 있습니다. 이것은 바로 플로피 드라이브의 갯수를
담고있는 비트 번호만이 그대로 남아 있고 나머지는 모두 0으로 세트 되는 현상을
가져 오게 되는 것이지요. 이렇게 따로 비트의 내용을 뽑아 올때 AND연산을 많이
사용하게 됩니다.
둘째로 OR에 대해 알아 보도록 하겠습니다.
;-----------------;
; X ; Y ; X OR Y ;
;-----------------;
; 0 ; 0 ; 0 ;
; 0 ; 1 ; 1 ;
; 1 ; 0 ; 1 ;
; 1 ; 1 ; 1 ;
;-----------------;
.
진리 표는 위와 같습니다.
간단히 연산 결과만을 보여 드리고 다음으로 넘어 가겠습니다.
X = 01001011
Y = 10011010
X OR Y = 11011011
위와 같이 됩니다. 즉 두개의 오퍼랜드 중 하나라도 '1'이 라는 값이 있으면
그 결과는 1이 되는 것입니다. ( 물론 1비트씩 따로 연산이 되어 집니다. )
이 OR이라는 명령어는 어떨때 쓰이느냐...하믄
바로 비트 중에 나머지 부분은 그냥 놔 두고 원하는 부분만을 '1'로 세트하고
싶을 때에 사용하게 됩니다.
;---------------------------------------------------------------;
; 0 ; 1 ; 1 ; 0 ; 0 ; 0 ; 0 ; 0 ; 1 ; 0 ; 1 ; 0 ; 1 ; 1 ; 1 ; 1 ;
;---------------------------------------------------------------;
F E D C B A 9 8 7 6 5 4 3 2 1 0 <- 비트 번호
위의 예에서 만약 비트 번호 9번이랑 8번을 모두 1로 세트하고 싶다면,
OR AX, 0000001100000000B
이와 같이 하면 비트 번호 9번이랑 8번은 모두 1로 세트가 되고 나머지는 변화없이
그대로 남게 됩니다.
다음은 NOT 인가요?
이건 너무 간단해서 ....
부정 한다는 뜻입니다.
단 이것은 오퍼랜드가 하나밖에 올 수 없다는 것입니다.
생각해 보면 당연한 얘기지만 말이죠...
X = 01001101
NOT X = 10110010
각 비트들의 값이 모두 거꾸로 전환이 된 것을 알 수 있습니다.
즉, '0'은 '1'로
'1'은 '0'으로
각각 변환이 된 것을 알 수 있습니다.
이렇게 NOT는 각각의 비트별로 그 값을 '0'이면 '1', '1'이면 '0' 으로 바꾸는
기능을 하여 줍니다.
이제 XOR로 넘어오죠..
이 XOR이라는 것은 EXCLUSIVE - OR 뜻으로 다음 진리표를 보면,
;-----------------;
; X ; Y ; X XOR Y ;
;-----------------;
; 0 ; 0 ; 0 ;
; 0 ; 1 ; 1 ;
; 1 ; 0 ; 1 ;
; 1 ; 1 ; 0 ;
;-----------------;
이와 같은 진리표가 됩니다.
또 예를 보도록 하죠.
X = 01101101
Y = 10111001
X XOR Y = 11010100
즉 다시 말해 두 개의 오퍼랜드를 비교 하여 각 비트 별로 그 값이 같으면 ( 모두
'1' 또는 '0' 이면 ) 결과 값은 0이 되고, 두 비교 대상의 값이 서로 다른 값이면
( '0'과 '1', 또는 '1'과 '0' 이면 ) 그 결과값은 1이 되는 것입니다.
이것은 생각보다 꽤 많이 사용되어 지는 연산 명령어 입니다.
그 이유는 어떠한 레지스터의 값을 깨끝이 ( 값을 0으로 해 주는것 ) 하고자 할 때
일단은 다음과 같은 방법이 있음을 알 수 있습니다.
1, MOV AX, 00 <-- AX 에 0 을 대입
2, SUB AX, AX <-- AX값에 AX값을 빼어 AX에 넣어줌 ( AX - AX = 0 )
3, AND AX, 00 <-- AND연산으로 AX레지스터의 값을 0으로 만들어 줌
4, OR AX,0FFH <-- AX 값을 모두 1로 세트한 다음 ( 비트단위 )
NOT AX 이것의 부정(NOT)을 취하여 0으로 세트
5, XOR AX, AX <-- AX 값을 AX와 XOR연산 시킴
일단은 위의 다섯 가지를 보도록 하죠.
이 다섯 가지 경우 모두 AX레지스터의 값을 0으로 세트하여 주는 어셈블리
명령어들 입니다.
사실 이 다섯 가지 중, 어떤것을 사용 해도 별 상관은 없습니다.
하지만 가장 많이 사용 되고, 또한 가장 권장할 만한 방법은 바로 다섯번째의 경우인
XOR AX, AX
입니다. 그 이유는 바로 어셈블리 프로그래밍을 하는 가장 중요한 이유중의 하나인
속도 차원의 문제 입니다.
대충 비교를 하자면, 덧셈이나 뺄셈 명령은 곱셈이나 나눗셈 명령보단 훨씬 속도가
빠릅니다.
그리고 여기에 논리 연산명령은 덧셈, 뺄셈 명령보다, 그리고 이동 명령인 'MOV'보다
훨씬 빠른 속도를 나타냅니다.
물론 쉬프트 명령이라는 것 또한 못지 않게 빠른 속도를 나타냅니다만, 이 것은
아직 설명을 하지 않았기 때문에 넘어 가기로 하고...
그런 속도 문제 때문에 XOR은 어떠한 레지스터의 값을 0으로 세팅 하는데 아주
많이 사용 됩니다.
[23] 제목 : 디버그(DEBUG.EXE) 정리 -전편-
올린이 : 영원의별(이세원 ) 95/01/10 18:58 읽음 : 455 관련자료 없음
안녕하세요.
디버그에관해 자세히 그리고 분명히 언급된 책이 별로 없더군요.
그래서 한번 꾸며보았습니다.
너무 길어서 둘로 나누어 올립니다.
많은 도움이 되시길...
디버그(DEBUG.EXE)의 사용법.
디버그라는 뜻은 프로그램의 버그 즉 오류를 찾아낸다는 뜻이다.프로그
램을 완성하고 실행시키면 예상치도 않은 버그가 발생하는수가 간혹있다. 이런
경우에 프로그램을 부분적으로 실행해보고 오류를 찾아내야 하는데 그런 작용을
전문적으로 해주는 프로그래머에게는 필수의 도구가 바로 도스의 디버그(DEBUG.E
XE) 이다. 정확한 명칭은 디버거(DEBUGER) 라고 해야하나 도스의 화일로서 제공
되는 이름은 이름은 디버그이다.
그러나 지금은 프로그램의 덩치가 워낙 커지고 또한 고급언어로 작성되
는지라 프로그램의 정확한 흐름을 아는것은 대단히 어렵다. 프로그래머 조차 라
이브러리의 내용을 모른다는 이야기다. 따라서 고급언어로 만들어진 프로그램을
디버거로 오류를 찾아내는것은 어렵다. 그런 이유로 고급언어에서는 통합환경 내
에서 자체적으로 오류를 찾도록해주는 자체 디버거를 가지고 있는 형편이다.
이 디버거는 예전의 어셈블리어로 프로그램을 작성하던때에 진가를 발휘
하던 프로그램이었으나 지금은 그런 목적보다도 컴퓨터의 시스템을 직접 조작하
는데 많이 쓰인다.
디버거를 기동하고 디버거 프롬프트 상에서 '?' 를 입력하면 다음과 같
은 도움말이 출력된다.
+-------------------------------------------------------------------------+
| -? |
| assemble A [address] |
| compare C range address |
| dump D [range] |
| enter E address [list] |
| fill F range list |
| go G [=address] [addresses] |
| hex H value1 value2 |
| input I port |
| load L [address] [drive] [firstsector] [number] |
| move M range address |
| name N [pathname] [arglist] |
| output O port byte |
| proceed P [=address] [number] |
| quit Q |
| register R [register] |
| search S range list |
| trace T [=address] [value] |
| unassemble U [range] |
| write W [address] [drive] [firstsector] [number] |
| allocate expanded memory XA [#pages] |
| deallocate expanded memory XD [handle] |
| map expanded memory pages XM [Lpage] [Ppage] [handle] |
| display expanded memory status XS |
| - |
+-------------------------------------------------------------------------+
1. A (어셈블)
A 명령은 어셈블하여 메모리에 직접 기계어 코드를 만들다. 다만 1행마다 어셈블
하는 기능이므로 레이블을 사용할 수는 없다.
형식) A [어드레스]
어드레스는 메모리내의 오프셋번지 또는 세그먼트와 오프셋의 번지이다.
세그먼트번지를 생략하면 현재의 CS 레지스터가 세그먼트값이되고 IP레지스터의
값이 오프셋 번지가 된다.
-A 100
154D:0100 MOV AH,4C
154D:0102 INT 21
154D:0104
.
.
2. C (비교)
C 명령은 메모리의 내용을 비교하고 달라져 있는 부분을 표시한다.
세그먼트가 생략된 경우에는 DS 세그먼트의 값으로 정해진다.
형식) C [개시어드레스 종료어드레스]
C [개시어드레스] L [길이]
1234:0000 ∼ 1234:000F 까지를 현재의 DS:0010 부터 시작해서
비교한다.
-c 1234:0 f 10
1234:0000 ED B1 154D:0010
1234:0001 75 0F 154D:0011
1234:0002 05 17 154D:0012
1234:0003 E8 03 154D:0013
1234:0004 26 B1 154D:0014
1234:0005 00 0F 154D:0015
1234:0006 EB FA 154D:0016
1234:0007 03 0E 154D:0017
1234:0008 E8 01 154D:0018
1234:0009 B0 01 154D:0019
1234:000B 72 00 154D:001B
1234:000C 08 02 154D:001C
1234:000D 5A FF 154D:001D
1234:000E E8 FF 154D:001E
1234:000F 05 FF 154D:001F
-
1234:0000 부터 10H개 만큼 현재의 DS:0010 부터 비교한다.
이것은 결과적으로 위와 같은 내용을 출력한다.
-c 1234:0 l10 10
1234:0000 ED B1 154D:0010
1234:0001 75 0F 154D:0011
1234:0002 05 17 154D:0012
1234:0003 E8 03 154D:0013
1234:0004 26 B1 154D:0014
1234:0005 00 0F 154D:0015
1234:0006 EB FA 154D:0016
1234:0007 03 0E 154D:0017
1234:0008 E8 01 154D:0018
1234:0009 B0 01 154D:0019
1234:000B 72 00 154D:001B
1234:000C 08 02 154D:001C
1234:000D 5A FF 154D:001D
1234:000E E8 FF 154D:001E
1234:000F 05 FF 154D:001F
-
3. D (덤프)
D 명령은 메모리의 내용을 16진수와 아스키문자로 나타낸다.
디폴트 세그먼트는 DS세그먼트이고 한번에 128바이트를 출력한다. 또한 덤프후에
또다시 D 명령을 치면 연속해서 메모리의 내용을 출력한다.
덤프명령은 아스키 문자도 출력하는데 아스키 128번 이후의 문자는 출력하지 못
하고 '.' 으로 표현한다. 그런 이유로 한글은 출력해도 알아볼수가 없다.
형식) D
D [어드레스]
-D
154D:0100 0F 00 B9 8A FF F3 AE 47-61 03 1F 8B C3 48 12 B1 .......Ga....H..
154D:0110 04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15 ........H.+.4.<.
154D:0120 00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16 ................
154D:0130 C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A ..........N...F.
154D:0140 C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2 .$.<.u........<.
154D:0150 75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E umm...P.t..2....
154D:0160 8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00 ....3.)......i..
154D:0170 0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94 FA .....t.&........
오프셋번지 FFH 이후의 메모리의 내용을 출력한다.
-D FF
154D:00F0 00 .
154D:0100 0F 00 B9 8A FF F3 AE 47-61 03 1F 8B C3 48 12 B1 .......Ga....H..
154D:0110 04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15 ........H.+.4.<.
154D:0120 00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16 ................
154D:0130 C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A ..........N...F.
154D:0140 C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2 .$.<.u........<.
154D:0150 75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E umm...P.t..2....
154D:0160 8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00 ....3.)......i..
154D:0170 0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94 .....t.&.......
특정 메모리번지인 1100:0000 의 메모리의 내용을 출력한다.(세그먼트 값과
오프셋 값을 설정한다.)
-D 1100:0
1100:0000 C3 C6 06 22 4A 00 E8 D0-FF 33 DB BF 07 57 E8 E8 ..."J....3...W..
1100:0010 EE 74 1F 8B 2E 1C 4A 57-53 E8 B3 F2 5B 5F 89 15 .t....JWS...[_..
1100:0020 89 45 02 83 C7 05 43 83-FB 0B 75 E2 BA F4 4B E9 .E....C...u...K.
1100:0030 D8 F5 89 1E AC 56 8B CB-E3 12 BF 07 57 1E 26 C5 .....V......W.&.
1100:0040 35 83 C7 04 A4 C6 44 FF-CC E2 F3 1F 8B 16 8E 4A 5.....D........J
1100:0050 89 16 1C 4A 8B 16 90 4A-89 16 1E 4A C7 06 AE 56 ...J...J...J...V
1100:0060 01 00 E9 EC FD B4 37 32-C0 CD 21 2E 88 16 75 56 ......72..!...uV
1100:0070 AC E8 C4 F6 74 05 E8 D2-F6 75 F5 4E C3 E8 08 F0 ....t....u.N....
-
4. E (엔터)
메모리의 내용을 세트한다.
이는 새로이 특정 메모리의 내용을 설정하는것을 말한다.
형식) E[개시어드레스 리스트]
-e B800:0100 ----- ①
B800:0100 20.31 ----- ②
① EB800:0100 의 내용을 출력시킨다.
② 20H 의 값을 '31' 로 바꾼다.
이명령은 또한 메모리에 문자열의 입력도 가능하다.
EB800:100 의 번지에 문자열을 입력한다. 중간중간의 '7' 은 문자의 속성이다.
결과적으로 화면 우측 상단에 'I AM A BOY' 라고 출력된다.
-e B800:100 'I' 7 ' ' 7 'A' 7 'M' 7 ' ' 7 'A' 7 ' ' 7 'B' 7 'O' 7 'Y'
- I AM A BOY
5. F (채움)
F 명령은 메모리를 지정한 데이타로 채운다.
형식) F[어드레스 리스트]
-f b800:0 ff 31 ----- ①
-f b800:0 ff 31,32,33 ----- ②
-f b800:0 ff 'i am a boy' ----- ③
-
① B800:0000 ∼ B800:00FF 까지 31H 로 채운다.
② B800:0000 ∼ B800:00FF 까지 31H,32H,33H 로 채운다.
③ B800:0000 ∼ B800:00FF 까지 'i am a boy' 로 채운다.
또한 이 명령을 이용해서 메모리의 특정 부분의 데이타를 소거하는데 쓸수있다.
-fb800:0 ffff 0 ----- B800:0000 ∼ B800:FFFF 까지를 0H 로 채운다.
6. G (실행개시)
G 명령은 메모리에 로드된 프로그램의 실행을 개시한다.
형식) G ---- ①
G [=시작점 중지점] ---- ②
① 현재의 CS:IP 로부터 프로그램을 시작한다.
② 시작번지에서 중지번지 바로 앞번지까지 프로그램이 진행된다.
C:\ASM>debug small.com
-g
Ex) small file1 file2
Program terminated normally
-
C:\ASM>debug small.com
-g=100 215
Ex) small file1 file2
AX=0924 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0215 NV UP EI PL ZR NA PE NC
155E:0215 B44C MOV AH,4C
-g
Program terminated normally
-
다음은 디버거로 웜부팅을 해보는 재미있는 예이다.
부트 스트랩 로더를 기동하는 프로그램을 CS:100H 로 복사하고 G 명령으로
실행 시켜본다.
C:\ASM>debug
-m ffff:0 f 100
-d 100
154D:0100 EA F4 04 A6 02 30 34 2F-33 30 2F 39 30 00 FC 00 .....04/30/90...
154D:0110 04 8B C6 F7 0A 0A D0 D3-48 DA 2B D0 34 00 3C 15 ........H.+.4.<.
154D:0120 00 DB D2 D3 E0 03 F0 8E-DA 8B C7 16 C2 B6 01 16 ................
154D:0130 C0 16 F8 8E C2 AC 8A D0-00 00 4E AD 8B C8 46 8A ..........N...F.
154D:0140 C2 24 FE 3C B0 75 05 AC-F3 AA A0 0A EB 06 3C B2 .$.<.u........<.
154D:0150 75 6D 6D 13 A8 01 50 14-74 B1 BE 32 01 8D 8B 1E umm...P.t..2....
154D:0160 8E FC 12 A8 33 D2 29 E3-13 8B C2 03 C3 69 02 00 ....3.)......i..
154D:0170 0B F8 83 FF FF 74 11 26-01 1D E2 F3 81 00 94 FA .....t.&........
-g
7. H (합과차)
H 명령은 16진수끼리의 합과 차를 계산하여 표시해준다.
좌측의 값이 합이고 우측의 값이 차이다.
형식) H[값1 값2]
-H 12 11
0023 0001
-H FF00 EF12
EE12 0FEE
-H 23 12
0035 0011
-
8. I (인풋)
I 명령은 I/O PORT 에서 1 바이트의 데이타를 입력하여 표시한다.
8086의 포트 어드레스는 0H ∼ 0FFFFH 의 범위에 있기때문에 포트 어드레스의
지정도 16비트로 한다.
형식) I[포트어드레스]
9. L (로드)
L 명령은 화일이나 디스크의 내용을 메모리에 로드한다.
형식) L [어드레스] ---- ①
L [어드레스 드라이브 레코드번호 섹터수] ---- ②
① 화일을 로드할때 쓰인다. 단 FCB 에 세트된 이름이 로드 되므로 사전에
N 명령을 하여야한다.
어드레스를 생략하면 CS:100H 에서 로드된다.
② 디스크의 특정 섹터를 로드할때 쓰인다.
+--------------------+-------------------------------------------+
| 어드레스 | 로드 시키는 메모리의 번지 |
+--------------------+-------------------------------------------+
| 드라이브 | 0=A,1=B,2=C,3=D 드라이브 |
+--------------------+-------------------------------------------+
| 레코드번호 | 읽는 디스크의 레코드 번호 |
+--------------------+-------------------------------------------+
| 섹터수 | 읽는 디스크의 섹터수 |
+--------------------+-------------------------------------------+
다음은 디버거 내에서 'small.com' 을 로드하는 예이다.
C:\ASM>debug
-n small.com
-l
-d
155E:0100 A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61 ...<.u"..Ex) sma
155E:0110 6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4 ll file1 file2$.
155E:0120 09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49 .....!.........I
155E:0130 49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0 I.......< t.....
155E:0140 00 AA BF D5 02 F3 A4 B0-00 AA B4 3D BA AC 02 B0 ...........=....
155E:0150 00 CD 21 72 5E A3 A8 02-B4 3D BA D5 02 B0 00 CD ..!r^....=......
155E:0160 21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69 !r...Already exi
155E:0170 73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C st.$...e..!....<
-
다음은 하드 디스크(C 드라이브) 의 부트 영역을 로드한 예이다.
C:\ASM>debug
-l 100 2 0 1
-d
154D:0100 EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 08 01 00 .<.MSDOS5.0.....
154D:0110 02 00 02 00 00 F8 A2 00-2E 00 08 00 2E 00 00 00 ................
154D:0120 72 10 05 00 80 00 29 79-59 E8 1C 20 20 20 20 20 r.....)yY..
154D:0130 20 20 20 20 20 20 46 41-54 31 36 20 20 20 FA 33 FAT16 .3
154D:0140 C0 8E D0 BC 00 7C 16 07-BB 78 00 36 C5 37 1E 56 .....|...x.6.7.V
154D:0150 16 53 BF 3E 7C B9 0B 00-FC F3 A4 06 1F C6 45 FE .S.>|.........E.
154D:0160 0F 8B 0E 18 7C 88 4D F9-89 47 02 C7 07 3E 7C FB ....|.M..G...>|.
154D:0170 CD 13 72 79 33 C0 39 06-13 7C 74 08 8B 0E 13 7C ..ry3.9..|t....|
-
10. M (무브)
M 명령은 메모리의 내용을 다른 위치에 복사한다.
형식) M [범위 번지]
다은은 'small.com' 의 내용중에 'Ex) small file1 file2' 의 부분을
다른 번지로 복사시키는 예이다.
C:\ASM>debug small.com
-d
155E:0100 A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61 ...<.u"..Ex) sma
155E:0110 6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4 ll file1 file2$.
155E:0120 09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49 .....!.........I
155E:0130 49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0 I.......< t.....
155E:0140 00 AA BF D5 02 F3 A4 B0-00 AA B4 3D BA AC 02 B0 ...........=....
155E:0150 00 CD 21 72 5E A3 A8 02-B4 3D BA D5 02 B0 00 CD ..!r^....=......
155E:0160 21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69 !r...Already exi
155E:0170 73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C st.$...e..!....<
-m 109 11d 140
-d 100
155E:0100 A0 80 00 3C 00 75 22 EB-16 45 78 29 20 73 6D 61 ...<.u"..Ex) sma
155E:0110 6C 6C 20 66 69 6C 65 31-20 66 69 6C 65 32 24 B4 ll file1 file2$.
155E:0120 09 BA 09 01 CD 21 E9 EC-00 8A 0E 80 00 B5 00 49 .....!.........I
155E:0130 49 BE 82 00 BF AC 02 AC-3C 20 74 03 AA E2 F8 B0 I.......< t.....
155E:0140 45 78 29 20 73 6D 61 6C-6C 20 66 69 6C 65 31 20 Ex) small file1
155E:0150 66 69 6C 65 32 A3 A8 02-B4 3D BA D5 02 B0 00 CD file2....=......
155E:0160 21 72 1B EB 0F 41 6C 72-65 61 64 79 20 65 78 69 !r...Already exi
155E:0170 73 74 2E 24 B4 09 BA 65-01 CD 21 E9 97 00 B4 3C st.$...e..!....<
-
[25] 제목 : 디버그(DEBUG.EXE) 정리 -후편-
올린이 : 영원의별(이세원 ) 95/01/10 20:33 읽음 : 293 관련자료 없음
11. N (네임)
N 명령은 실행파일의 이름을 수정하거나 파라메터를 지정한다.
결과로 FCB가 세트된다.
+----------------------------------------------------------+
| FCB |
+----------------------------------------------------------+
| CS:5C 첫 번째 파일을 위한 파일 제어블럭 |
| CS:6C 두 번째 파일을 위한 파일 제어블럭 |
| CS:80 파라메터 길이 |
| CS:81 파라메터 시작 위치 |
+----------------------------------------------------------+
형식) N [패스네임 파라메터]
다음은 디버거 내에서 'small.asm' 을 어셈블하고 링크하는 과정이다.
C:\ASM>debug
-n masm.exe
-l
-n small;
-g
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.
51448 + 383976 Bytes symbol space free
0 Warning Errors
0 Severe Errors
Program terminated normally
-n link.exe
-l
-n small;
-g
Microsoft (R) Overlay Linker Version 3.60
Copyright (C) Microsoft Corp 1983-1987. All rights reserved.
LINK : warning L4021: no stack segment
Program terminated normally
-n c:\dos\exe2bin.exe
-l
-n small.exe
-g
Program terminated normally
-
12. O (아웃풋)
O 명령은 I/O 포트에 데이타를 출력한다.
형식) O [포트어드레스 바이트데이타]
13. P (진행)
P 명령은 프로그램을 로드한 상태에서 실제로 프로그램을 한명령씩 실행 시키
는 것이다. 따라서 오히려 T 명령보다 정확하게 프로그램을 추적할수 있다.
형식) P ---- ①
P [=어드레스] ---- ②
P [=어드레스,넘버] ---- ③
P [넘버] ---- ④
① 현재의 CS:IP 에서 실행 시킨다.
② IP 의 값을 지정해준 번지에서부터 실행한다.
③ IP 의 값과 실행시킬 명령의 갯수만큼 실행한다.
④ 실행시킬 명령의 갯수만큼 실행한다.
C:\ASM>debug small.com
-p
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0103 NV UP EI PL NZ NA PO NC
155E:0103 3C00 CMP AL,00
-p=110
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0002
DS=155E ES=155E SS=155E CS=155E IP=0115 NV UP EI PL ZR NA PE NC
155E:0115 6C DB 6C
-p=100
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0103 NV UP EI PL ZR NA PE NC
155E:0103 3C00 CMP AL,00
-
-p=110,4
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0002
DS=155E ES=155E SS=155E CS=155E IP=0115 NV UP EI PL ZR NA PE NC
155E:0115 6C DB 6C
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0119 NV UP EI PL ZR NA PE NC
155E:0119 66 DB 66
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0121 OV UP EI PL NZ NA PE CY
155E:0121 BA0901 MOV DX,0109
AX=0000 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0124 OV UP EI PL NZ NA PE CY
155E:0124 CD21 INT 21
-
-p4
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0105 NV UP EI PL ZR NA PE NC
155E:0105 7522 JNZ 0129
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0107 NV UP EI PL ZR NA PE NC
155E:0107 EB16 JMP 011F
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=011F NV UP EI PL ZR NA PE NC
155E:011F B409 MOV AH,09
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=C640 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0121 NV UP EI PL ZR NA PE NC
155E:0121 BA0901 MOV DX,0109
-
14. Q (종료)
Q 명령은 디버거를 종료하고 도스로 빠져나간다.
Ctrl-C 또는 Ctrl-Break 으로는 빠져나갈 수 없다.
형식) Q
15. R (레지스터)
R 명령은 현재의 레지스터의 내용을 보여준다.
형식) R ---- ①
R [레지스터] ---- ②
R [F] ---- ③
① 단지 레지스터의 내용을 나타낸다.
② 레지스터의 내용을 나타내고 새로이 설정할 수 있다.
레지스터는 AX,BX,CX,DX,SP,BP,SI,DI,DS,ES,SS,CS,IP,이다.
③ F 는 프래그 레지스터이다.
프래그
治뵀痼 각 프래그가 나타내는것은 다음과 같다.
만약 프래그를 변경하려면 새로이 코드를 입력한다.
+----------------------------+----------------+---------------+
| Flag name | 설정 | 해제 |
+----------------------------+----------------+---------------+
| Overflow | OV | NV |
| Drection | DN | UP |
| Interrupt | EL | DL |
| Sign | NG | PL |
| Zero | ZR | NZ |
| Auxiliary Carry | AC | NA |
| Parity | PE | PO |
| Carry | CY | NC |
+----------------------------+----------------+---------------+
C:\ASM>debug small.com
-r
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0100 NV UP EI PL NZ NA PO NC
155E:0100 A08000 MOV AL,[0080] DS:0080=00
-r ax ---- ①
AX 0000 ---- ②
:100 ---- ③
-r
AX=0100 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0100 NV UP EI PL NZ NA PO NC
155E:0100 A08000 MOV AL,[0080] DS:0080=00
-r f ---- ④
NV UP EI PL NZ NA PO NC -ov cy ---- ⑤
-r f
OV UP EI PL NZ NA PO CY -
-r
AX=0100 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0100 OV UP EI PL NZ NA PO CY
155E:0100 A08000 MOV AL,[0080] DS:0080=00
-
① AX 레지스터의 내용을 보인다.
② AX 레지스터의 내용
③ 새로이 AX 레지스터의 내용을 설정한다.
④ 프래그 레지스터의 내용을 보인다.
⑤ 오버플로우 프래그와 캐리 플래그의 내용을 변경한다.
16. S (검색)
S 명령은 메모리내의 특정한 데이타를 찾아내고 발견된 어드레스를 표시한다.
형식) S ['문자,문자열']
앞에서 만들어 보았던 SMALL.COM 의 메시지인 'Ex) small file1 file2' 중에서
'small' 을 찾아내보자.
C:\ASM>small
Ex) small file1 file2 ---- ①
C:\ASM>debug small.com ---- ②
-r ---- ③
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0100 NV UP EI PL NZ NA PO NC
155E:0100 A08000 MOV AL,[0080] DS:0080=00
-s 155e:100 'small' ---- ④
155E:010D ---- ⑤
-
① small.com 의 메시지이다.
② small.com 을 디버거로 로드한다.
③ R 명령으로 CS:IP 의 값을 알아낸다.
④ 155E:100(CS:IP) 에서부터 'small' 의 문자열을 검색한다.
⑤ 문자열을 찾아낸 메모리의 번지
17. T (트레이스)
T 명령은 프로그램을 1 명령씩 실행하고 그때의 레지스터의 값,프래그의 상태
,명령을 표시해준다.
형식) T ---- ①
T [=개시어드레스] ---- ②
T [=개시어드레스 횟수] ---- ③
① 현재의 CS:IP 의 값에서 1 명령씩 수행한다.
② 개시 어드레스에서 부터 1 면령씩 수행한다.
③ 개시 어드레스에서 부터 횟수만큼 수행한다.
C:\ASM>debug small.com
-t
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0103 NV UP EI PL NZ NA PO NC
155E:0103 3C00 CMP AL,00
-t
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0105 NV UP EI PL ZR NA PE NC
155E:0105 7522 JNZ 0129
-t
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=0107 NV UP EI PL ZR NA PE NC
155E:0107 EB16 JMP 011F
-t
AX=0000 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=155E ES=155E SS=155E CS=155E IP=011F NV UP EI PL ZR NA PE NC
155E:011F B409 MOV AH,09
-
-t=110
AX=0900 BX=0000 CX=01FF DX=0000 SP=FFFE BP=0000 SI=0000 DI=0002
DS=155E ES=155E SS=155E CS=155E IP=0115 NV UP EI PL ZR NA PE NC
155E:0115 6C DB 6C
-
-t=110 5
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0001
DS=155E ES=155E SS=155E CS=155E IP=0111 NV UP EI PL ZR NA PE NC
155E:0111 6C DB 6C
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0002
DS=155E ES=155E SS=155E CS=155E IP=0112 NV UP EI PL ZR NA PE NC
155E:0112 206669 AND [BP+69],AH SS:0069=00
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0002
DS=155E ES=155E SS=155E CS=155E IP=0115 NV UP EI PL ZR NA PE NC
155E:0115 6C DB 6C
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0116 NV UP EI PL ZR NA PE NC
155E:0116 65 DB 65
AX=0900 BX=0000 CX=01FF DX=0109 SP=FFFE BP=0000 SI=0000 DI=0003
DS=155E ES=155E SS=155E CS=155E IP=0119 NV UP EI NG NZ NA PE NC
155E:0119 66 DB 66
-
18. U (역 어셈블)
U 명령은 기계어를 역 어셈블해준다.
이 명령에 의해 역 어셈블된 코드는 정확한 프로그램의 코드가 아니다.
디버거는 명령코드인지 아니면 데이타 인지를 가리지 않고 무조건 역어셈블
하기 때문이다. 따라서 대게 점프 명령이후의 역어셈코드는 데이타인 경우가
많다.
형식) U ---- ①
U [범위] ---- ②
U [개시어드레스] ---- ③
① CS:IP 로부터 32바이트를 역어셈블한다.
② 범위로 정해진만큼 역어셈블한다.
③ 개시 어드레스로부터 이후 32바이트를 역어셈블한다.
C:\ASM>debug small.com
CS:IP 로부터 32바이트를 역 어셈블한다.
-U
155E:0100 A08000 MOV AL,[0080]
155E:0103 3C00 CMP AL,00
155E:0105 7522 JNZ 0129
155E:0107 EB16 JMP 011F
155E:0109 45 INC BP
155E:010A 7829 JS 0135
155E:010C 20736D AND [BP+DI+6D],DH
155E:010F 61 DB 61
155E:0110 6C DB 6C
155E:0111 6C DB 6C
155E:0112 206669 AND [BP+69],AH
155E:0115 6C DB 6C
155E:0116 65 DB 65
155E:0117 3120 XOR [BX+SI],SP
155E:0119 66 DB 66
155E:011A 69 DB 69
155E:011B 6C DB 6C
155E:011C 65 DB 65
155E:011D 3224 XOR AH,[SI]
155E:011F B409 MOV AH,09
-
CS:100 ∼ 110 의 내용을 역 어셈블한다.
-u 100 110
155E:0100 A08000 MOV AL,[0080]
155E:0103 3C00 CMP AL,00
155E:0105 7522 JNZ 0129
155E:0107 EB16 JMP 011F
155E:0109 45 INC BP
155E:010A 7829 JS 0135
155E:010C 20736D AND [BP+DI+6D],DH
155E:010F 61 DB 61
155E:0110 6C DB 6C
-
CS:200 이후 32바이트를 역 어셈블한다.
-u 200
155E:0200 73E4 JNB 01E6
155E:0202 B43E MOV AH,3E
155E:0204 8B1EA802 MOV BX,[02A8]
155E:0208 CD21 INT 21
155E:020A B43E MOV AH,3E
155E:020C 8B1EAA02 MOV BX,[02AA]
155E:0210 CD21 INT 21
155E:0212 E85300 CALL 0268
155E:0215 B44C MOV AH,4C
155E:0217 CD21 INT 21
155E:0219 EB0C JMP 0227
155E:021B 52 PUSH DX
155E:021C 65 DB 65
155E:021D 61 DB 61
155E:021E 64 DB 64
155E:021F 206572 AND [DI+72],AH
-
19. W (쓰기)
W 명령은 메모리의 내용을 화일이나 임의의 섹터에 써 넣는다.
형식) W [어드레스]
W [어드레스 드라이브번호 레코드번호 섹터수]
다음은 디버그를 이용해서 컴퓨터를 리부팅 시켜주는 잘 알려진 프로그램을
만들어본다. 이 부팅은 웜 부팅이고 메모리를 검사하지 않는다.
C:\ASM>debug reboot.com
File not found
-a 100
154D:0100 mov ax,40 ---- ①
154D:0103 mov ds,ax ---- ②
154D:0105 mov ax,1234 ---- ③
154D:0108 mov [72],ax ---- ④
154D:010B jmp ffff:0 ---- ⑤
154D:0110
-r cx ---- ⑥
CX 0000
:10 ---- ⑦
-w
Writing 00010 bytes
-q
① AX 에 40H 를 넣는다.
② DS 에 AX 를 복사한다.
이것은 결국 DS 를 설정하는 작업이다. (DS=40H)
③ AX 에 1234H 를 넣는다.
④ DS:72H 에 1234H 를 넣는다.
이것은 DS:72H 에 워드 1234H 를 넣으면 웜부팅시에 메모리 검사를 생략하기
때문이다.
⑤ 부트 스트랩 프로그램으로 분기
⑥ CX 레지스터를 새로 작성하기 위함이다.
⑦ CX 에 10H 를 넣는다.
이것은 W 명령시에 CX 레지스터의 값 만큼 쓰기 때문이다.
이상입니다.
어땠어요? 괜찮았죠?
워드로 뽑아두시면 좋은 교재가 될것입니다.
안녕히계세요.
[26] 제목 : [예고] 강좌를 시작하기 전에...
올린이 : natali (박선근 ) 95/01/20 02:18 읽음 : 165 관련자료 없음
안녕하세요.. 나탈리 박선근입니다.
어셈블리 강좌를 시작하기 전에 알려드릴 것과 부탁하고픈 것이
있어서 그 예고편을 올립니다.
이번에 하게될 어셈블리 강좌는 어셈블리 소스를 분석하는 형식으로
진행하게 됩니다. 그런데 강좌에 쓸 재료가 너무 부족한 형편입니다.
구상하기는 바이러스 코드를 분석해 보려고 했는데 그 소스를 얻기가
그리 쉬운 일이 아니군요.
바이너리라도 있다면 좋겠지만 그것도 없어서 오늘 자료실을 이곳 저곳
뒤졌습니다만 한 건도 못올렸습니다.
강좌에 쓰일 만한 재료감을 갖고 계신 분은 제게 좀 보내 주세요.
바이러스 코드가 아니라도 괜찮습니다. 다만, 그래픽 프로그램이나
상업용 프로그램의 락 같은 것은 사절하겠습니다.
보내주신 코드가 강좌에 쓰일지는 모릅니다. 그건 제가 코드를 보고
강좌에 쓰일 만한 내용인가를 결정합니다.
그럼 관심있는 분의 도움을 바라면서 이만...
[28] 제목 : [강좌] 어셈블리 소스분석 <1>
올린이 : natali (박선근 ) 95/01/24 19:14 읽음 : 451 관련자료 없음
=============================================================================
어셈블리 < 1 > - Source 분석을 중심으로 -
작성: 박선근(NATALI), 1995.01.
* 간편화를 위해 경어를 사용하지 않았습니다.
* TASM 1.0 이상 또는 MASM 5.0이상의 어셈블러가 필요합니다.
-----------------------------------------------------------------------------
<> 어셈블리가 필요한 이유 <>
=============================================================================
[ 어셈블리의 효용성 ]
어셈블리어는 인간이 이해할 수 있는 유일한(?) 저수준 언어이다.
예전의 거의 대부분의 우수한 프로그램들은 어셈블리로 제작되었었고, 지금도 어셈
블리는 여전히 중요한 언어임에 틀림없다.
그러나 어셈블리어가 표현과 판독의 어려움이 있고 프로그램 개발에 투자해야 하는
노력의 비대함으로 점차적으로 그 소용이 미진해 지고 있는 것 또한 사실이다.
현재 고급언어 중에서 가장 최적의 코드를 생성하는 것으로 평가되는 C언어 역시
궁극에는 어셈블리의 범주에 포함됨에도 불구하고 마치 어셈블리가 C를 위해 있는
듯한 착각을 일으킬 만큼 어셈블리의 사용층은 엷어졌고, 대신 C의 사용층은 그만
큼 두터워 졌다. 그러나, 전혀 새로운 유형의 시스템과 언어가 나오지 않는 한 어
셈블리는 여전히 최적의 코드를 생성하는 언어로 존재할 것이고, 또 사용될 것이다
(현재의 모든 시스템은 0과 1의 표현에 의존하는 기계어를 그
긆瓮 삼고있다
그런 점에서 어셈블리는 기계어를 대신하는 가장 간결한 코드를 생성한다. 그러나
만약 시스템이 인간의 말을 바로 이해하는 이른 바 '인공지능'이 현실화 된다면 더
이상 어셈블리를 사용하여 복잡한 프로그래밍을 할 이유는 없을 것이다. 말하는 것
이 곧 프로그램이 될테니까...)
많은 프로그램들이 C를 통해 구현되고 있다. 그러나 전문적인 프로그래머는 결코
C 하나에 의존하지 않는다. C가 편리하기는 하지만 결코 최적의 코드를 생성하지는
못한다는 것을 잘 알고 있기 때문이다. 그렇다고 해서 어셈블리만을 사용하지도 않
는다. 그건 너무나 비경제적이다.
가장 좋은 방법은, C와 어셈블리를 병용하는 것이다. 이런 것이 있다.
데이타베이스를 구축하는데 굳이 C나 어셈블리를 사용할 필요는 없다. 이미 데이타
베이스를 위한 전문적인 언어들이 있다. 클리퍼나 폭스프로 등의 쉽게 데이타베이
스를 구축할 수 있는 언어들이 개발되어 있는 것이다. 쉽게 할 수 있는데 굳이 어
렵게 할 이유는 없는 것이다. 그런데, 기계를 제어하는 프로그램을 만드는데 클리
퍼나 폭스프로 만을 사용할 수 있겠는가? 어려운 일이다. 이럴 땐 오히려 C나 어셈
블리를 사용하는 것이 경제적이다. 그렇다면, 데이타베이스내에서 기계를 제어해야
한다면 어떻게 해야 하는가? 데이타베이스 구축 부분은 데이타베이스를 위한 전용
언어를 사용하고, 기계제어하는 부분은 또 그에 적합한 언어를 사용하여 그 둘을
결합한다면 일은 보다 수월해 질 것이다.
C와 어셈블리를 병용하는 것도 이러한 내용과 맥락을 같이 한다.
보다 빠른 실행 속도가 요구되는 부분은 빠른 처리가 가능한 언어로, 그다지 속도
는 요구되지 않지만 어셈블리와 같은 복잡한 언어로는 구현하기 까다로운 부분은
표현이 보다 쉬운 언어로 작성하여 그 둘을 결합하는 것이다.
그런 점에서, 어셈블리는 어떤 언어보다도 빠른 처리를 가능하게 한다. 이것이 어
셈블리가 구현이 어려운 중에도 꾸준히 사용되고 있는 가장 유력한 이유이며, 어셈
블리의 효용성이 여기에 있는 것이다.
[ 어셈블리를 배우는데 필요한 것들 ]
본 강좌를 보시는 데는 아래의 사항을 준비하고 있으면 도움이 된다.
- 디버거(Debugger)
프로그래밍을 하는데 있어서 필수적인 것이다.
자신의 손에 익은 디버거가 있다면 좋겠지만 그렇지 못하다면 먼저 디버
거의 사용을 익히는 것이 좋겠다.
디버거는 여러 종류가 있는데 가장 간단한 형태로는 도스에 함께 제공되
는 debug가 있고, 볼랜드의 Turbo Debugger와 MS의 Code View 등이 가장
많이 사용되고 있다. 이 강좌는 Turbo Debugger와 Code View가 구입하기
어려운 점을 감안하여 도스의 debug를 표준 디버거로 사용할 것이다.
debug의 상세한 사용 방법은 메뉴얼을 참고하시기 바라고, 이 강좌에서
는 debug의 명령어 사용에 관한 일체의 설명을 하지 않는다.
- 소서(Sourcer)
이 프로그램은 어셈블리 source를 얻을 수 있도록 하는 역어셈블러이다.
'V Communication'이라는 곳 (회사 이름이 정확한지 모르겠다)에서 나온
프로그램인데 아마 쉐어일 것이다. 이 프로그램이 있다면 좋은 일이겠으
나 없어도 상관없다. 역어셈블 코드는 도스의 debug를 통해서도 얻을 수
있기 때문이다. debug로 역어셈블 코드를 얻는 방법은 따로 설명을 드리
겠다.
- 프린터(Printer)
프린터가 없다면 상당히 곤란하다. 수백, 수천 line의 어셈블리 코드를
화면만 통해서 보기는 무척 힘든 까닭이다. 한 화면에 나타낼 수 있는
line의 수가 너무 작기 때문에 전체적인 윤곽을 잡기가 무척 어렵다.
이 때는 프린트해 놓고 보아야 한다. 프린터가 없다면 비상한 기억력(?)
과 어떤 유혹도 뿌리치고 컴퓨터 모니터에 집중할 수 있는 무서운(?) 인
내력이 요구될 것이다.
[ debug를 통해 역어셈블 코드를 얻는 방법 ]
debug는 간이 어셈블러라고도 불릴 만큼 나름대로 독특한 기능을 보유하고 있다.
직접 어셈블리 코드를 써넣을 수 있을 뿐만 아니라 메모리의 내용을 역어셈블해 볼
수도 있다. 역어셈블된 코드를 화일이나 프린터로 출력하기 위해서는 도스의 필터
기능을 이용하면 된다.
우선 다음과 같은 간단한 내용의 텍스트 화일을 작성해 두자.
u 100 200 <- 모든 .COM프로그램은 100h번지에서 시작된다.
q <- 반드시 'q(Q)'가 있어야 한다.
그렇지 않으면 필터에서 빠져 나오질 못한다.
이 화일의 이름을 "dump.dat"라고 하자.
이제 명령행 상에서 다음과 같이 입력한다.
C:\>debug command.com < dump.dat > command.a
명령을 입력하고 잠시 기다렸다가 디렉토리를 보면 'command.a'가 새로
생성되었을 것이다.
이 화일의 내용을 보면 어셈블리 코드가 들었을 텐데, 이는 위의 일련의
동작을 통해 필터처리된 command.com의 CS:0100h~CS:0200h 범위의 역어셈
블리 코드이다. command.com의 모든 내용을 역어셈블 하자면 전체 프로그
램의 크기 만큼 처리하면 될 것이다(하지만 이런 일은 하지 말기 바란다.
아래에 실험을 위한 보다 작은 프로그램을 예로 들었다)
이런 방법으로, 다소 답답한 감이 있지만 sourcer없이 debug를 통해 어셈
블리 코드를 얻게 된다.
[ debug를 통해 역어셈블리 코드를 얻는 간단한 실험 ]
궁금증을 해소하기 위해 간단한 실험을 해보도록 하자. debug를 기동시키고 다음과
같이 입력한다.
C:\>debug
-a
jmp 110
db 'Hello, World!$'
mov dx,102
mov ah,9
int 21
mov ax,4c00
int 21
<ENTER>
-nhello.com
-rcx
1c
-w
-q
위의 내용을 모두 입력했다면 debug에서 빠져 나와 있을 것이다.
디렉토리를 보면 'hello.com'이 새로이 생성되어 있을 것인데, 이 프로그램은 실행
할 수 있다. 실행하면 화면에 'Hello, World!'를 출력한다. 위의 프로그램을 이젠
거꾸로 역어셈블 해보자.
다음과 같은 내용의 'dump.dat'화일을 작성한다.
u 100 11c
q
이제 도스상에서 다음을 입력하자.
C:\>debug hello.com < dump.dat > hello.asm
생성된 'hello.asm'을 보면 위에서 입력할 때와는 조금 다름을 알 수 있다
다른 부분은 'Hello, World!$'를 찾을 수 없는 것인데 대신 'DB ...'라는
내용이나 입력하지 않았던 명령등을 볼 수 있을 것이다.
CS:0100 JMP 0110 ; 데이타 부분을 건너뜀 ----+
; |
CS:0102 DEC AX ; 이 부분이 'Hello, World!$' |
DB 65 ; " " |
DB 6C ; " " |
DB 6C ; " " |
DB 6F ; " " |
SUB AL,20 ; " " |
PUSH DI ; " " |
DB 6F ; " " |
JB 0179 ; " " |
DB 64 ; " " |
AND [SI],SP ; " " |
; |
CS:0110 MOV DX,0102 ; 문자열 출력 부분 <---+
MOV AH,09
INT 21
CS:0117 MOV AX,4C00 ; 종료처리
INT 21
이 프로그램의 경우는 직접 작성한 것이므로 정확히 데이타가 어디에서 어
디까지라는 것을 알 수 있었지만 자신이 작성하지 않은 어떤 임의의 프로
그램을 역어셈블한다면 그런 것들을 어떻게 알 수 있을까?
힘든 일이지만, 그런 내용을 파악하는 것이 바로 '분석'의 묘미이다. 많은
경우에 그러한 내용을 판단하는 것은 분석자의 직관(?)이나 경험에 의해
파악된다. 위의 프로그램의 예에서는 'jmp'에 의해 아예 데이타 부분을 건
너 뛰고 있다. 그리고, 도스의 문자열 출력 함수인 09h는 DX에 출력할 문
자열의 옵셋을 가지며 그 문자열의 끝은 반드시 '$'이다. 이러한 점으로
미루어 볼 때, 옵셋 102h에서 10Fh까지는 실행할 수 없는 데이타 임을 짐
작하여 알 수 있다(정말일까?)
물론, 현재로썬 필자가 이렇게 말하니 '그런것 같다..' 하는 생각을 할 것
이다. 하지만 실전(?)에서는 간단한 문제가 아니다. 수천라인(어셈블리에
서 수백라인 넘기기는 쉬운 일이다)의 코드에서 어떻게 간단할 수 있겠는
가? 어려운 일이다.
[ 강좌를 시작하기 전의 문제점들... ]
본 강좌에서는 지금 그렇게 어려운 일을 해보려고 한다. 강좌가 어떻게 진행될지는
글을 쓰고 있는 본인으로써도 예측할 수 없다.
예고편에서 알려 드렸듯이, 분석해 볼만한 어셈블리 소스를 구하는 일이 그다지 쉬
운 일이 아니다(분석하기도 어려운데 재료마저 구하기 어렵다). 가장 구히기 쉬운
코드는 '바이러스'라고 생각되서(이건 저작권 문제에 크게 신경쓰지 않아도 된다)
몇분이 보내주신 바이러스 코드를 살펴 봤다. 그런데 필자가 바이러스에 대해 아는
게 없어서 인지는 모르겠으나 다음과 같은 생각을 하게 되었다.
- 바이러스 코드들은 한결 같이 쓰잘데 없는(?) 기교를 많이 부려서 눈을
어지럽게 했다(물론 숨기려는 의도가 있는 것들이니 그럴 것이다).
- 우연인지, 아니면 바이러스 코드의 정통성(?)인지는 모르겠으나 비슷한
형태가 많았다 (어떤 모범적인(?) 바이러스의 변종들이었던지...). 나름
대로 조금씩 변화를 가지기는 했으나 그건 조금 장난친 것에 지나지 않
았다(예를 들자면, 상주하는 코드의 크기를 조금 다르게 한다던가 내부
적으로 사용하는 바이러스 인식 코드를 다르게 한 내용들.... 만약 모든
바이러스가 이런 형편이라면 바이러스 만든 사람들은 더 늦기 전에 바
이러스에 대한 생각일랑 싹 지우고 그 시간에 좀 더 유용한 프로그램을
구상하는게 좋지 않을까?)
- 크기가 적어도 2,000~3,000 바이트 정도가 되니 역어셈블 코드로는 약
20~30페이지(프린트했을 때) 정도의 제법 큰 코드가 되었다 (이 정도면
하나 분석하는데 몇일이 걸려야 할까? 그것도 온라인 강좌로...)
어떤 코드는 약 2~3페이지 정도의 작은 분량도 있었는데 그건 바이러스가 아니었다
바이러스라고 설명되어 있기는 했으나 일반적인 바이러스 코드가 가지는 특성들을
구비하지 못한 것들이었고, 분석할 만한 내용이 못되었다(너무 간단해서...)
강좌를 시작하기 전에 간추려 본 문제점들은 다음과 같다.
- 바이러스 코드의 기교가 너무 혼란스러워서, 이런 것들을 강좌에서 다룰
만큼 가치가 있을까 하는 의문이 생기며, 그런 기교들이 어셈블리에 대
한 이해를 목적으로 하는 본 강좌의 목적과 어느 정도 부합되는가 하는
당위성에 대한 회의(?).
- 이런 바이러스 코드의 분석이 반드시 좋은 결과만을 줄 수 있을까?
바이러스 코드를 살펴본 바로는 변종이 많은 것 같은데 이 강좌가 그런
변종의 발생을 촉진하는 결과가 되지는 않을까?
- 이 강좌가 온라인인 관계로, 20~30 페이지나 되는 분량의 어셈블리 코드
가 너무 부담스럽다.
바이러스 코드를 분석한다고 할 때, 가장 우려되는 점은 두번째의 경우이다.
'해커'라는 말이 공공연히 나쁜 의미로 해석되고 말해지는 요즈음의 추세이고 보면
예측할 수 있는 가장 최악의 경우는 또다른 변종 바이러스가 나올 수 있다는 것이
다. 필자는 그런 결과를 원치 않는다.
그래서 본 강좌에서는 바이러스 코드에 대해서 만큼은 전체 소스를 소개하는 대신
부분적으로 사용된 기교들을 분석하는 방법을 취한다. 물론 바이러스 코드가 아니
라면 모든 내용에 대한 분석을 시도할 것이다.
본 강좌의 목적이 어셈블리를 배우고자 하는데 있으므로 부분적으로 발췌된 내용을
분석하더라도 그곳에서 얻을 것은 많다. 물론 이러한 방법은 전체적인 흐름에 대한
이해면에서는 다소의 부족한 점이 있을 것이다. 이점은 다른 소스를 분석하는 것으
로 대신할 것이다. 본 강좌를 보시는 분들은 이같은 필자의 고충(?)을 이해해 주셨
으면 고맙겠다.
[29] 제목 : [목록] 어셈블리 강좌 목록입니다..
올린이 : 아꾸 (하성욱 ) 95/02/05 00:51 읽음 : 126 관련자료 없음
ASSEMBLER 강좌 목록
━━━━━━━━━━━━━━━━
아이디 이름 제목 게시판 번호
─────────────────────────────────
까망벌레 정태식 [초급-기초] 맛보기... 3
까망벌레 정태식 [초급-기초] 메모리에 대해서 4~ 5
까망벌레 정태식 [초급-기초] 레지스터에 대해서 6
까망벌레 정태식 [초급-실습] 프로그램 하나(소스&설명 7~ 8
까망벌레 정태식 [초급-보충] 스텍? 9
까망벌레 정태식 [초급-실습] 프로그램 두울 10~11
까망벌레 정태식 [초금-명령] MOV 에 대해 12
까망벌레 정태식 [초급-실습] 프로그램 세엣 15
까망벌레 정태식 [초급-보충] 변수 선언에 대하여 16
까망벌레 정태식 [초급-보충] 주소지정방식에 대하여... 17
까망벌레 정태식 [초급-보충] 프로시져 및 스텍의 변화에 18
까망벌레 정태식 [초급-보충] 논리연산에 대해서... 19
영원의별 이세원 디버그(DEBUG.EXE) 정리 23,25
natali 박선근 [강좌] 어셈블리 소스분석 <1> 28
─────────────────────────────────
2/4일짜 까지의 어셈블리 강좌란 목록입니다.
[30] 제목 : [강좌] 어셈블리 소스분석 <2>
올린이 : natali (박선근 ) 95/02/08 20:48 읽음 : 308 관련자료 없음
=============================================================================
어셈블리 < 2 > - Source 분석을 중심으로 -
작성: 박선근(NATALI), 1995.02.
* 간편화를 위해 경어를 사용하지 않았습니다.
* TASM 1.0 이상 또는 MASM 5.0이상의 어셈블러가 필요합니다.
-----------------------------------------------------------------------------
<> 첫번째 프로그램 - Disk Monitor <>
=============================================================================
[ 변명 ... ]
서비스 이용료가 연체된 줄 모르고 있다가 설연휴 지나고서야 알았습니다. 나우는
그 달 25일까지 이용료를 내지 않으면 다음 달 1일에 사용중지 된다는 군요.
음.. 전 그 사실을 전혀 모르고 있다가 연휴 끝나는 날 접속해보니... 흘~
이래저래 첫 강좌 올리고 근 2주일이 지나버렸군요. 이에 죄송한 말씀을 드리고,
강좌를 시작하기 전에 여러분께 한가지 당부하고 싶은 것이 있습니다.
근래 며칠 동안 이번 강좌의 내용에 대해 많은 생각을 했었습니다. '바이러스분석'
이라는 것 때문이었죠. 강좌의 서두에서 분명히 본 강좌의 목적이 '바이러스분석'
에 있는 것이 아니라 '다양한 어셈블리 표현을 익히는'데 있다는 것을 밝혔었습니
다만 많은 분들이 강좌의 목적에 대한 부분을 지나치신 것 같습니다. 바이러스 코
드를 분석하는 일은 본 강좌에서 주요 줄거리가 못됩니다. 제가 강좌를 위해서 프
로그램을 직접 작성하고 그 내용을 소개할 수도 있겠지만 그건 너무 많은 시간을
소모하게 되기 때문에 조금 편리한 방향에서 저작권 문제에 지장이 없는 바이러스
이야기가 나온 것입니다. 그런데 많은 분들이 제가 한번도 들어 본적이 없는 수많
은 바이러스에 대한 질문을 하셨고 또 그것들을 분석해 달라고 자료를 보내 주셨습
니다. 이번 일로 많은 분들이 바이러스에 대한 관심이 크다는 것과 저 또한 여러가
지를 알게 된 것도 사실이지만 그 보다는 '변형바이러스'에 대한 걱정이 더 큽니다
제가 살펴 본 바로는 우리나라에서 제작된 거의 대부분의 바이러스들이 외국의 것
을 변형한 것이었습니다. 모방은 창조의 어머니라지만 글쎄요... 이런 경우에도 그
런 말이 정당할 것 같지는 않습니다.
그래서 이런 저런 고민 끝에 본 강좌에서는 바이러스 코드를 분석하더라도 전체 내
용은 소개하지 않습니다. 특별하거나 독특한 표현이 있다면 그런 부분 만을 개별적
으로 소개할 것입니다. 바이러스 코드가 아닐 때는 전체 소스를 소개할 것입니다.
본 강좌의 목적이 '바이러스 분석'에 있지 않음을 다시 한번 분명히 밝히고 오해없
기를 바라며 두번째 강좌를 시작하겠습니다.
[ 어셈블리 프로그래밍 시의 주의점들 ]
어셈블리 프로그래밍은 여타의 고급 언어 프로그래밍에서는 신경쓰지 않아도 될 많
은 내용을 프로그래머가 일일이 신경써 주어야 한다. 이러한 내용은 보다 섬세하고
강력한 프로그램을 작성할 수 있는 요소가 되지만, 다른 면으로는 매우 성가신 문
제가 아닐 수 없다. 어셈블리 프로그래밍에서 발생할 수 있는 중요하면서도 성가신
몇가지 문제점들을 정리해 보았다.
- 스택(Stack)
스택의 크기는 '적당한 것'이 좋다. 그러나 적당한 크기의 스택을 설정하기란 상
당히 신경쓰이는 일이다. 그래서 일반적으로 어느 정도의 여유를 두고 스택을 결
정하게 된다. 어떤 경우에, 스택이 모자란다고 해도 프로그램이 정상적으로 수행
될 수가 있는데 이는 스택의 넘친 부분이 다행히 프로그램의 수행에 지장이 없었
기 때문이다. 그러나 이것은 명백한 버그이며, 프로그램이 언제 심각한 에러를
발생할지 모르는 잠재적인 폭주 가능성을 안고 있는 것이다. 이런 이유로 스택은
'어느 정도 여유를 갖고' 결정되는 것이 안전하다고 할 수 있다. COM파일의 경우
코드,데이타,스택이 모두 하나의 세그먼트 안에 있으므로 스택의 사용은 더욱 조
심스럽게 행해져야 한다. 코드나 데이타 세그먼트의 크기가 커짐에 따라 그만큼
스택의 크기가 줄어 들기 때문이다.
- 레지스터(Register)
어셈블리 프로그래밍에서 레지스터의 사용은 빼놓을 수 없는 부분인 만큼 중요성
또한 크다. 그러나 레지스터는 사용 빈도나 중요성에 비해 크기가 너무 작고 일
시적이다. 그러므로 레지스터의 내용이 보관될 필요가 있고 다른 일을 수행하는
과정에서 그 내용이 변경될 가능성이 있다면 그 전에 적절한 장소에 보관해 두어
야 한다.
- 변수/데이타의 참조
다음에 나타낸 어셈블리 표현들에서 DX가 가지는 값은 무엇일까?
VALUE DW 1234H
(1) MOV DX, VALUE
(2) MOV DX, [VALUE]
(3) MOV DX, OFFSET VALUE
(1)과 (2)는 결과가 동일하고 (3)은 다르다.
(1), (2) : DX = 1234H
(3) : DX = 1234H가 있는 메모리상의 주소(Offset Address)
표현 (1), (2), (3)은 지루한(?) 어셈블리 프로그래밍에서 때때로 혼동할 수 있
는 소지를 갖고 있다. 고급언어 프로그래밍에서도 마찬가지 이겠지만, 훌륭한 프
로그래머가 되기 위해서는 어중간한 표현을 삼가하고 명확한 표현방법을 익히는
데 힘써는 것이 중요하다. 더우기 VALUE와 [VALUE]의 경우는 동일한 결과를 갖지
만 전자 보다는 후자가 보다 명백하게 '값'의 의미를 강조하고 있다.
- LABEL의 사용과 함수 구성
어셈블리 프로그램에 있어서 함수로 처리해야 할 부분과 분기 명령을 사용해서
강제적으로 이동해야 할 부분을 구분하는 일은 프로그램의 최적화에 큰 영향을
끼칠 수 있으므로 신중히 처리해야 한다.
보다 기본적인 문제들이 더 있지만 여기서 모든 내용을 언급하지는 않는다. 강좌가
진행되면서 그런 내용들이 부분적으로 다루어 질 것이다.
[ 첫번째 프로그램 - DISKMON.ASM ]
다음의 어셈블리 코드는 이세원(영원의별)님이 보내주신 프로그램을 역어셈블하고,
TASM과 MASM에서 어셈블될 수 있도록 필자가 함수/라벨등을 적절히 구분한 것이다.
프로그램의 내용이 신선하고, 본 강좌에서 처음으로 분석하게 될 프로그램인 만큼
가벼운 마음으로 풀어 볼 수 있는 내용으로 생각되어 소개한다.
참고로, 이 프로그램은 램상주 프로그램인데 이세원님이 보내주신 원래의 프로그램
인 DISKMON.COM의 크기는 783바이트였으며 램에 상주하면 624바이트의 메모리를 차
지하게 되어 있었다. 그러나, 여기에 소개된 소스를 어셈블해보면 아시겠지만 파일
의 크기는 803바이트(MASM=803바이트, TASM=802바이트), 램상주시의 메모리 차지는
640바이트로 변경되었다. 변경(추가)된 부분은 뒤에서 알려드리겠다.
;---------------------------------------------------------------------------
; DISKMON.COM, Disk Monitor
;
; To build MASM /ML diskmon;
; LINK diskmon;
; EXE2BIN diskmon.exe diskmon.com
; -------------------------------
; TASM /ml diskmon
; TLINK /t diskmon <- '/t'는 링크하고 바로 COM파일을 생성
;
; 이 프로그램은 INT 21h의 31h함수를 이용해서 메모리에 상주하며, 바이오스의 디
; 스크 인터럽트인 INT 13h를 가로채고 디스크 작업의 상태를 화면상단, 우측에 표
; 시한다. 디스크 작업의 상태는 '디스크ID-작업-진행과정'의 형태로 표시한다.
;
; 디스크ID = 'A', 'B', 'C', 'D'중의 하나
; 작업 = 'R'ead, 'W'rite, 'V'erify, 'F'ormat중의 하나
; 진행과정 = 바람개비의 형태를 표시('|', '/', '-', '\')
;
; 처음 프로그램을 실행하면 메모리에 상주하며, 다시 한번 프로그램을 실행하면
; 상주를 해제한다. 별도의 옵션은 없다.
;
; 각 소스라인에 대한 상세한 설명은 별도로 하도록 하고 우선 소스를 살펴보자.
; 라인번호는 설명을 위해 편의상 붙인 것이므로 어셈블할 때는 라인번호를 제거
; 해야 한다.
;
0001 PSP_ENV_SEG EQU 2CH
0002 VIDEO_OFF EQU 9AH
0003
0004 ;----------------------------------------------------------------------
0005 .MODEL SMALL
0006
0007 .CODE
0008
0009 ORG 100h
0010
0011 ;----------------------------------------------------------------------
0012
0013 DiskMonitor PROC FAR
0014 JMP InstallCheck
0015
0016 Author DB 'Copyright (C) Yi,se-won.'
0017 KeepInt13h DD ?
0018 KeepInt60h DD ?
0019 VideoSeg DW 0B000H ; 칼라이면 800h를 더함
0020 SiteBuffer DB 0, 0, 0, 0, 0, 0
0021 DriveID DB 0, 70H ; 70H는 문자 색상값이다
0022 CurrentJob DB 0, 70H ; " "
0023 CurrentSite DB 7CH, 70H ; " "
0024 DiskMonitor ENDP
0025
0026 ;----------------------------------------------------------------------
0027 ; New handler for INT 60h
0028
0029 NewInt60h PROC FAR
0030 PUSHF
0031 CALL DWORD PTR CS:[KeepInt60h]
0032 IRET
0033 NewInt60h ENDP
0034
0035 ;----------------------------------------------------------------------
0036 ; New handler for INT 13h
0037
0038 NewInt13h PROC FAR
0039 PUSHF
0040 CLI ; 이 작업을 진행하는 동안의
0041 ; 인터럽트를 금지한다
0042 PUSH SS ; 레지스터들을 대피시킨다
0043 PUSH SP
0044 PUSH BP
0045 PUSH AX
0046 PUSH BX
0047 PUSH CX
0048 PUSH DX
0049 PUSH SI
0050 PUSH DI
0051 PUSH ES
0052 PUSH DS
0053
0054 PUSH CS ; CS를 DS에 복사한다
0055 POP DS
0056
0057 CALL CheckDriveID
0058 CALL CheckCurJob
0059 CALL CheckCurSite
0060 CALL StoreCurSite
0061 CALL PutDriveID
0062
0063 POP DS
0064 POP ES
0065 POP DI
0066 POP SI
0067 POP DX
0068 POP CX
0069 POP BX
0070 POP AX
0071 POP BP
0072 POP SP
0073 POP SS
0074 POPF
0075
0076 PUSHF
0077 CALL DWORD PTR CS:[KeepInt13h]
0078
0079 PUSHF
0080 CLI
0081
0082 PUSH SS
0083 PUSH SP
0084 PUSH BP
0085 PUSH AX
0086 PUSH BX
0087 PUSH CX
0088 PUSH DX
0089 PUSH SI
0090 PUSH DI
0091 PUSH ES
0092 PUSH DS
0093
0094 CALL PutCurSite
0095
0096 POP DS
0097 POP ES
0098 POP DI
0099 POP SI
0100 POP DX
0101 POP CX
0102 POP BX
0103 POP AX
0104 POP BP
0105 POP SP
0106 POP SS
0107 POPF
0108
0109 RETF 2 ; 아직 POP되지 않은
0110 NewInt13h ENDP ; FLAG 레지스터를 위해
0111
0112 ;----------------------------------------------------------------------
0113
0114 CheckDriveID PROC NEAR
0115 CMP DL,0
0116 JE DRIVE_A
0117 CMP DL,1
0118 JE DRIVE_B
0119 CMP DL,80H
0120 JE DRIVE_C
0121 CMP DL,81H
0122 JE DRIVE_D
0123 RET
0124 DRIVE_A:
0125 MOV [DriveID],41H ; 'A'
0126 RET
0127 DRIVE_B:
0128 MOV [DriveID],42H ; 'B'
0129 RET
0130 DRIVE_C:
0131 MOV [DriveID],43H ; 'C'
0132 RET
0133 DRIVE_D:
0134 MOV [DriveID],44H ; 'D'
0135 RET
0136 CheckDriveID ENDP
0137
0138 ;----------------------------------------------------------------------
0139
0140 CheckCurJob PROC NEAR
0141 CMP AH,2
0142 JE READ_DISK
0143 CMP AH,3
0144 JE WRITE_DISK
0145 CMP AH,4
0146 JE VERIFY_DISK
0147 CMP AH,5
0148 JE FORMAT_DISK
0149 CMP AH,6
0150 JE FORMAT_DISK
0151 CMP AH,7
0152 JE FORMAT_DISK
0153 CMP AH,0AH
0154 JE READ_DISK
0155 CMP AH,0BH
0156 JE WRITE_DISK
0157 RET
0158 READ_DISK:
0159 MOV [CurrentJob],52H ; 'R'
0160 RET
0161 WRITE_DISK:
0162 MOV [CurrentJob],57H ; 'W'
0163 RET
0164 VERIFY_DISK:
0165 MOV [CurrentJob],56H ; 'V'
0166 RET
0167 FORMAT_DISK:
0168 MOV [CurrentJob],46H ; 'F'
0169 RET
0170 CheckCurJob ENDP
0171
0172 ;----------------------------------------------------------------------
0173 ; CurrentSite의 최초값은 '|'이다.
0174 CheckCurSite PROC NEAR
0175 CMP CurrentSite,7CH ; '|'
0176 JE SITE_2
0177 CMP CurrentSite,2FH ; '/'
0178 JE SITE_3
0179 CMP CurrentSite,2DH ; '-'
0180 JE SITE_4
0181 MOV CurrentSite,7CH ; '|'
0182 RET
0183 SITE_2:
0184 MOV [CurrentSite],2FH ; '/'
0185 RET
0186 SITE_3:
0187 MOV [CurrentSite],2DH ; '-'
0188 RET
0189 SITE_4:
0190 MOV [CurrentSite],5CH ; '\'
0191 RET
0192 CheckCurSite ENDP
0193
0194 ;----------------------------------------------------------------------
0195
0196 PutDriveID PROC NEAR
0197 PUSH CS
0198 POP DS
0199 MOV SI,OFFSET DriveID
0200 MOV AX,CS:[VideoSeg]
0201 MOV ES,AX ; ES = 0B800h또는 0B000h
0202 MOV DI,VIDEO_OFF ; DI = 9Ah(=154)
0203 MOV CX,3
0204 REP MOVSW ; Copy to ES:DI from DS:SI
0205 RET ; '문자+속성'을 위해 워드복사
0206 PutDriveID ENDP
0207
0208 ;----------------------------------------------------------------------
0209
0210 StoreCurSite PROC NEAR
0211 PUSH CS
0212 POP ES
0213 MOV DI,OFFSET SiteBuffer
0214 MOV AX,CS:[VideoSeg]
0215 MOV DS,AX
0216 MOV SI,VIDEO_OFF
0217 MOV CX,3
0218 REP MOVSW
0219 RET
0220 StoreCurSite ENDP
0221
0222 ;----------------------------------------------------------------------
0223
0224 PutCurSite PROC NEAR
0225 PUSH CS
0226 POP DS
0227 MOV SI,OFFSET SiteBuffer
0228 MOV AX,CS:[VideoSeg]
0229 MOV ES,AX
0230 MOV DI,VIDEO_OFF
0231 MOV CX,3
0232 REP MOVSW
0233 RET
0234 PutCurSite ENDP
0235
0236 TSR_BLOCK EQU $ ; TSR_BLOCK = 이곳의 옵셋
0237
0238 ;----------------------------------------------------------------------
0239
0240 InstallMsg DB 0DH, 0AH
0241 DB '旼컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴 '
0242 DB 0DH, 0AH
0243 DB ' Disk monitor. '
0244 DB 0DH, 0AH
0245 DB ' Copyright (C) Yi, se - won. 1994.11 '
0246 DB 0DH, 0AH
0247 DB '읕컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴 '
0248 DB 0DH, 0AH
0249 DB ' Installed.'
0250 DB 7, 0DH, 0AH, '$'
0251 RemoveMsg DB 0DH, 0AH, ' Disk monitor'
0252 DB 0DH, 0AH, ' Removed.'
0253 DB 7, 0DH, 0AH, '$'
0254 NotRemoveMsg DB 0DH, 0AH, ' Disk monitor'
0255 DB 0DH, 0AH
0256 DB ' Not removed.'
0257 DB 7, 0DH, 0AH, '$'
0258
0259 ;----------------------------------------------------------------------
0260
0261 InstallCheck:
0262 MOV AX,3560H ; 인터럽트 벡터 얻기
0263 INT 21H ; ES:BX = Seg:Ofs
0264 CMP WORD PTR ES:[Author],6F43H ; 'Co'인가?
0265 JNE InstallTSR ; Author의 옵셋
0266 ; 은 103h이다
0267 MOV AX,3513H
0268 INT 21H
0269 CMP WORD PTR ES:[Author],6F43H ; 한번 더 확인
0270 JE RemoveTSR
0271
0272 MOV DX,OFFSET NotRemoveMsg
0273 MOV AH,9
0274 INT 21H
0275
0276 INT 20H ; 프로그램 종료
0277 RemoveTSR:
0278 PUSH DS
0279
0280 MOV AX,2513H ; 인터럽트 벡터 설정
0281 MOV DX,WORD PTR ES:[KeepInt13h] ; 옵셋
0282 MOV DS,WORD PTR ES:[KeepInt13h+2] ; 세그먼트
0283 INT 21H
0284
0285 MOV AX,2560H
0286 MOV DX,WORD PTR ES:[KeepInt60h] ; 옵셋
0287 MOV DS,WORD PTR ES:[KeepInt60h+2] ; 세그먼트
0288 INT 21H
0289
0290 POP DS
0291
0292 MOV AH,49H ; 메모리 해제, ES=해제할 메모리
0293 INT 21H ; 블럭의 세그먼트
0294
0295 MOV DX,OFFSET RemoveMsg
0296 MOV AH,9
0297 INT 21H
0298
0299 INT 20H ; 프로그램 종료
0300
0301 InstallTSR:
0302 MOV AH,0FH ; 비디오 모드 얻기
0303 INT 10H
0304 CMP AL,7 ; 흑백인가?
0305 JE Settings ; 그렇다면 VideoSeg=0B000h
0306 ADD CS:[VideoSeg],800H ; 아니면 VideoSeg=0B800h
0307 Settings:
0308 MOV AX,3560H
0309 INT 21H
0310
0311 MOV WORD PTR CS:[KeepInt60h],BX ; 옵셋 보관
0312 MOV WORD PTR CS:[KeepInt60h+2],ES ; 세그먼트 보관
0313
0314 MOV AX,2560H
0315 MOV DX,OFFSET NewInt60h ; 새로운 핸들러 설치
0316 INT 21H
0317
0318 MOV AX,3513H
0319 INT 21H
0320
0321 MOV WORD PTR CS:[KeepInt13h],BX ; 옵셋 보관
0322 MOV WORD PTR CS:[KeepInt13h+2],ES ; 세그먼트 보관
0323
0324 MOV AX,2513H
0325 MOV DX,OFFSET NewInt13h ; 새로운 핸들러 설치
0326 INT 21H
0327
0328 MOV AH,49H
0329 MOV ES,CS:[PSP_ENV_SEG]
0330 INT 21H
0331
0332 MOV AH,9
0333 MOV DX,OFFSET InstallMsg
0334 INT 21H
0335
0336 LEA DX,TSR_BLOCK
0337 ADD DX, 0Fh
0338 MOV CL, 4
0339 SHR DX, CL
0340
0341 MOV AX,3100H
0342 INT 21H
0343
0344 ;----------------------------------------------------------------------
0345
0346 END DiskMonitor ; Entry point
이 프로그램의 전체적인 흐름을 살펴보자.
┌───────┐
│프로그램 시작 │
└───┬───┘
│
▽
┌───────────────┐Yes
│ 이미 설치되었는가? ├──┐
└───────┬───────┘ │
│No │
▽ │
┌───────────────┐ │
│ 원래의 인터럽트 핸들러 저장 │ │
┌──┤ 새로운 인터럽트 핸들러 설치 │ │
│ │ 메모리 할당/램상주 │ │
│ └───────────────┘ │
│ ┌───────────────┐ │
│ │ 원래의 인터럽트 핸들러 복구 │◁─┘
│ │ 메모리 해제/램상주 해제 │
│ └───────┬───────┘
│ │
│ ▽
│ ┌───────┐
└─────▷│프로그램 종료 │
└───────┘
전체 흐름도에서 보는 바와 같이 어셈블리 소스가 약 300여 라인이나 되는 것에 비
해 하는 일은 매우 간단하다. 하지만 이런 일을 하는 프로그램을 고급언어로 작성
한다면 최적화 된다고 해도 메모리에 상주하는 크기가 적어도 5~6,000바이트는 될
것이다(예를 들어서 C와 같은 언어). 그러나 어셈블리로 작성된 이 프로그램은 640
바이트 만을 차지할 뿐이다.
위의 흐름도를 통해 프로그램이 전체적인 윤곽을 알 수 있었을 것이다. 이제 프로
그램의 세부적인 내용을 보자. 각 라인 단위로 설명한다.
0001 : PSP의 옵셋 2Ch에는 프로그램의 환경 블럭에 대한 세그먼트 주소가 저
장되어 있다(Word). 프로그램이 메모리에 상주하기 전에 자신에게 할
당되었던 환경블럭을 메모리에서 해제하게 되는데 이 부분은 다시 설
명된다.
0002 : 이 프로그램에서는 현재의 디스크 작업 상태를 화면의 최상단, 최우측
에 표시한다. 화면상의 임의의 위치 (X,Y)에 대응되는 비디오 메모리
를 계산하기 위해서는 다음과 같이 한다.
임의의 위치 (X, Y)에 대응되는 비디오 메모리 주소
문자주소 = (Y*160)+(X*2)
속성주소 = (Y*160)+(X*2)+1
이 계산에 따르면 9Ah(154)는 화면상의 (X, Y) = (77, 0)의 좌표를 지
정하는 것임을 알 수 있다. 이 값은 뒤에서 비디오 메모리의 세그먼트
주소에 더해진다.
0005 : 메모리 모델을 SMALL로 설정하도록 한다. SMALL 모델은 코드 세그먼트
와 데이타 세그먼트를 각각 한 개씩 가질 수 있다.
0007 : 코드세그먼트의 경계를 설정한다. 간이 코드 세그먼트 지시어에 의해
생성된 세그먼트의 이름은 기본적으로 '_TEXT'가 된다.
0009 : 초기 명령 포인터(Instruction Point, IP)를 100h로 설정한다.
모든 COM 프로그램은 100h번지로 부터 시작된다.
0014 : 초기 CS:IP 위치에는 반드시 실행 가능한 코드가 있어야 한다.
만약 여기에 실행 코드가 아닌 어떤 데이타가 오더라도 CPU는 그것이
데이타임을 알아보지 못한다.
0016~0023 : 메모리 상주 후에 사용하게 될 변수들을 정의한다.
Author : 프로그램 제작자에 대한 정보
KeepInt13h : 원래의 INT 13h의 주소 보관용
KeepInt60h : 원래의 INT 60h의 주소 보관용
VideoSeg : 비디오 메모리의 세그먼트 주소(Default=0B000H, 흑백화면)
SiteBuffer : 디스크 동작 상태를 저장할 버퍼
CurrentJob과 CurrentSite를 합친 문자열
예) R|, R/, R-, R\ <- 읽기 작업중...
F|, F/, F-, F\ <- 초기화 작업중...
DriveID : 드라이브 ID = 'A', 'B', 'C', or 'D'
CurrentJob : 현재의 작업 = 읽기('R'), 쓰기('W')
검사('V'), 초기화('F')
CurrentSite : 바람개비 모양(Default='|')
0029~0033 : INT 60h에 대한 새로운 핸들러.
프로그램이 메모리에 상주한 후 새로운 핸들러의 세그먼트 주소는 메
모리에 상주한 프로그램의 CS와 동일하다. 이 점을 이용해서 메모리
상주 여부를 확인한다. 라인 0031의 표현은 원래,
CALL CS:[KeepInt60h]
이었으나 뜻을 명확히 하기 위해 변경되었다.
0038~0110 : INT 13h에 대한 새로운 핸들러.
바이오스를 통한 디스크 입출력이 있을 때 마다 이 핸들러가 호출되고
핸들러는 INT 13h가 호출될 때의 레지스터를 검사해서 어떤 작업을 할
것인지를 파악하게 된다. 라인 0054~0055는 DS의 값을 CS의 값으로 대
치하는 작업인데 이는 다음과 같이 표현될 수 있다.
MOV DX,CS
MOV DS,DX
즉, 세그먼트 레지스트 끼리의 직접적인 교환은 할 수 없으므로 범용
레지스터를 통해 CS를 DS로 복사하는 것이다. 그러나 이 방법은 PUSH,
POP에 의한 복사방법 보다 코드의 크기가 크며 CPU 사이클도 늦다.
0114~0136 : 작업이 진행될 디스크의 ID를 검사하고 DriveID에 보관한다.
0140~0170 : INT 13h로 넘겨진 레지스터에서 AH를 검사하면 어떤 작업을 하게될 지
알 수 있다. 이를 검사하고 각 작업에 따라 'R', 'W', 'V', 'F' 중의
한가지를 CurrentJob에 보관한다.
0174~0192 : 바람개비의 종류를 결정한다. 바람개비는 '|', '/', '-', '\'를 차례
대로 출력하여 구현한다. 이 값은 CurrentSite에 저장된다.
0196~0206 : 드라이브 ID('A', 'B',..)를 화면의 최상단 우측(Y=0, X=77)에 출력한
한다. ES:DI = 비디오세그먼트:009Ah
0210~0220 : 작업의 유형과 선택된 바람개비 모양을 SiteBuffer에 저장한다.
0224~0234 : SiteBuffer에 저장된 문자열을 비디오세그먼트:009Ah에 출력한다.
0236 : 메모리에 상주시킬 영역의 크기(바이트 단위)를 계산하기 편리하도록
하기 위해 사용되었다. '$'은 한 세그먼트 내에서 '그곳 까지의 거리'
인 옵셋의 개념과 동일하므로 그 값은 16비트 범위(0~64KB)에 있다.
즉, 이 프로그램은 CS:0000~CS:TSR_BLOCK 영역을 메모리에 상주시키게
된다. 이 부분은 역어셈블한 코드에서는 찾을 수 없다. 필자가 프로그
램의 앞뒤를 맞추어 보고 임의로 삽입했다.
0240~0257 : 프로그램의 설치/제거시에 보야줄 메세지를 정의한다.
0261~0276 : 프로그램이 이미 메모리에 설치되어 있는지를 검사하고 이미 설치되어
있다면 상주를 해제하는 곳으로, 그렇지 않다면 설치하는 곳으로 분기
한다. 프로그램의 설치 여부는 앞서 이미 언급한 바 있지만 INT 60h의
새로운 핸들러의 세그먼트 주소를 v얻고 그 세그먼트의 103h옵셋을 조
사하는 것이다. 103h옵셋에는 'Copyright..'라는 문자열이 있고 이 문
자열의 머릿부분인 'Co'는 워드 단위로 읽었을 때 6F43h가 되므로 이
를 확인한다. 만약 6F42h가 아니라면 아직 설치되지 않은 것으로 간주
하여 설치처리를 하게되며, 6F43h라면 확인을 위해 다시 한번 INT 13h
의 세그먼트를 얻고 그 세그먼트의 103h옵셋과 6F43h를 비교하여 동일
하다면 이미 설치된 것으로 간주하여 상주 해제 부분으로 분기한다.
INT 13h의 새로운 핸들러 세그먼트는 INT 60h의 새로운 핸들러 세그먼
트와 동일하다.
0277~0299 : 메모리 상주를 해제하기 위해 보관해 두었던 원래의 인터럽트 핸들러
벡터를 복구하고 상주를 위해 할당되었던 메모리를 해제한다.
0301~0342 : 비디오 메모리에 디스크의 작업 상태를 써야 하므로 비디오 메모리의
세그먼트를 계산하기 위해(칼라는 0B800h, 흑백은 0B000h로 다르다)
비디오 모드를 얻고 칼라이면 VideoSeg(Default=0B000h)에 800h를 더
한 후 메모리에 상주하기 위해 INT 13h,INT 60h의 새로운 핸들러를 설
치하고 상주하게될 메모리 블럭 (바이트 단위를 패러그래프 단위로 환
산)을 할당한다. 라인 0328~0330의 표현,
MOV AH,49H
MOV ES,CS:[PSP_ENV_SEG]
INT 21H
은 이 프로그램이 실행되면서 생성된 환경영역(Default=384바이트)을
해제한다. 프로그램이 실행되면 도스는 그 프로그램을 위한 환경영역과
프로그램 자체를 위한 프로그램영역을 할당한다. 메모리 상주 프로그램
의 경우 상주를 해제할 때는 프로그램영역과 환경영역을 모두 해제해
주어야 하는데 이 프로그램에서는 환경영역을 미리 해제하고 있다.
라인 0336~0342는 메모리 상주를 위해 상주할 코드의 크기를 계산한다.
LEA DX,TSR_BLOCK ; TSR_BLOCK의 유효주소(옵셋)을 얻는다.
; 상주하게 될 영역은 옵셋 0에서
; TSR_BLOCK까지의 바이트 크기와 PSP의
; 256바이트를 합해서 계산된다. 그러나
; COM 프로그램은 같은 세그먼트 내에
; PSP를 가지고 있으므로(옵셋 0~FFh)
; 단순히 TSR_BLOCK까지의 바이트 길이만
; 계산된다. EXE프로그램이라면 PSP의 크
; 기인 100h가 더해져야할 것이다.
ADD DX, 0Fh ; DX는 메모리에 상주할 프로그램의 크기
; 가 바이트 단위로 저장되어 있다.
; 이 값을 패러그래프 단위로 변환하기
; 위해서는 4로 나누어 주게 되는데 그
; 때의 소실되는 나머지 값을 보정하기
; 위해서 0Fh를 더해 주었다.
; 이 부분은 원래의 원래 코드에는 없는
; 내용이다. 원래의 프로그램에서는 DX에
; 바로 패러그래프 단위의 메모리 크기가
; 저장되었었는데 이는 아마도 EQU를 사
; 용해서 앞부분에서 미리 계산되었기 때
; 문일 것으로 추측된다.
MOV CL, 4
SHR DX, CL ; 바이트 단위의 길이를 패러그래프 단위
; 로 환산하기 위해 DX를 우측으로 4비트
; 시프트한다(DX / 4).
MOV AX,3100H ; 메모리 상주후 종료 서비스, 복귀코드=0
INT 21H
0346 : 프로그램의 진입점(Entry point)을 명시한다.
어셈블리 소스와 설명 부분을 따로 프린트한 후 라인번호를 비교하면서 보는 것이 편
리할 것이다.
프로그램은 비교적 간단한 내용이므로 이제 막 어셈블리를 배우기 시작하는 초보가
아니라면 이해하는데는 별 어려움이 없을 것으로 생각된다.
[31] 제목 : 소스분석<2>에서분석된프로그램의제작자입니다.
올린이 : 영원의별(이세원 ) 95/02/17 00:38 읽음 : 223 관련자료 없음
안녕하세요.
박선근님(ID:natali)의 [강좌] 어셈블리 소스분석 <2> 에서 분석된
프로그램 (diskmon.com)의 제작자 이세원 입니다.
먼저 선근님의 놀라운 코드 분석력에 경탄을 마지 않습니다. 물론
코드는 소서로 뽑아낼 수 있으나 제작자의 코드 의도를 정확히 파악한
다는것은 결코 쉬운것이 아니기 때문입니다.
밑의 강좌 내용중에 극히 일부 제작자의 본래 의도와 약간 다른 해
석이 있었는데 단지, 제작자가 살아있는 이유로 이를 보충할까 합니
다.(하는게 낫겠죠? 선근님?)
이 프로그램을 만들게된 동기는 iomon.com 이나 노턴 유티리티의 di
skmon.exe가 디스크의 움직임을 화면에 표시해 주지만 뭔가 부족한 점
이 있어 보다 확실한 디스크의 움직임을 구현해보기 위해서 였습니다.
그것은 단지, 재미있는 화면 출력으로 끝나는것이 아니라 디스크의 섬
세한 움직임을 포착하여 디스크 작동상의 오류나 어떤 변화를 찾아내
기 위해서 였습니다.
제작기간은 총 7일 가량 소요 되었습니다. 처음에 화면의 구성과 프
로그램의 흐름을 잡아 몇시간만에 완성된 프로그램을 제작하여 시험하
는데, 알 수 없는 이유로 A나 B드라이브를 읽을때 디스켓이 끼워져 있
지 않았는데도 A나 B드라이브로 넘어갔습니 다. 저로서는 도무지 이유
를 알 수가 없었습니다. 이 문제는 선근님의 코드 분석에도 밝혀지지
가 않았습니다.(선근님 소스 109 라인) 결국 이 문제로 오랜 고민을
하게 되었는데 우연히 디스크 인터럽트의 복귀과정에서 스택의 워드를
하나 더 pop 해야함을 알게 되었습니다. (보통의 사용자 인터럽트 핸
들러에서의 복귀는 iret 으로 스택의 3워드를 비워야하나 디스크 인터
럽트만 유난히 4워드를 비워야 합니다.) 이것이 109 라인의 'retf 2'
인 이유입니다.
그리고 이것은 기술적인 문제이지만, 프로그램을 메모리에 상주시키
기전에 먼저 환경블럭을 해제하는것이 좋습니다. 왜냐하면 가능한한
램상주 프로그램은 메모리를 덜 차지해야하기 때문입니다.
그리고 이것은 보충입니다만, 램상주 프로그램의 경우에 메모리에
상주시킬 프로그램 일부분의 크기를 계산하는 방법은 매우 간단합니
다. 그것은 상주시킬 부분까지의 레이블에서 start 레이블을 빼고 다
시 256byte를 더합니다.(256은 psp의 크기) 거기에 15바이트를 더합니
다. (패러그래프 단위로 나눌때 완전히 포함되게하기 위해서) 그리고
패러그래프 단위인 16으로 나눕니다. - 이것은 도스인터럽트 21h 함수
번호 31h인 경우 입니다.
그러나 더욱 간단히 프로그램을 메모리에 상주시키고자 한다면 도스
인터럽트 27h를 쓰십시오. 위의 방법(int 21h,ah=31h)이 프로그램의
크기를 위에서부터 측정하는 방법이라면 int 27h는 반대로 프로그램의
크기를 밑에서부터 측정합니다. 즉 메모리에서 제거하고자 하는부분
이하를 dx에 넣고 호출만하면 됩니다. 이 차이는 방법의 차이일뿐이고
기능에서의 차이는 없습니다. (int 21h,ah=31h는 패러그래프 단위로
램상주시키고, int 27h는 바이트 단위로 램상주 시키기 때문에 방법의
차이가 생겨난것 입니다.) 그리고 이러한 계산은 어셈블러가 어셈블
시(pass 1)에 계산하기 때문에 프로그래머는 신경쓰지 않아도 됩니다
.(하나의 세그먼트 단위로 절대번지를 계산해서 자동으로 넣어주므로)
그리고 int 60h를 후킹한 이유는, 이 프로그램을 상주시킨후에 다른
int 13h를 후킹하는 프로그램이 있었다고 할때, 이 프로그램을 메모리
에서 제거하면 디스크 인터럽트는 아작납니다.(매우 큰 사고임) 따라
서 이 프로그램의 제거시에 이 프로그램 이후에 디스크 인터럽트를 후
킹한 프로그램이 있었는가를 알아보기 위해서 인식자를 int 60h에 숨
겨 두었던것 입니다. (다른 프로그램에서 int 60h를 후킹할 이유가 없
으므로...)
끝으로 이 프로그램의 원 소스(흔히 하는말로 오리지날 쏘쓰~)를 공
개합니다. 그리고 별 볼일 없는 프로그램임에도 불구하고 분석을 해주
신 선근님에게 다시한번 감사 드립니다.
;===========================================================================
; DISKMON
; 디스크의 움직임을 관찰한다.
;==========================================================================
include macro.inc
color = 01110000b ;흰색바탕에 검은색글자
code segment
assume cs:code
org 100h
start:
jmp begin
copyright db 'Copyright (C) Yi,se-won.' ;인식자는 Co 이다.
old_13h dd ?
old_60h dd ?
video_buffer dw 0b000h ;칼라 비디오 버퍼
old_screen db 6 dup(0) ;본래의 화면 저장소
drive db ?
db color
status db ?
db color
speed db '|' ;바람개비 초기치
db color
;-------------------------
; user interrupt handler
;-------------------------
new_60h: ;인식자 검사를 위한
pushf ;사용자 인터럽트 핸들러
call cs:old_60h
iret
;------------------------
; disk interrupt handler
;------------------------
new_13h:
pushf
cli
push ss
push sp
push bp
push_register
push es
push ds
push cs
pop ds
perform:
call drive_check ;드라이브 체크
call status_check ;디스크 체크
call rotation_count ;바람개비 상태 체크
call screen_save ;본래화면 저장
call monitor ;화면 출력
pop ds
pop es
pop_register
pop bp
pop sp
pop ss
popf
pushf
call cs:old_13h
next:
pushf
cli
push ss
push sp
push bp
push_register
push es
push ds
call screen_recover
pop ds
pop es
pop_register
pop bp
pop sp
pop ss
popf
retf 2 ;스택의 4바이트를 비운다.
drive_check: ;드라이브를 체크한다.
cmp dl,0
je a_drive
cmp dl,1
je b_drive
cmp dl,80h
je c_drive
cmp dl,81h
je d_drive
drive_check_exit:
ret
a_drive:
mov ds:drive,'A'
ret
b_drive:
mov ds:drive,'B'
ret
c_drive:
mov ds:drive,'C'
ret
d_drive:
mov ds:drive,'D'
ret
status_check:
cmp ah,2
je read
cmp ah,3
je write
cmp ah,4
je verify
cmp ah,5
je format
cmp ah,6
je format
cmp ah,7
je format
cmp ah,0ah
je read
cmp ah,0bh
je write
status_check_exit:
ret
read:
mov ds:status,'R'
ret
write:
mov ds:status,'W'
ret
verify:
mov ds:status,'V'
ret
format:
mov ds:status,'F'
ret
rotation_count:
cmp ds:speed,'|'
je rotation_1
cmp ds:speed,'/'
je rotation_2
cmp ds:speed,'-'
je rotation_3
mov ds:speed,'|'
ret
rotation_1:
mov ds:speed,'/'
ret
rotation_2:
mov ds:speed,'-'
ret
rotation_3:
mov ds:speed,'\'
ret
monitor: ;디스크의 상태를 화면
push cs ;출력한다.
pop ds
mov si,offset drive
mov ax,cs:video_buffer
mov es,ax
mov di,154
mov cx,3
rep movsw
ret
screen_save:
push cs
pop es
mov di,offset es:old_screen
mov ax,cs:video_buffer
mov ds,ax
mov si,154
mov cx,3
rep movsw
ret
screen_recover: ;본래의 화면을 복구한다.
push cs
pop ds
mov si,offset ds:old_screen
mov ax,cs:video_buffer
mov es,ax
mov di,154
mov cx,3
rep movsw
ret
install_msg db 13,10,'┌────────────────────┐'
db 13,10,'│ Disk monitor. │'
db 13,10,'│ Copyright (C) Yi, se - won. 1994.11 │'
db 13,10,'└────────────────────┘'
db 13,10,' Installed.',7
db 13,10,'$'
removed_msg db 13,10,' Disk monitor'
db 13,10,' Removed.',7
db 13,10,'$'
no_remove_msg db 13,10,' Disk monitor'
db 13,10,' Not removed.',7
db 13,10,'$'
begin:
mov ax,3560h ;인식자를 위한 인터럽트에서
int 21h ;인식자를 검사한다.
cmp word ptr es:copyright,6f43h ;만약 인식자가 없다면
jne install ;인스톨한다.
disk_vector_check:
mov ax,3513h ;디스크 인터럽트에서
int 21h ;인식자를 검사한다.
cmp word ptr es:copyright,6f43h ;인식자가 있다면 프로그램을
je remove ;메모리에서 제거한다.
no_remove: ;만약 인식자가 없다면 다른
mov dx,offset no_remove_msg ;프로그램이 디스크 인터럽트를
mov ah,9 ;후킹한 것이므로 프로그램을
int 21h ;메모리에서 제거할 수 없다.
;
int 20h
remove: ;이미 메모리에 상주하므로
push ds ;프로그램을 제거한다.
mov ax,2513h
mov dx,word ptr es:old_13h
mov ds,word ptr es:old_13h[2]
int 21h
mov ax,2560h
mov dx,word ptr es:old_60h
mov ds,word ptr es:old_60h[2]
int 21h
pop ds
mov ah,49h
int 21h
mov dx,offset removed_msg
mov ah,9
int 21h
int 20h
install: ;프로그램을 인스톨한다.
mov ah,0fh ;현재의 비디오모드를 검사하고
int 10h ;모노크롬이면 color_m 로
;점프한다.
cmp al,7
jne color_m
hooking:
mov ax,3560h ;인식자를 위한 사용자
int 21h ;인터럽트를 후킹한다.
mov word ptr old_60h,bx
mov word ptr old_60h[2],es
mov ax,2560h
mov dx,offset new_60h
int 21h
mov ax,3513h ;디스크 인터럽트 후킹
int 21h
mov word ptr old_13h,bx
mov word ptr old_13h[2],es
mov ax,2513h
mov dx,offset new_13h
int 21h
mov ah,49h
mov es,cs:[2ch]
int 21h
mov ah,9
mov dx,offset install_msg
int 21h
mov ah,31h ;메모리 상주 종료.
mov dx,(install_msg-start+256+15)/16
int 21h
color_m: ;모노크롬 모니터를 위한
add cs:video_buffer,800h ;비디오 버퍼 설정
jmp hooking
code ends
end start
[32] 제목 : [참고] 어셈블리 참고 책들..
올린이 : onestep (류창균 ) 95/02/18 13:19 읽음 : 325 관련자료 없음
Fri 29 Jan 93 8:34
By: Yousuf Khan
To: All
Re: FAQ: Books on Assembler
------------------------------------------------------------------------
Q2) I want to learn assembler, what books should I read?
***
A2) Various people have found these books useful to them:
General
=======
Beginner:
---------
-"Assembly Language from Square One", Jeff Duntemann, Scott Foresman IBM
Comptuter Books. ISBN 0-673-38590-6.
-"Assembly Language for the IBM PC", Kip R. Irvine, ISBN 0-02-359840-9
-"Mastering Turbo Assembler", by Tom Swan, Hayden Books, 1989.
ISBN 0-672-48435-8.
-"Assembly Language and Systems Programming for the IBM PC and
Compatables", Karen A. Lemone, Little, Brown, & Co. ISBN 0-316-52069-1.
-"Assembly Language Primer for the IBM PC/XT", Robert Lafore,
Plume/Waite.
-"Using Assembly Language", Allen L. Wyatt Sr., Que 1990.
ISBN 0-88022-464-9.
Intermediate:
-------------
-"The Zen of Assembly", Michael Abrash, Scott Foresman Publ.
-"Assembly Language Primer for the IBM PC/XT"
-"IBM Microcomputers: A Programmer's Handbook", Julio Sanchez and Maria
P. Canton, McGraw-Hill. ISBN 0-07-054594-4.
-"Programmer's Problem Solver for the IBM PC, XT, and AT", Robert
Jourdain, Prentice Hall 1986. ISBN 0-89303-787-7.
-"IBM PC ASSEMBLER LANGUAGE AND PROGRAMMING", Peter Abel, 1987,
Prentice-Hall, hardcover (college text). ISBN 0-13-448143-7.
Advanced:
---------
-"80386: A Programming and Design Handbook", 2nd ed., Penn & Don Brumm,
TAB Books. ISBN 0-8306-3237-9.
-"80486 Programming", Penn & Don Brumm and Leo J. Scanlon, McGraw-Hill.
ISBN 0-8306-3577-7.
-"ADVANCED ASSEMBLY LANGUAGE", Steven Holzner and Peter Norton Computing,
Inc., Brady Books/Simon and Schuster. ISBN 0-13-658774-7.
Video Graphics
==============
Intermediate:
-------------
-"Programmer's Guide to PC & PS/2 Video Systems", Richard Wilton,
Microsoft Press. ISBN 1-55615-103-9.
Advanced:
---------
-"Power Graphics Programming", Michael Abrash, Que Corporation. ISBN
0-88022-500-9
-"Programmers Guide to the EGA and VGA cards", 2nd Ed., Richard F.
Ferraro, Addison-Wesley Publishing Co. ISBN 0-201-57025-4.
-"Advanced Programmers Guide to the EGA/VGA", George Sutty and Steve
Blair, Brady Books/Prentice Hall Trade. ISBN 0-13-729039-X.
References/Specialized
======================
Intermediate:
-------------
-"Undocumented DOS", Andrew Schulman, Raymond J. Michels, Jim Kyle, Tim
Paterson, David Maxey, and Ralf Brown, Addison-Wesley. ISBN
0-201-57064-5.
-"DOS Programmer's Reference", 2nd Edition, Terry Dettmann, QUE.
ISBN 0-88022-458-4.
Advanced:
---------
-"386SX Microprocessor Programmer's Reference Manual", Intel Corp.,
McGraw-Hill. ISBN 0-07-881673-4.
-"i486 Microprocessor Programmer's Reference Manual", Intel Corporation,
McGraw-Hill. ISBN 0-07-881674-2.
-"The Programmer's PC Sourcebook", Thom Hogan, Microsoft Press. ISBN
1-55615-321-X.
-"System BIOS for IBM PCs, Compatables, and EISA Computers", 2nd Ed.,
Phoenix Technologies Ltd., Addison Wesley. ISBN 0-201-57760-7.
-"PC Magazine Programmers Technical Reference: The Processor and
Coprocessor", Robert L. Hummel, Ziff-Davis Press. ISBN 1-56276-016-5.
-"Mastering Serial Communications", Peter W. Gofton, Sybex 1986. ISBN
0-89588-180-2.
-"DOS Programmer's Reference", 2nd Ed.
-"MS-DOS Programmer's Reference", MS Press. ISBN 1-555615-329-5.
from CG.
[33] 제목 : 스택(STACK)에 관한 연구
올린이 : 영원의별(이세원 ) 95/03/22 23:11 읽음 : 135 관련자료 없음
스택 (Stack)
1. 스택이란.
스택은 프로그램에서 사용하는 조금 특별한 데이타를 저
장하는 아주 특별한 영역이다. 그 영역은 스택 세그먼트이고 스
택 세그먼트 레지스터가 그곳의 위치를 가리킨다.
스택이라는 이름은 식당에서 접시를 쌓아두는 장치에서
유래했다고 한다. 이 장치의 특징을 한가지 예로 들어 설명하겠
다. 커피 전문점에 가면 커피잔을 전기열로 말리는 장치가 있다.
이곳에 커피잔을 씻어 올려 놓는다. 그리고 다음에 씻은잔은 먼저
올려놓은 잔 위에 올려놓는다. 이런 동작들을 반복한다면 가장 먼
저 씻은잔이 가장 밑에 놓여 있게 된다. 반대로 이번에는 손님이
와서 커피잔이 나가게 되었을때는 맨 위에 놓인잔이 가장 먼저 손
님에게 나가게 된다. 물론 이 방법은 손님에게는 합리적이지 못하
다. 맨 밑의 잔이 손님에게 나간다면 그 잔은 얼마나 먼지를 덮어
쓰고 있는지 종업원 조차 알수 없기 때문이다.
이런 식으로 가장 먼저 들어온 데이타가 가장 늦게 나가
는 다시말해 가장 최근에 저장된 데이타가 가장 먼저 사용되는 그
런 형태이다. 이러한 구조를 가지고 있으므로 당연히 가장 최근에
들어온 데이타의 위치를 알수 있는 지시자를 가지고 있다. 그것이
바로 스택 포인터이다. 스택 포인터는 스택 영역에서 다음에 저장
할 위치를 가리키고 있다. 즉 PUSH 를 하게 되면 현재의 스택 포
인터가 가리키는 위치에 데이타가 저장되는것이다. 스택에 데이타
를 저장했으면 다음에 저장할 데이타의 스택에서의 위치를 알려주
기 위해서 스택 포인터의 값은 2 감소한다. 스택에서 데이타를 꺼
내는 명령은 POP 이다. 또한 POP 명령으로 데이타를 거낸후에는
스택 포인터는 2 증가한다. 2씩 감소하고 2씩 증가하는 이유는 스
택에는 워드단위(2 BYTE)로 저장하기 때문이다.
2. 스택을 만든 이유
일반적으로 데이타는 별도의 데이타 영역을 두어 그곳에
저장한다. 스택에 저장할 이유가 없다. 그러나 스택에 저장을하면
여러가지 이득을 볼 수 있다. 프로그램에는 프로그램을 종료할때
까지 보존해야할 데이타가 있는가 하면 일시적으로 사용하고는 버
리는 데이타가 있다. 이런 일시적인 데이타조차 메모리의 데이타
영역에 보존한다면 메모리를 낭비하는 결과를 초래할수도있다. 따
라서 이런 데이타는 스택에 저장한다. 또한 스택을 이용한 데이타
의 이동은 매우 효과적이다. PUSH 와 POP 은 한쌍으로 쓰여서 데
이타를 아주 빠르게 복사할수 있다.
고급언어의 경우 전자의 경우 즉 일시적으로 쓰이는 데이
타를 스택에 저장하는 것은 필수적이다. 고급언어는 구조상 라이
브러리를 써야만하고 이과정에서 프로그램에 소용되는 변수의 갯
수가 많아진다. 그런 이유로 지역변수는 스택을 이용한 조작이 불
가피하기 때문이다. 그러나 어셈블리어는 데이타의 이용이란 측면
에서는 아주 자유롭다. 프로그래머가 데이타의 저장되는 위치와
그 양을 모두 알기 때문이다. 반면에 고급언어에서는 그것을 아는
것이 거의 불가능하다.
**************************************************************
주석)
지역변수 (Local variable, Internal variable)
지역변수는 하나의 독립된 서브 프로그램 내에서만 쓰는 변수를
말한다. 그러므로 이 변수는 서브 프로그램의 종료와 함께 제거된
다.
전역변수 (Global variable, External variable)
전역 변수는 주 프로그램 서브 프로그램 모두 사용할수 있는 변수
를 말한다. 따라서 전체 프로그램이 종료할때가지 메모리에 남아
있는다.
* 위의 두개념은 고급언에서만 존재한다.
**************************************************************
3. 스택 포인터의 감소
위에서 잠깐 언급한 스택 포인터의 값이 감소하는 이유는
무엇일까 ? 스택에 데이타가 저장될때마다 메모리에서의 번지는
오히려 2바이트씩 감소한다. 정상적인 경우 즉 메모리에 데이타가
저장되는 경우에 메모리의 번지는 증가해야함에도 불구하고 스택
에서는 반대의 현상을 보인다. 그 이유는 프로그램의 형태와 무관
하지 않을 것이다. 컴퓨터에서 실행화일이라 부르는 화일은 확장
명이 COM 아니면 EXE 이다. 즉 컴퓨터는 확장명이 COM 또는 EXE
이면 무조건 메모리에 로드하고 실행해버린다. 이중 COM 프로그램
이 스택과 관련이 깊다. 앞에서도 언급이 되었지만 COM 형의 프로
그램에는 세그먼트 재배치에관한 헤더가 없다. 다시말해 .COM 형
의 프로그램은 하나의 세그먼트만을 사용하고 그에 따라서 프로그
램과 관계되는 네개의 세그먼트가 시작하는곳이 모두 같은 번지이
다. 그런데 임시적인 데이타를 스택에 저장한다고하여 스택 세그
먼트의 시작점에서부터 데이타를 저장한다면 프로그램 코드와 데
이타가 들어있는 메모리에 모두 '덮어써버리는' 결과를 초래하고
말것이다. 물론 그렇게 되면 프로그램은 엉망이 되어버린다. 그러
므로 프로그램에서 사용하는 한정된 64KB 의 메모리 영역중에 그
래도 비교적 사용가능성이 드문 64 KB 의 끝부분 부터 데이타를
저장해 내려온다면 프로그램의 코드나 데이타와 중복 사용될 가능
성이 적어질것이다. 이런 연유에서 스택 포인터는 프로그램이 시
작할때부터 스택 세그먼트의 가장 끝부분을 가리키고 있는것이다.
그러나 만약 프로그램의 크기가 커서 거의 64 KB 의 끝부분까지
도달한다면 문제는 달라진다. 이경우에는 프로그램에서 저절로 설
정되는 스택 포인터를 사용하면 않되고 별도의 스택 영역을 확보
하고 스택 포인터도 이에 맞게 고쳐써야 할것이다.
4. 스택의 호출번지 저장
스택의 데이타 저장구조 즉 가장 늦게 저장된 데이타가
가장먼저 사용되는 구조를 이용해 서브 프로그램의 호출시에 호출
한 프로그램에로의 복귀번지를 저장하는데 사용한다. 즉 현재의
프로그램에서 서브 프로그램을 호출하는 경우에 호출하려는 서브
프로그램이 현재의 세그먼트 내에 있으면 IP 값만 저장하고 현재
의 세그먼트 밖에 있으면 CS 와 IP 의 값을 스택에 저장한다.
이러한 스택에 저장하는 방법은 생각해보면 합리적이다.
서브 프로그램으로 제어가 넘어가기전에 현재의 CS 와 IP 의 값을
만약에 데이타 영역에 저장했다고하고 제어를 서브 프로그램으로
넘겼다고 하자. 그리고 서브 프로그램의 수행을 마치고 본래의 프
로그램으로 돌아가려 한다면 어떻게 CS 와 IP 를 저장한 번지를
찾을수 있을까 ? 물론 서브 프로그램이 어셈블리어로 제작된다면
별로 문제 될것은 없다. 어셈블리어는 데이타에 관한한 자유롭기
때문에 프로그래머가 데이타의 위치를 임의로 설정 하는것이 가능
하다. 그러나 고급언어에서는 문제가 전혀 다르다. 일단 고급언어
로 작성된 프로그램내로 들어오게되면 메모리의 번지를 알아내는
것이 결코 쉽지않다. 많은 라이브러리가 결합된 형태의 고급언어
프로그램은 프로그래머 조차 레지스터의 움직임을 알 수 없기 때
문이다. 그러나 이러한 프로그램도 스택 영역은 변함이 없으므로
(스택은 하나이므로) 이곳에 데이타를 저장한다면 안전할것이다.
마찬가지로 메인 프로그램에서 서브 프로그램으로 인수를 전달할
때에도 스택을 이용한다. 이것은 일종의 약속이다. 즉 언어를 막
론하고 스택의 영역은 변함이 없으므로 이곳을 통해 데이타를 교
환한다는것이다. 물론 반대로도 해석이 가능하다 '프로그램과 프
로그램간의 데이타 교환은 스택을 이용한다. 그러므로 스택 영역
은 변경하지 않는다.' 스택에 관하여 마치 닭이 먼저냐 알이 먼저
냐 하는식의 얘기가 되었으나 결론은 분명하다. 즉 '프로그램간의
데이타 교환이나 호출한 프로그램에로의 복귀번지는 스택에 저장
한다' 하는것이 약속이라는 것이다.
FFFFH +--------------------+
| 0H |
FFFEH +--------------------+ 최초의 SP
| DATA 1 |
FFFCH +--------------------+
| DATA 2 |
FFFAH +--------------------+
| DATA 3 |
FFF8H +--------------------+
| DATA 4 |
FFF6H +--------------------+ 현재의 SP
| |
| |
.COM 형의 스택
최초의 SP 는 FFFEH 이고 SS 의 값은 CS 와 동일하다. 이
것은 .COM형의 프로그램은 하나의 세그먼트 값만을 가질수 없다는
이유에서 비롯된것이다. 그후 스택에 데이타가 하나씩 저장 될 때
마다 SP 의 값은 2 바이트 씩 감소하여 FFFCH,FFFAH,FFF8H 로 떨어
졌으며 데이타가 4개 저장될때 FFF6H 의 값을 가지게 되었다.
0000H +--------------------+ 최초의 SP
| DATA 1 |
FFFEH +--------------------+
| DATA 2 |
FFFCH +--------------------+
| DATA 3 |
FFFAH +--------------------+
| DATA 4 |
FFF8H +--------------------+ 현재의 SP
| |
| |
스택영역을 설정하지 않은 .EXE 형의 스택
최초의 SP 값은 0000H 이고 데이타가 하나씩 저장될때마다
역시 2바이트씩 감소한다. 0000H 에서 FFFEH 로 바뀌는 것에 의아
해 할수도 있다. 그 이유는 다음과 같다. .EXE 형의 프로그램이
시작될때 이때는 SS와 CS의 값은 같다. 따라서 SS:SP 는 PSP:100
H 를 가리키고 있다. 그러나 데이타가 스택에 저장되기 전에 SS
의 값은 다시 설정되고 그후부터는 정상적으로 SP는 SS 의 끝에
서부터 내려로기 때문에 어느 세그먼트의 영역과도 겹치지 않는다.
[34] 제목 : [소스] 보호모드 진입코드
올린이 : yunix (유경상 ) 95/03/25 02:41 읽음 : 160 관련자료 없음
안녕하세요 yunix 입니다.
제가 32비트 프로그래밍 강좌를 하고 있는데.....
과연 386 보호모드로 진입하기 위해서는 어떠한 일들을 해야 하는 지
궁금해 하는 분들이 있으신것 같아 여기 간단한(?) 프로그램을 올려봅니다.
소스가 약간 길군요. 잘 분석해 보시고 질문을 하시면 성의껏 답변해 드리겠읍니다.
스스로 부딛혀보고 생각해 보는 것이 실력향상에 도움이... :)
참고로 Loner는 제 call-sign 입니다.
컴파일러는 tasm을 썻구요. 아마 masm으로도 되리라 믿습니다.
링크하실때는 /3 옵션을 꼭 주셔야 합니당...
주석이 모두 영어군요... 짭...
제 콩글리쉬 실력을 마음껏 감상하십시요. :)
( 제길... 영어를 10년 넘게 공부했는데 ... 이게 뭐야 !
우리나라 교육.... 문제야 ! )
---------------------------------------------------------------
;*
;* File : enter.asm
;*
;* enter.asm : entering protected mode code.
;*
;* This code is used in [booting]. It will set up gdt and idt, and
;* print some message.
;* Once entered protected mode, we cannot go back to dos because we
;* destory gdt,idt etc., which is used by dos memory manager like
;* EMM386, QEMM386, 386MAX ....
;* After execution this, you should power down your computer.
;* This NEVER harm your computer.
;*
;* [Extention]
;* I will write a new code that read gnu program in real mode and enter
;* protected mode using this code for running the gnu program.
;* the gnu program is written with djgpp or gpp on the linux, which
;* is allowed no dos function call, no bios call. This will be primitive
;* operating system of mine. Isn't it wonderful?
;*
;* Assembler : any assembler compatiable with MASM 5.0
;*
;*
;* Copyright (C) 1994, Yu, Kyoung Sang
;*
;* Wed 11-02-1994 02:07:30 written by Loner
;*
.386p
text segment use16
assume cs:text,ds:data,ss:stk
;*
;* Let's DO IT !!!!
;*
enter_prot:
;*
;* check if operation mode is protected mode.
;*
smsw ax
and ax,1
jz ready_to_enter
;*
;* hum... operation mode is already protected mode. memory manager like
;* EMM386 or QEMM may control 386. if we touch cr0(msw),idt, or gdt to enter
;* protected mode, the memory manager will complain and kill our program.
;* let's find other way. how about go back to real mode ?
;* but it need touch cr0(msw) and it is not allowed.
;* when processor is reset, it will operate in real mode. ok... let's reset
;* processor ! and jump to ready_to_enter. how ?
;* there is back door to go back in real mode. write 5 or 10 to shutdown
;* status byte of CMOS and set seg:off to jump at 040:0067 after reset.
;* then, when processor is reset, POST of BIOS will jump to address
;* where we write at 0040:0067. How funny it is !
;*
;* NOTE :
;* after reset, you may not use some interrupt which memory
;* manager have interceptted.
;*
cli
mov al,0fh ; offset of shutdown status byte of CMOS
out 70h,al ; write offset
mov al,10 ; after reset, jump where 40:67 point.
out 71h,al
mov ax,40h ; set magic address(40:67)
mov es,ax
mov bx,67h
mov ax,offset ready_to_enter
mov es:[bx],ax ; write address to jump after reset
mov ax,cs
mov es:[bx+2],ax
mov cx,10 ; for safe reset, repeat 10 times
_reset:
mov al,0feh ; reset cmd ....
out 64h,al ; reset !!
call wait_8042 ; program control will never reach here
__die: jmp __die
;*
;* now.... do some initialization to enter...
;*
ready_to_enter:
mov ax,data
mov ds,ax
;*
;* disabled interrupt will not be enabled until everything(idt) is ready.
;*
cli
mov ax,stk
mov ss,ax
mov sp,offset stk_ptr
;*
;* ok. segment initialzation is done. now, let's disable NMI.
;*
mov al,80h ; disable NMI
out 70h,al
;*
;* all right. let's enable A20 line.
;* if A20 line is not enabled, we cannot access high memory area (10000h)
;*
call wait_8042 ; wait until 8042 is ready to read
mov al,0d1h ; d1h = write a byte to output port
out 64h,al
call wait_8042 ; wait until 8042 reads a command
mov al,0dfh ; Gate A20 enable !
out 60h,al
call wait_8042 ; wait until 8042 reads a data
;*
;* now, set gdt pointer.
;* gdt base is needed in format of 32bit linear address. so we should change
;* 20bit address of gdt to 32bit linear address.
;* save 32bit data segment pointer for later use (message ouput)
;*
mov ax,ds
shl ax,4
mov bx,ax
mov ax,ds
shr ax,12
push ax
push bx ; ax:bx=32bit data segment base
add bx,offset gdt
adc ax,0
mov word ptr gdt_ptr+2,bx
mov word ptr gdt_ptr+4,ax
;*
;* set far jump address as 32bit form into si,di register
;*
mov ax,text_32
shl ax,4
mov bx,ax
mov ax,text_32
shr ax,12
add bx,offset start_32
adc ax,0 ; ax:bx => 32 bit linear pointer
mov cs:jmp_addr,bx
mov cs:jmp_addr+2,ax
;*
;* we shoud save stack pointer as 32bit form. because once entering protected
;* mode, stack pointer should be initialized again.
;*
mov ax,ss
shl ax,4
mov dx,ax
mov ax,ss
shr ax,12
add dx,sp
adc ax,0
mov cx,ax
;*
;* load gdt and idt onto descriptor table registers (gdtr,idtr)
;*
lidt pword ptr idt_ptr
lgdt pword ptr gdt_ptr
;*
;* ok. everythings are ready. now we are entering protected mode !
;*
mov ax,1 ; set proctected mode bit
lmsw ax ; now !!!
jmp $+2 ; flush instruction prefetch queue.
;*
;* now, we are in protected mode. before do somthing, initialzing segment
;* register and do far jump to change cs register
;*
mov bx,18h
mov ds,bx
mov ss,bx
mov es,bx
db 66h,0eah ; jmp far 10h:start_32
jmp_addr dw 0h
dw 0h
dw 10h
;*
;* if input buffer is full, wait until 8042 reads from it.
;*
wait_8042: in al,064h ; read 8042 status register
test al,02h ; 8042 reads a byte from input buffer?
jnz wait_8042
ret
text ends
.386p
text_32 segment use32
assume cs:text_32
;*
;* OK... we have succeeded !
;* you can do somthing in protected mode!
;* I will just output 'Hello ...' on the screen.
;*
start_32:
mov ax,cx ; cx:dx= 32bit stack pointer
shl eax,16
mov ax,dx
mov esp,eax
;*
;* foolish check is done. check that A20 is REALLY enabled.
;*
xor eax,eax
mov ebx,0
mov ecx,100000h
check_a20: inc eax
mov ebx,eax
cmp eax,ecx
jz check_a20
;*
;* clear screen
;*
mov ecx,80*25 ; clear screen
mov ax,0820h ; blank with black backgournd
mov ebx,0b8000h ; text vram address
clear_screen: mov [ebx],ax
inc ebx
inc ebx
loop clear_screen
;*
;* output message. message is in data segment. so we needs 32bit pointer of
;* message. we have already saved 32bit pointer of data segment on the stack.
;*
pop esi ; get 32bit data segment base
mov eax,0 ; clear upper word
mov ax,offset data:message ; message offset
add esi,eax ; esi<- 32bit data segment
mov ebx,0b8820h+54 ; screen position
mov ah,1eh ; yellow/blue attribute
output_msg: mov al,[esi]
or al,al ; null character ?
jz end_msg
mov [ebx],ax ; output character
inc esi ; next pointer
inc ebx ; next screen position
inc ebx
jmp output_msg
end_msg:
;*
;* everything is done. we can go back to real mode but
;* we can't go back to dos. so...
;*
die: jmp short die ; die...
text_32 ends
data segment use16
gdt_ptr dw 4*8-1 ; limit of gdt
dw 0,0
idt_ptr dw 0,0,0 ; empty idt
gdt dw 0,0,0,0 ; null descriptor
dw 0,0,0,0 ; unused
dw 07ffh ; limit 8MB ((2047+1)*4096)
dw 0000h ; base = 00000000h
dw 9a00h ; present,dpl=0,exec/read code segment
dw 00c0h ; granularity=4KB, 32bit
dw 07ffh ; limit 8MB
dw 0000h ; base = 00000000h
dw 9200h ; present,dpl=0,read/write data segment
dw 00c0h ; graularity=4KB, 32bit
message db 'Hello 386 world !'
db 0
data ends
stk segment use16
stack_area db 512 dup (0) ; stack area
stk_ptr label
db 16 dup (0) ; for dummy byte
stk ends
end
[38] 제목 : MASM 6.1의 조건문 정리.
올린이 : 영원의별(이세원 ) 95/04/22 18:26 읽음 : 64 관련자료 없음
MASM 6.1 의 조건문 입니다.
이 조건문들을 통해서 고급언어 흉내를 낼 수 가 있지요.
예전에 만들어 두었던 것인데 그때가 그립군요.
이제 다시는 프로그램 만들기가 쉽지 않을듯 합니다.
프린터로 뽑아서 고이 간직하세요...
MASM 6.x 는 고급언어에서나 볼 수 있었던 여러가지 조건문을 사용할 수 있게
되었다. 블럭 구조의 이들 조건문의 연산자는 다음과 같다.
┌────────┬──────────────────┐
│ 연산자 │ 의미 │
├────────┼──────────────────┤
│ == │ 같으면 │
│ != │ 같지 않으면 │
│ > │ 크면 │
│ >= │ 크거나 같으면 │
│ < │ 작으면 │
│ <= │ 작거나 같으면 │
│ & │ BIT TEST │
│ ! │ NOT │
│ && │ AND │
│ || │ OR │
└────────┴──────────────────┘
1 .IF,.IFELSE,.ELSE,.ENDIF
이 블럭 구조문은 .IF 에서 시작하여 .ENDIF 에서 끝난다. 따라서 조건을 만족
했으면 .ENDIF 이후의 명령을 수행하게된다.
형식)
.IF 조건식
명령어
.ELSE
명령어
.ENDIF
먼저 조건을주고 조건에 맞으면 다음 문장을 수행하고 빠져나간다. 그러나 조건과
맞지 않으면 .ELSEIF 이후의 문장을 수행하고 빠져나간다.
.IF 조건식
명령어
.ELSEIF
명령어
.ELSEIF
명령어
.ELSEIF
명령어
.
.
.
.ENDIF
이 구조식도 위와 위와 별 차이가 없다. 먼저 조건을 비교하고 만족하면 밑의
명령을 수핸하고 빠져나가고 만족하지 않으면 다음 조건과 비교한다. 다음조건
과 비교해서 만족하면 밑의 명령을 수행하고 빠져나가고 만족하지 않으면 다음
조건과 비교한다.....
code segment
assume cs:code
org 100h
start:
mov cx,100
.if cx==10 ;─── ①
mov ah,9 ;─┐
mov dx,offset msg1 ; ├─ ②
int 21h ;─┘
.else ;─── ③
mov ah,9
mov dx,offset msg2
int 21h
.endif ;─── ③
mov ah,4ch ;─── ⑤
int 21h
msg1 db 'msg1$'
msg2 db 'msg2$'
code ends
end start
① 만약 CX 가 10이면 ② 를 수행하고 그렇지 않다면 ③으로 간다.
② 'msg1' 을 출력하고 ⑤ 로 간다.
③ 'msg2' 를 출력하고 ⑤ 로 간다.
④ 이 블럭 구조문의 끝
⑤ 프로그램을 종료한다.
이 프로그램을 어셈블하여 디버깅을 해본다.
C:\MASM61\BIN>debug sam2.com
-u
186F:0100 B96400 MOV CX,0064
186F:0103 83F964 CMP CX,+64
186F:0106 7509 JNZ 0111
186F:0108 B409 MOV AH,09
186F:010A BA1C01 MOV DX,011C
186F:010D CD21 INT 21
186F:010F EB07 JMP 0118
186F:0111 B409 MOV AH,09
186F:0113 BA2101 MOV DX,0121
186F:0116 CD21 INT 21
186F:0118 B44C MOV AH,4C
186F:011A CD21 INT 21
186F:011C 6D DB 6D
186F:011D 7367 JNB 0186
186F:011F 3124 XOR [SI],SP
-
막상 디버깅을 해보명 역시 우리의 소스 프로그램과 약간 다르게 기계어로
번역 되었음을 알 수 있다. 하지만 우리가 의도했던대로의 결과를 나타내고 있다.
2 .WHILE,.ENDW
이 블럭 구조문은 .WHILE 에서 시작하여 .ENDW 로 끝난다.
먼저 조건을 주고 그 조건이 만족될 때까지 밑의 명령을 반복 수행한다.
형식) .WHILE 조건식
명령어
.ENDW
code segment
assume cs:code
org 100h
start:
mov dx,10 ;①
.while dx > 0 ;②
dec dx ;③
.endw ;④
mov ah,4ch
int 21h
code ends
end start
① DX 에 10을 넣는다.
② DX 가 10보다 큰 동안 밑의 명령을 수행한다.
③ DX 의 값을 1 감소한다.
④ .WHILE 블럭 구조문의 끝
다음은 이 프로그램을 어셈블링하여 디버깅한것이다.
C:\MASM61\BIN>debug sam3.com
-u
186F:0100 BA0A00 MOV DX,000A
186F:0103 EB01 JMP 0106 ─┐
186F:0105 4A DEC DX ├─ ①
186F:0106 83FA00 CMP DX,+00 │
186F:0109 77FA JA 0105 ─┘
186F:010B B44C MOV AH,4C
186F:010D CD21 INT 21
186F:010F 7C4E JL 015F
186F:0111 8700 XCHG AX,[BX+SI]
186F:0113 004E92 ADD [BP-6E],CL
186F:0116 4E DEC SI
186F:0117 9D POPF
186F:0118 4E DEC SI
186F:0119 A84E TEST AL,4E
186F:011B B34E MOV BL,4E
186F:011D BE4EC9 MOV SI,C94E
-
① 의 내용이 .WHILE 블럭 구조문에 의해 생성된 기계어 코드이다.
3 .REPEAT,.UNTIL,.UNTILCXZ
형식) .REPEAT
명령어
.UNTIL 조건식
.UNTIL 의 조건이 만족 될때까지 .REPEAT 밑의 명령을 반복 수행한다.
.REPEAT
명령어
.UNTILCXZ
.UNTIL 조건식
.UNTIL 의 조건이 만족 될때까지 .REPEAT 밑의 멸영을 반복수행 하다가 만약
CX 의 값이 0이되면 빠져나간다.
code segment
assume cs:code
org 100h
start:
.repeat ----①
inc si
.until si == 10 ----②
mov cx,10 ----③
.repeat ----④
inc di
dec cx ----⑤
.untilcxz ----⑥
mov ah,4ch
int 21h
code ends
end start
① .REPEAT 블럭문의 시작
② SI 가 10 이 뚜때까지 반복 수행한다.
③ CX 의 초기값으로 10을 넣는다.
④ .REPEAT 블럭문의 시작
⑤ CX 의 값을 1 감소 시킨다.
⑥ 만약 CX 의 값이 0이면 빠져 나간다.
C:\MASM61\BIN>debug sam4.com
-u
186F:0100 46 INC SI ──┐
186F:0101 83FE0A CMP SI,+0A ├─ ①
186F:0104 75FA JNZ 0100 ──┘
186F:0106 B90A00 MOV CX,000A ──┐
186F:0109 47 INC DI ├─ ②
186F:010A 49 DEC CX │
186F:010B E2FC LOOP 0109 ──┘
186F:010D B44C MOV AH,4C
186F:010F CD21 INT 21
186F:0111 8700 XCHG AX,[BX+SI]
186F:0113 004E92 ADD [BP-6E],CL
186F:0116 4E DEC SI
186F:0117 9D POPF
186F:0118 4E DEC SI
186F:0119 A84E TEST AL,4E
186F:011B B34E MOV BL,4E
186F:011D BE4EC9 MOV SI,C94E
-
① .REPEAT 문에 의해서 SI 가 10이 될때까지 반복한다.
② .REPEAT 문에 .UNTILCXZ 가 삽입된 관계로 LOOP 명령어가 출현하였다.
4 .BREAK,.CONTINUE
.BREAK 은 .WHILE/.ENDW 나 .REPEAT/.UNTIL/.UNTILCXZ 문의 루프를 도중에
빠져나오게 하고 .CONTINUE 은 .WHILE/.ENDW 나 .REPEAT/.UNTIL/.UNTILCXZ 문의
루프를 계속 실행시키게한다.
형식)
.BREAK
.BREAK .IF 조건식
.CONTINUE
.CONTINUE .IF 조건식
code segment
assume cs:code
org 100h
start:
.while dx!=100 ---①
inc ax
.if ax==100 ---②
.break ---③
inc si
.endif
inc dx
.endw
mov ah,4ch
int 21h
code ends
end start
① DX 가 100이 아닌동안 밑의 명령을 반복 수행한다.
② 만약 AX 가 100 이면 밑의 명령을 수행한다.
③ 이 명령에 의해 .WHILE 블럭 루프문을 완전히 빠져나간다.
C:\MASM61\BIN>debug sam5.com
-u
1A7C:0100 EB0A JMP 010C
1A7C:0102 40 INC AX
1A7C:0103 83F864 CMP AX,+64
1A7C:0106 7503 JNZ 010B
1A7C:0108 EB07 JMP 0111 ----①
1A7C:010A 46 INC SI
1A7C:010B 42 INC DX
1A7C:010C 83FA64 CMP DX,+64
1A7C:010F 75F1 JNZ 0102
1A7C:0111 B44C MOV AH,4C
1A7C:0113 CD21 INT 21
1A7C:0115 92 XCHG DX,AX
1A7C:0116 4E DEC SI
1A7C:0117 9D POPF
1A7C:0118 4E DEC SI
1A7C:0119 A84E TEST AL,4E
1A7C:011B B34E MOV BL,4E
1A7C:011D BE4EC9 MOV SI,C94E
-
① .BREAK 문이 나타난 명령코드이다.
[40] 제목 : [소스] 부팅시간을 기록하는 bootlog.asm
올린이 : 영원의별(이세원 ) 95/04/22 19:20 읽음 : 45 관련자료 없음
;┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
;┃ bootlog.asm ┃
;┃ 날짜및 시각을 화일에 순차로 입력하는 프로그램. ┃
;┠────────────────────────────────────┨
;┃ 자기 컴퓨터의 부팅시간을 기록하는 프로그램은 수 없이 많습니다. ┃
;┃ 하지만 이 프로그램 만큼 작고 그리고 빠르게 기록하는 ┃
;┃ 프로그램은 없을 겁니다. ┃
;┃ 왜냐구요? 어셈블리로 만들었으니까요... ┃
;┃ 어셈블리를 공부하시는 분에게 도움이 되길 빌며... ┃
;┃ 앞으로 가끔 그동안 제가 만들었던 어셈유틸의 소스를 올리겠습니다. ┃
;┃ 마지막으로 .EXE 형의 프로그램입니다. ┃
;┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
dosseg
data segment
year dw ?
month_date dw ?
a_day dw ?
time dw ?
second dw ?
temp_1 db ?
temp_2 db ?
temp_3 db ?
temp_4 db ?
now_year db 4 dup('y')
db '.'
now_month db 2 dup('m')
db '.'
now_date db 2 dup('d')
db ' '
now_time db 2 dup('t')
db ':'
now_minute db 2 dup('m')
db ':'
now_second db 2 dup('s')
db 13,10
write_error_msg db 'Write error.$'
open_error_msg db 'Open error.$'
pointer_error_msg db 'Pointer error.$'
path_name db 'c:\bootlog',0 ;화일이름(아스키즈 문자열)
handle dw ?
data ends
stack segment stack
db 100h dup(0)
stack ends
code segment
assume cs:code,ds:data,ss:stack
start:
mov ax,data
mov ds,ax
push ds ;ES 를 DS 에 맞춘다.
pop es
;---------------------
;날짜및 요일 읽어내기
;---------------------
read_date:
mov ah,2ah ;년,월,일을 구한다.
int 21h
mov year,cx ;year
mov month_date,dx ;month & date
mov ax,year
call div_1 ;나누기 수행
mov cx,4 ;year
mov si,offset temp_1 ;년도를 저장한다.
mov di,offset now_year
rep movsb
mov ax,month_date
call div_2 ;나누기 수행
mov cx,2 ;month
mov si,offset temp_1 ;월을 저장한다.
mov di,offset now_month
rep movsb
mov cx,2 ;date
mov si,offset temp_3 ;일을 저장한다
mov di,offset now_date
rep movsb
;----------------
; 시각 읽어내기
;----------------
mov ah,2ch ;시각 구하기
int 21h
mov time,cx ;time
mov second,dx ;second
mov ax,time
call div_2 ;나누기 수행
mov cx,2 ;시간저장
mov si,offset temp_1
mov di,offset now_time
rep movsb
mov cx,2 ;분저장
mov si,offset temp_3
mov di,offset now_minute
rep movsb
mov ax,second
call div_2 ;나누기 수행
mov cx,2 ;초저장
mov si,offset temp_1
mov di,offset now_second
rep movsb
;---------------------
; 화일 오픈 및 쓰기
;---------------------
file_open:
mov ah,3dh ;화일오픈
mov dx,offset path_name ;화일명
mov al,1 ;쓰기전용
int 21h
jc file_creat ;오픈에 실패하면 화일을 만든다.
mov handle,ax ;핸들을 저장한다.
mov ah,42h ;화일 포인터의 설정
mov al,2 ;이동 오프셍을 화일의
;끝에다 더한다.
mov bx,handle ;핸들번호
mov cx,0 ;이동 바이트 상위 값
mov dx,0 ;이동 바이트 하위 값
int 21h
jc pointer_error ;캐리발생하면 포인터에라
mov ah,40h ;화일쓰기
mov dx,offset now_year ;출력버퍼 번지
mov cx,22 ;써넣을 바이트수
mov bx,handle ;핸들번호
int 21h
jnc file_close ;캐리가 발생안하면
;화일을 닫아라.
mov ah,9 ;쓰기에라 메시지출력
mov dx,offset write_error_msg
int 21h
jmp file_close
file_creat:
mov ah,3ch ;화일생성
mov dx,offset path_name ;화일명
mov cx,0 ;정상속성
int 21h
jnc file_open ;캐리가 발생안하면 오픈하라
mov ah,9 ;오픈에라 메시지 출력
mov dx,offset open_error_msg
int 21h
jmp exit
pointer_error:
mov ah,9 ;포인터 에라 메시지출력
mov dx,offset pointer_error_msg
int 21h
file_close:
mov ah,3eh ;화일닫기.
mov bx,handle ;핸들번호
int 21h
exit:
mov ah,4ch
int 21h
;-------------------
; 연결된 숫자 변환
;-------------------
div_1:
mov bl,100 ;젯수 100
div bl ;100으로 나누기
push ax ;100으로 나눗값 보존
mov bl,10 ;젯수 10
mov ah,0 ;몫을 다시 10으로 나눈다.
div bl
add al,30h
mov temp_1,al ;1000단위 저장
add ah,30h
mov temp_2,ah ;100단위 저장
pop ax ;100으로 나눈값 복구
xchg ah,al ;100으로 나눈값의 나머지를
mov ah,0 ;다시 나누기위해
div bl ;다시 10으로 나눈다
add al,30h
mov temp_3,al ;10단위 저장
add ah,30h
mov temp_4,ah ;1단위 저장
ret
;--------------------
; 분리된 숫자 계산
;--------------------
div_2:
push ax
xchg ah,al ;ah,al을 따로 나누기위해
mov ah,0
mov bl,10 ;젯수 10
div bl ;10으로 나눈다
add al,30h
mov temp_1,al ;몫 저장
add ah,30h
mov temp_2,ah ;나머지 저장
pop ax
mov ah,0
div bl
add al,30h
mov temp_3,al ;몫 저장
add ah,30h
mov temp_4,ah ;나머지 저장.
ret
code ends
end start ;진입접
[41] 제목 : [새내기 연재] 어셈블러가 도대체 뭐길래
올린이 : hanuly님(조광현 ) 95/04/23 21:19 읽음 : 114 관련자료 있음(TL)
어셈블러?? 어셈블러?! 어셈블러!? 어셈블러!
─────────────────────
Copyright(c)1995,.Sir hanuly ┍━┯━━━━━━━━━━━━━┑
어셈 새내기를 위한 어셈 기초 연재... │Ⅰ│어셈블러가 도대체 뭐길래..│
┕━┷━━━━━━━━━━━━━┙
★ 기억하세요!
* 저의 비주얼 베이직 연재를 읽어보신 분들은 아시겠지만, 내용이 '잡담 형식'
으로 꾸며지다가, 때로는, 교습 형식으로 꾸며지는 등의 이상한 진행 형태를 취
하고 있습니다.
* 이 연재는 어셈블러를 처음 배우시는 분들을 위해 마련되었습니다.
* 개인적 질문 편지는, 되도록 '삼가'해주시고, '묻고 답하기' 란을 이용해주셨
으면 합니다.
* 내용상의 오류가 발견된 경우는 언제든지 편지로 연락주시기 바랍니다. 제 아
이디는 아시죠? 'hanuly님'입니다.
* 본 내용을 제 허가 없이 베끼거나(!), 복사(?)하는 행위를 하시면, 제가 화낼
거예요.. 그리고, 저작권법에 의거(?)해 고소할거예요.. (히히.. 돈버는거 좋아
하는 리님..)
* 본 내용의 소스들은 거의 다 Macro Assembler 5.x를 기준으로 작업합니다. 공
개자료실에 등록되어 있습니다.
* 필요한 파일들..
masm.exe
link.exe
exe2bin.??? <-이름을 까먹었어요...
그리고, 즐기는 에디터...('산'이나, 'UEdit'나, 도스의 'edit'
같은..)
─────────────────────────────어셈블러는 어렵다?
안녕하세요? 하누리님입니다.
어셈블러는 어렵다는 말씀을 많이 하시더군요... 게다가...많은 분들이 공부하다
가 중간에 포기하십니다. 물론, 어셈블러는 어려워요...(...) 인터럽트를 직접 건
드리고 놀아야(?) 합니다. 하드웨어, CPU 원리...
..맞아요.. 어셈블러를 배우는 것은 영어를 배우는 것과 비슷하다고 해야겠군요.
영어를 배울때는 단어와 숙어를 열심히 외움과 동시에, 문법도 연구합니다.. 게다
가, 사고 방식, 환경까지...
어셈블러는 그렇지만, 영어보다 쉬워요. printf...int.. 엄청나게 많은 단어들..
(C나, Basic, Pascal 같은 고급 언어들은 다 그렇죠..뭐..) 언제 외우노.. 그쵸?
그렇지만, 어셈블러는 mov, int, add등의 몇개의 명령어만 배우면 됩니다. 문화
방식 이해하기도 쉽고(미국이나, 영어의 문화 방식 같은..)..
어셈블러는 아는대로 써먹을 수 있다는 것이 장점일수도 있겠군요. 물론, 이 연
재와, 앞으로 올라올 연재들을 학습한다고 해서, 다 깨닫는 것은 절대 아니겠죠!
그렇지만, 어셈블러로 쬐끄만 프로그램은 짤 수 있을거예요(저도 바라고.. 당신
도 바라고.. 우리도 바라는 목표입니다).
영어를 배울려면, "영어에 대해서, 학교에서 배우는 것" 보다는, "미국에 가서,
직접 경험하고 느끼는 것"이 훨씬 효과적입니다. 어셈블러도... 시스템이 다운되
고, CMOS가 날아가고..(우리는 그정도로 극단적인 기법은 학습하지 않아요... 그
러한 것들을 하려면, 다른 분들의 강좌를 읽으셔야 겠죠..) 아무튼, 실제로 느껴
서 하는 것이 가장 중요하다는 거.. 아시죠? 어셈은 그럴때 더욱 더 나의 친구로
다가오게 되는 것입니다.
자, 우리 어셈 새내기들... 화이팅을 외치며.. 조그만 시작을.. 하자구요..
¤ 명언 : 시작이 반이랍니다.
─────────────────────────────── 어셈이 뭐꼬?
자, 여기서 처음부터 어셈블러(이하, '어셈'이라고 부릅니다)를 배우려고, 하시
는 분들은 손 들어보시겠어요?
당신도, 남대문에서 과일 파시는 아저씨도, 손을 내리고 계시는 군요..어! 그런
데.. 저쪽에서는 국민학생 같아 보이는 학생들이 손을 들고 있네요... 아.. 학교
에서 벌을 받고 있었군요..
어셈을 처음부터 배우려는 분들은 아주 극소수입니다. 물론이죠! 극소수죠.
베이직이나, 씨나, 파스칼이나.. 다른 언어를 먼저 배우고, 어셈을 배우는 것이
보통입죠. 네..
원래 컴퓨터는 고철덩어리죠. 2진수만 받아들이죠.. 꺼졌다 켜졌다... 아시리라
생각되는 군요.. 세게.. 약하게는 해당되지 않습죠(디지탈 형식이다..).
게다가, 컴퓨터의 머리는 꼭 자기가 기계인 것을 티를 낸답니다, 기계어만을 받
아들일줄 안답니다.
그래서, 수치 비교도 사실 컴의 머리(CPU:중앙처리장치)로서는 엄청난 작업을..
거치는 겁니다.
그래서, 고급언어는 하나의 문장으로 처리되는 것이... 어셈-기계어와 1대 1로
대응하는 언어-에서는 복잡해지고.. 기계어는... 꼬로로로록..(거품내는 소리)
예를 들어서.. 1+2+3을 계산합니다.
Basic은요..
abc = 1+2+3
C는요.....
abc = 1+2+3;
어셈은요...
mov abc,1
add abc,2
add abc,3
기계어는요...
(저도 잘모릅니다. ... ★... 하지만, 엄청 복잡하다는 것은 아시리라.. 악!
돌이,.,.,.)
고급언어(씨나, 베이직)은 한줄이면 끝나죠. 어셈은요.. 에고.. 석줄씩이나..
기계어는... 아시리라...
그렇지만, CPU는 한번에 하나만 더할줄 압니다. 즉,
a=1+2
┌─┐
│1│→ 넣어요.. CPU에..
└─┘ ┌─┐
│1│이 CPU에 기억됩니다.
└─┘
┌─┐
│2│->넣어요.. 더하라는 신호도..
└─┘ ┌┬┐
│3┤이 기억됩니다.
└─┘
이런 구조이기에.. 1+2+3을 계산할때 석줄이 필요했던 것입니다.
─────────────────────────────────어셈이 뭐꼬?
이 정도로만 해 두죠... 어셈에대해서 이해하셨으리라 믿습니다.
1+2를 더했던 것과 같이, CPU의 계산 방식처럼, 한번에 하나를 더하는... 기계어와
1대 1로 대응하는.. (모 학습지처럼, 기계와 눈높이를 맞추고 있는) 언어가 바로 어
셈입니다.
숫자를 더하는 예를 들어서 설명드렸고요..
다음에는... 맛보기 예제를 하나 만들어보도록 하죠..
그럼 안녕히! 노력이 있으면, 결과가 있기 마련입니다.
[42] 제목 : [새내기 연재] 어셈으로 만든 첫 프로그램
올린이 : hanuly님(조광현 ) 95/05/02 00:15 읽음 : 88 관련자료 있음(TL)
어셈블러?? 어셈블러?! 어셈블러!? 어셈블러!
─────────────────────
Copyright(c)1995,.Sir hanuly ┍━┯━━━━━━━━━━━━━┑
어셈 새내기를 위한 어셈 기초 연재... │Ⅱ│어셈으로 만든 첫 프로그램.│
1995. 5. 2 ┕━┷━━━━━━━━━━━━━┙
★ 기억하세요!
* 이 연재는 어셈블러를 처음 배우시는 분들을 위해 마련되었습니다.
* 개인적 질문 편지는, 되도록 '삼가'해주시고, '묻고 답하기' 란을 이용해주셨
으면 합니다.
* 내용상의 오류가 발견된 경우는 언제든지 편지로 연락주시기 바랍니다. 제 아
이디는 아시죠? 'hanuly님'입니다.
* 본 내용을 제 허가 없이 베끼거나(!), 복사(?)하는 행위를 하시면, 제가 화낼
거예요.. 그리고, 저작권법에 의거(?)해 고소할거예요.. (히히.. 돈버는거 좋아
하는 리님..)
* 본 내용의 소스들은 거의 다 Macro Assembler 5.x를 기준으로 작업합니다. 매
크로 어셈블러 5.x는 공개 자료실에 등록되어 있습니다.
────────────────────────────────어셈블이라..
안녕하세요? 하누리님입니다.
우리는 지난번에, 어셈블러가 도대체 뭔지에 대해서, 이야기를 했었죠. 어셈블러
를 배우는 것은 영어를 배우는 것, 영어를 배울때는 단어, 숙어, 문법.. 미국놈들
의 사고 방식, 환경 등까지 배워야 이해를 할 수 있다고 했었죠!
오늘은 간단한 프로그램(?)을 만들어봄으로서, 어셈블러의 프로그래밍 개념에 대
해서 조금만 맛 보도록 할 것입니다. 아무튼, 노력만 한다면, 결과는 분명히 좋을
거라고 생각해요...
씨나, 파스칼의 컴파일이라는 작업을 아시겠지요. 그러나, 어셈으로 소스를 코딩
하고, obj로 변환시키는 건, 컴파일한다고 하면 망신살이 뻗친다는 사실을 기억하
세요~!
어셈블에 대해서 들어보셨는지요. 어셈블리어 소스를 기계어로 바꿔주는 과정이
라고 생각하시면 쉽습니다. 컴파일 정도에 견주어보시면 됩니다. 그리고, 이 어셈
블 과정을 해주는 것이 바로 이름하여 '어셈블러' 입니다.
────────────────────────어셈으로 프로그램을 짜는데..
어셈 프로그램을 만들때는 보통 다음과 같이 하게 됩니다.
1> 프로그램의 작성해야겠다!
우선 시작을 위해서는 어떤 프로그램을 짤 것인지 목표를 정해야 겠죠?
2> 에디터로 소스 프로그램을..
산이나, Uedit등으로 소스를 작성합니다. → 저장함으로 ?.ASM이 만들어졌겠죠.
3> masm이나, tasm으로 어셈블합니다. IF 실패?=True 그렇다면.. 1,2번으로..
성공이라면.. obj파일이 생성되었겠죠!
4> link나 tlink로 링크! If 실패?=True 그렇다면 1,2,3번...
성공이면 .EXE.COM파일 생성!
5> 실행테스트! if 실패?=True 그렇다면 2번...(논리적 오류이므로, 제작자의
알고리듬 실수일 가능성이 제일 높습니다.)
6> 성공?! -> 완성이닷!
────────────────────────────── 나의 첫 프로그램
자, 어셈에 대한 개괄적 이해를 하게 되셨으리라 믿고(광현아! 설명도 x떡같이 해
놓고는 무슨 소리얏!! 끄아아~!), 간단한 다음과 같은 소스를 준비해 보았습니다.
우리가, 터보씨를 배웠을때 맨처음 작성했던 프로그램이죠..
"Hello, World!"라는 문자열을 출력하는 것입니다. main안에서 printf만 써도 될
것 같은데.. 어셈이라는 점을 명심하시고..
┌hello.asm ───────────────────────────────┐
│ DOSSEG │
│ .MODEL SMALL ;메모리 모델을 SMALL로! │
│ │
│ .STACK 100h ;스택을 잡아둡니다. │
│ │
│ .DATA │
│message db 'Hello, World!',13,10,'$' ;메세지는 바로...이것! │
│ │
│.CODE │
│ │
│mov ax,@data ;DS 초기화... │
│mov ds,ax │
│ │
│mov ah,9 ;메세지를 화면에 찍는 부분 │
│mov dx,offset message │
│int 21h │
│ │
│mov ah,4ch ;끝내는 부분.. │
│int 21h │
│ │
│END │
└────────────────────────────────────┘
자, 다 작성하셨다면 (전문가 같이 보이게 하는 말로)어셈블링 시켜볼까요?
masm이 준비되셨겠죠? 저는 분명히 5.0정도로도 충분하다고 말씀드렸습니다~!
┌────────────────────────────────────┐
│ │
│C:\LANG\MASM>masm hello← │
│Microsoft (R) Macro Assembler Version 5.10 │
│Copyright (C) Microsoft Corp 1981, 1988, All right reserved. │
│ │
│Object filename [hello.obj] : ← ;엔터로 그냥 넘어가세요.. 신경쓰지말고.│
│Source Listing [NUL.LST] : ← ;소스가 어쩌구저쩌군요..그냥 무시합시다~!│
│Cross-reference [NUL.CRF] : ← ; 냠냠.. 그냥 넘어갑시다~! │
│ │
│ ????? + ?????? Bytes symbol space free │
│ │
│ 0 Warning Errors │
│ 0 Severe Errors │
│ │
└────────────────────────────────────┘
이렇게 되었다면 성공~! 그런데, 여기서 끝나면 바보.. 링킹해야죠..(연결??)
┌─────────────────────────────────────┐
│C:\LANG\MASM>link hello← │
│Microsoft (R) Overlay Linker. .... 어쩌구... │
│ │
│Run File [HELLO.EXE] : ← │
│ │
│List ... : ← │
│Library...: ← │
│ │
└─────────────────────────────────────┘
이런식으로 넘기면 hello.exe가 만들어집니다.
자, 실행해보시죠?!
그리고.. 결과는??
간단한 맛배기 였습니다.
─────────────────────────────────────결론
우리는 이 소스를 구경하고 입력해봄으로써, 어셈블러에 대해서 어느정도의 파악은
할수 있었습니다.
다음에는 뭘할까요? 좀 따분한 내용을 다루도록 하죠. 그럼 이만...
-------------------------------
함께누리는 넓은 하늘아래 우리를 모으는 한울타리.. 그리고, 상대방을 존중
할줄아는 그러한 말가짐새... 하누리님은 그러한 뜻을 가지고 있습니다.
출력이 끝났습니다. [Enter]를 누르십시오.