• 카테고리
    • 전체 글

    • 카테고리1
    • 카테고리2
    • 카테고리3
    • 카테고리4
  • 태그
  • 방명록

'2005/02/25'에 해당되는 글 2건

  • 2005.02.25 [번역중] Win32 Assembly - 2 : MessageBox 1
  • 2005.02.25 [번역완] Win32 Assembly - 1 : The Basic

[번역중] Win32 Assembly - 2 : MessageBox

번역문서 2005. 2. 25. 22:50
이 과정에서는, "Win32 어셈블리는 위대하다!!" 라고 말할 수 있는 메시지 박스를 보여 줄 수 있는 모든 함수형(functional) Windows  프로그램을 생성할 수 있습니다.  
예제 파일은 이것을 다운로드 하세요.

서론:
Windows 에서는 Windows 용 프로그램을 위한 자원들을 확보해서 준비해 놓고 있습니다 . 이러한 기능을 수행할 수 있도록 하는 핵심이 바로 윈도우즈 API(Application Programming Interface) 입니다. Windows API란, Windows 프로그램을 짤 때 사용되는 각종 유용한 함수들의 거대한 집합체를 말합니다. 이 함수들은 Dynamic-linked libraries(DLL)에  These functions are stored in several dynamic-linked libraries (DLLs) such as kernel32.dll, user32.dll and gdi32.dll. Kernel32.dll contains API functions that deal with memory and process management. User32.dll controls the user interface aspects of your program. Gdi32.dll is responsible for graphics operations. Other than "the main three", there are other DLLs that your program can use, provided you have enough information about the desired API functions.
Windows programs dynamically link to these DLLs, ie. the codes of API functions are not included in the Windows program executable file. In order for your program to know where to find the desired API functions at runtime, you have to embed that information into the executable file. The information is in import libraries. You must link your programs with the correct import libraries or they will not be able to locate API functions.
When a Windows program is loaded into memory, Windows reads the information stored in the program. That information includes the names of functions the program uses and the DLLs those functions reside in. When Windows finds such info in the program, it'll load the DLLs and perform function address fixups in the program so the calls will transfer control to the right function.
There are two categoriesof API functions: One for ANSI and the other for Unicode. The names of API functions for ANSI are postfixed with "A", eg. MessageBoxA. Those for Unicode are postfixed with "W" (for Wide Char, I think). Windows 95 natively supports ANSI and Windows NT Unicode.
We are usually familiar with ANSI strings, which are arrays of characters terminated by NULL. ANSI character is 1 byte in size. While ANSI code is sufficient for European languages, it cannot handle several oriental languages which have several thousands of unique characters. That's why UNICODE comes in. A UNICODE character is 2 bytes in size, making it possible to have 65536 unique characters in the strings.
But most of the time, you will use an include file which can determine and select the appropriate API functions for your platform. Just refer to API function names without the postfix.
Example:
I'll present the bare program skeleton below. We will flesh it out later.
.386
.model flat, stdcall
.data
.code
start:
end start

The execution starts from the first instruction immediately below the label specified after end directive. In the above skeleton, the execution will start at the first instruction immediately below start label. The execution will proceed instruction by instruction until some flow-control instructions such as jmp, jne, je, ret etc is found. Those instructions redirect the flow of execution to some other instructions. When the program needs to exit to Windows, it should call an API function, ExitProcess.

ExitProcess proto uExitCode:DWORD

The above line is called a function prototype. A function prototype defines the attributes of a function to the assembler/linker so it can do type-checking for you. The format of a function prototype is like this:

FunctionName PROTO [ParameterName]:DataType,[ParameterName]:DataType,...

In short, the name of the function followed by the keyword PROTO and then by the list of data types of the parameters,separated by commas. In the ExitProcess example above, it defines ExitProcess as a function which takes only one parameter of type DWORD. Functions prototypes are very useful when you use the high-level call syntax, invoke. You can think of invoke as a simple call with type-checking. For example, if you do:

call ExitProcess

without pushing a dword onto the stack, the assembler/linker will not be able to catch that error for you. You'll notice it later when your program crashes. But if you use:

invoke ExitProcess

The linker will inform you that you forgot to push a dword on the stack thus avoiding error. I recommend you use invoke instead of simple call. The syntax of invoke is as follows:

INVOKE  expression [,arguments]

expression can be the name of a function or it can be a function pointer. The function parameters are separated by commas.

Most of function prototypes for API functions are kept in include files. If you use hutch's MASM32, they will be in MASM32/include folder. The include files have .inc extension and the function prototypes for functions in a DLL is stored in .inc file with the same name as the DLL. For example, ExitProcess is exported by kernel32.lib so the function prototype for ExitProcess is stored in kernel32.inc.
You can also create function prototypes for your own functions.
Throughout my examples, I'll use hutch's windows.inc which you can download from http://win32asm.cjb.net

Now back to ExitProcess, uExitCode parameter is the value you want the program to return to Windows after the program terminates. You can call ExitProcess like this:

invoke ExitProcess, 0

Put that line immediately below start label, you will get a win32 program which immediately exits to Windows, but it's a valid program nonetheless.

.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:
        invoke ExitProcess,0
end start

option casemap:none tells MASM to make labels case-sensitive so ExitProcess and exitprocess are different. Note a new directive, include. This directive is followed by the name of a file you want to insert at the place the directive is. In the above example, when MASM processes the line include \masm32\include\windows.inc, it will open windows.inc which is in \MASM32\include folder and process the content of windows.inc as if you paste the content of windows.inc there. hutch's windows.inc contains definitions of constants and structures you need in win32 programming. It doesn't contain any function prototype. windows.inc is by no means comprehensive. hutch and I try to put as many constants and structures into it as possible but there are still many left to be included. It'll be constantly updated. Check out hutch's and my homepage for updates.
From windows.inc, your program got constant and structure definitions. Now for function prototypes, you need to include other include files. They are all stored in \masm32\include folder.

In our example above, we call a function exported by kernel32.dll, so we need to include the function prototypes from kernel32.dll. That file is kernel32.inc. If you open it with a text editor, you will see that it's full of function prototypes for kernel32.dll. If you don't include kernel32.inc, you can still call ExitProcess but only with simple call syntax. You won't be able to invoke the function. The point here is that: in order to invoke a function, you have to put its function prototype somewhere in the source code. In the above example, if you don't include kernel32.inc, you can define the function prototype for ExitProcess anywhere in the source code above the invoke command and it will work. The include files are there to save you the work of typing out the prototypes yourself so use them whenever you can.
Now we encounter a new directive, includelib. includelib doesn't work like include. It 's only a way to tell the assembler what import library your program uses. When the assembler sees an includelib directive, it puts a linker command into the object file so that the linker knows what import libraries your program needs to link with. You're not forced to use includelib though. You can specify the names of the import libraries in the command line of the linker but believe me, it's tedious and the command line can hold only 128 characters.

Now save the example under the name msgbox.asm. Assuming that ml.exe is in your path, assemble msgbox.asm with:

ml  /c  /coff  /Cp msgbox.asm
/c tells MASM to assemble only. Do not invoke link.exe. Most of the time, you would not want to call link.exe automatically since you may have to perform some other tasks prior to calling link.exe.
/coff tells MASM to create .obj file in COFF format. MASM uses a variation of COFF (Common Object File Format) which is used under Unix as its own object and executable file format.
/Cp tells MASM to preserve case of user identifiers. If you use hutch's MASM32 package, you may put "option casemap:none" at the head of your source code, just below .model directive to achieve the same effect.
After you successfully assemble msgbox.asm, you will get msgbox.obj. msgbox.obj is an object file. An object file is only one step away from an executable file. It contains the instructions/data in binary form. What is lacking is some fixups of addresses by the linker.
Then go on with link:

link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS  informs Link what sort of executable this program is
/LIBPATH: tells Link where the import libraries are. If you use MASM32, they will be in MASM32\lib folder.
Link reads in the object file and fixes it with addresses from the import libraries. When the process is finished you get msgbox.exe.
Now you get msgbox.exe. Go on, run it. You'll find that it does nothing. Well, we haven't put anything interesting into it yet. But it's a Windows program nonetheless. And look at its size! In my PC, it is 1,536 bytes.

Next we're going to put in a message box. Its function prototype is:

MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD

hwnd is the handle to parent window. You can think of a handle as a number that represents the window you're referrring to. Its value is not important to you. You only remember that it represents the window. When you want to do anything with the window, you must refer to it by its handle.
lpText is a pointer to the text you want to display in the client area of the message box. A pointer is really an address of something. A pointer to text string==The address of that string.
lpCaption is a pointer to the caption of the message box
uType specifies the icon and the number and type of buttons on the message box
Let's modify msgbox.asm to include the message box.
  
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.data
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0

.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start

Assemble and run it. You will see a message box displaying the text "Win32 Assembly is Great!".

Let's look again at the source code.
We define two zero-terminated strings in .data section. Remember that every ANSI string in Windows must be terminated by NULL (0 hexadecimal).
We use two constants, NULL and MB_OK. Those constants are documented in windows.inc. So you can refer to them by name instead of the values. This improves readability of your source code.
The addr operator is used to pass the address of a label to the function. It's valid only in the context of invoke directive. You can't use it to assign the address of a label to a register/variable, for example. You can use offset instead of addr in the above example. However, there are some differences between the two:

addr cannot handle forward reference while offset can. For example, if the label is defined somewhere further in the source code than the invoke line, addr will not work.
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM will report error. If you use offset instead of addr in the above code snippet, MASM will assemble it happily.
addr can handle local variables while offset cannot. A local variable is only some reserved space in the stack. You will only know its address during runtime. offset is interpreted during assembly time by the assembler. So it's natural that offset won't work for local variables. addr is able to handle local variables because of the fact that the assembler checks first whether the variable referred to by addr is a global or local one. If it's a global variable, it puts the address of that variable into the object file. In this regard, it works like offset. If it's a local variable, it generates an instruction sequence like this before it actually calls the function:
lea eax, LocalVar
push eax

Since lea can determine the address of a label at runtime, this works fine

* 관리자님에 의해서 게시물 이동되었습니다 (2005-03-09 04:09)
728x90
블로그 이미지

하인도1

[하인드/하인도/인도짱 의 홈페이지] 저만의 공간입니다. 다양한 소재들을 나열하는 아주 단순 무식한 홈페이지 입니다. 다양한 문서 자료도 있겠지만, 저의 푸념들도 있답니다.

[번역완] Win32 Assembly - 1 : The Basic

번역문서 2005. 2. 25. 19:24

Tutorial 1: The Basics



이 과정에서는 독자들이 기본적으로 MASM을 사용할 수 있다고 간주하고 기록하였습니다. 혹시 MASM류의 프로그램이 없다면 win32asm.exe를 다운로드 하시고, 이 과정을 수행하기 전에, 다운로드한 파일 안에 있는 문서로 학습해주시기 바랍니다. 좋습니다. 준비 되셨으면 시작하도록 하죠 ^^

서론


Win32 프로그램들은 80286 시절때 부터 계속 프로텍트 모드상에서 실행되었습니다.물론 80286은 까마득한 과거의 유산일 뿐입니다. 지금부터 우리는 80386과 그 뒤의 CPU에 대해 더 많은 관심을 가지고 분석을 할 필요가 있습니다.윈도우는 나뉘어진 가상공간상에서 각각의 Win32 프로그램들이 유기적으로 뭉쳐 돌아가는 집합체 입니다. 여기서 조금 자세히 들어가게 되면, 각 Win32 프로그램이 각자 4GB 주소 공간을 가지고 있다는 것입니다. 하지만 정확히 해야될 부분은 모든 Win32 프로그램들이 실제 메모리를 4GB씩 가지고 있다는 것이 아닙니다. 단지 그 만큼의 주소값을 가질 수 있는 범위를 의미하는 것이죠. 윈도우즈는 단지 각 프로그램이 올바른 주소값을 사용할 수 있도록 필요한 모든일을 수행하는 것 뿐입니다. 물론, 그 프로그램들은 윈도우즈에서 정한 규칙을 정확히 따라야 합니다. 그렇지 않게 되면 두려운 General Protection Fault(일반 보호 실패) 문제를 발생하게 되죠. 각 프로그램은 자신에게 살당된 각 주소 공간안에서 수행되어야 하는 것입니다. 이 부분이 Win16 시절의 형태와 명확히 구분되는 부분입니다. 모든 Win16 프로그램들은 다른 프로그램들의 주소 내용을 참조하여 볼 수 있을 만큼 구분되어진 공간이 없었습니다. 이런 특징은 어떤 프로그램도 다른 프로그램의 코드/데이터를 함부로 간섭하게 되는 문제를 줄일 수 있게 되었습니다.  
메모리 모델 또한 16비트 시절의 형태와는 전혀 다른 형태를 가지고 있습니다. Win32 하에서, 메모리 모델 또는 세그먼트(segments)등에 대해 신경쓸 필요가 없습니다. 단지 여기서는 Flat memory model 만이 있을 뿐이죠. 즉 64K의 제한적인 세그먼트는 더 이상 없습니다. 메모리는 4GB의 거대한 연속적 공간가 된 것입니다. 즉, 더이상 세그먼트 레지스터들 가지고 고민하면서 작업하실 필요가 없다는 것을 의미합니다. 메모리 공간안에 어떤 포인터를 가르키는 어떠한 세그먼트 레지스터든지 간에 모두 사용할 수 있다는 의미입니다. 이 부분은 프로그래머들에게 아주 특별한 선물일 수 밖에 없습니다. 그래서 Win32 assembly는 C처럼 쉬워지게 됩니다.
Win32하에서 프로그램을 짜게 될때, 몇가지 중요한 법칙을 아셔야 됩니다. 윈도우즈는 내부적으로 esi, edi, ebp 과 ebx를 사용ㅇ합니다. 그리고 그 레지스터들의 변화되는 값을 전혀 예상할 수 없습니다. 여러분이 지켜주셔야 될 법칙 그 하나는 바로 이것입니다. 만약 이 4가지 레지스터 중 한가지라도 여러분의 만든 Callback 함수들에서 사용된다면, 윈도우즈로 제어가 넘어가기 전에 그 레지스터들의 이전 값들을 전부 잃게 됩니다. Callback 함수라는 것은 윈도우즈에 의해 호출 되는 함수를 의미하는 것입니다. 그 쉬운 예가 윈도우즈 프로시져 입니다. 정리하자면 그 4개의 레지스터들을 가급적 사용하지 않아야 된다는 것입니다. 그래야, 정확하게 윈도우즈에게 제어권이 넘어가기전 각 레지스터의 값들이 올바르게 이전값으로 돌아 갈 수 있습니다.

내용


여기에 요점 정리형 프로그램이 있습니다. 코드가 이해가 안간다고 포기하진 마세요. 어차피 모두 나중에 설명될 내용들입니다.

.386
  .MODEL Flat, STDCALL
  .DATA
     
      ......
  .DATA?
    
     ......
  .CONST
    
     ......
  .CODE
    


That's all! Let's analyze this skeleton program.
.386

이 부분은 어셈블러의 지시자 입니다. 이부분의 의미는 80386 명령어 셋을 사용한다고 어셈블러에게 전달하는 부분입니다. 물론 .486, .586 를 사용할 수 있겠지만, .386 으로 써주는게 가장 안전합니다. 여기에는 각 CPU 모델에 맞춰서 .386/.386p, .486/.486p 와 같이 2가지의 형태로 쓸 수 있습니다. 여기서 "p" 버젼은 프로그램 안에 특별한 명령어를 사용하실 때, 꼭 필요한 내용입니다. 여기서 특수한 명령이란, 프로텍트 모드상에서 CPU/OS 상에서 예약되어져 사용되는 명령어들입니다. 그 명령들은 가상 장치 드라이버와 같은 특수한 코드에서 사용됩니다. 일반적으로 프로그램을 짤때는 p 모드를 할 필요가 없고 또 일반 모드로 사용하는게 안전한 경우가 많습니다.  
.MODEL FLAT, STDCALL

.MODEL 이라는 것도 어셈블러의 지시자 입니다. 이 지시자의 의미는 현재 작성할 프로그램의 메모리 모델을 설정하는 것입니다. Win32 상에서는 한가지 모델 밖에 없는데, 그것이 바로 FLAT 모드 입니다. 그러므로 그냥 그대로 쓰시면 됩니다.
STDCALL 은 MASM에게 파라미터 전달 집합체(parameter passing convention)의 유형을 알려주는 부분입니다. 파라미터 전달 집합체는 파라미터 전달 하는 순서를 정의하는 부분으로 왼쪽에서 오른쪽, 오른쪽에서 왼쪽으로 전달 할지를 결정합니다. 또, 함수 호출후 스택 프레임을 누가 조율 할지도 결졍하게 되는 부분입니다. 참고로 과거 Win16시절에는 C와 PASCAL의 두가지 형태의 파라미터 전달 집합체만 사용할 수 있었습니다.
  "C" 호출 방식은 오른쪽에서 왼쪽으로 전달하는 방식으로 맨 오른쪽 파라미터 내용부터 스택에 쌓이게 됩니다. 즉, 함수 호출이 끝나면 스택 프레임상의 스택 조율을 호출자가 담당하게 됩니다.  예를 들어 foo(int first_param, int second_param, int third_param) 라는 이름의 함수를 "C" 호출 방식으로 하는 방식으로 asm 코드로 만들게 되면 다음과 같습니다. :

push  [third_param]               ; Push the third parameter
push  [second_param]            ; Followed by the second
push  [first_param]                ; And the first
call    foo
add    sp, 12                                ; The caller balances the stack frame


"PASCAL" 호출 방식은 "C" 호출 방식의 반대로 생각하시면 간단합니다. 즉 파라미터 전달을 왼쪽에서 오른쪽으로 하게 되며, 함수 호출이 끝나면 스택 프레임상의 스택 조율을 피호출된 함수가 담당하게 됩니다. 기존 Win16은 "PASCAL" 호출방식으로 맞춰져 있는데, 그 이유가 코드를 더 작게 만들 수 있기 때문입니다.

"C"  호출 방식은 몇개의 파라미터가 넘겨질지 모를때 사용하기 편한데, 보통 wsprinf() 와 같은 형태의 함수에서 사용됩니다. wsprintf()의 형태에서는 실행되기 전에는 몇개의 파라미터가 있는지를 알 수 없기 때문에, 피 호출된 함수에서 함부로 스택 조율을 할 수 없게 됩니다.  
원점으로 돌아와서 STDCALL은  C 와 PASCAL 방식을 합친 방식으로 파라미터 전달을 오른쪽에서 왼쪽으로 하지만, 호출 후에 피 호출된 곳에서 스택 조율을 담당합니다. Win32 platform에서는 대부분 STDCALL 을 사용합니다. 단 예외적으로 wsprintf()만 틀립니다.. 이런 부분에선 꼭 C 호출 방식으로 사용해야 합니다.  

.DATA
.DATA?
.CONST
.CODE

총 4개의 지시자는 호출될 섹션이 어떤것인지를 알려주는 부분입니다. Win32에서는 세그먼트라는 것이 없습니다. 기억나시죠? 하지만, 전체 주소 공간에서 로직 섹션 부분을 나눌 수 있습니다. 한 섹션의 시작이란 이전 섹션과의 끝을 의미합니다. 여기에는 두가지 형태의 섹션이 있는데, 그것이 바로 데이터와 코드 입니다. 여기서 데이터 섹션은 3가지로 나뉩니다.

.DATA    이 섹션은 프로그램의 초기화된 데이터가 담는 부분입니다.
.DATA?  이 섹션은 프로그램의 초기화되지 않은 데이터를 담는 부분입니다. 가끔 미리 저장될 메모리만 잡고 그 안의 데이터는 굳이 초기화 될 필요가 없는 경우가 있습니다. 이 섹션이 바로 그런 용도로 사용됩니다. 데이터를 초기화 하지 않게 되는 경우 얻는 장점은 실행될 파일안의 공간을 차지 하지 않는다는 것입니다. 예를 들어, DATA? 섹션안에 10,000 바이트를 잡아 놓았다고 할때, 실행 테이블에서는 10,000 바이트를 안 잡습니다. 실제 메모리 크기도 마찬가지 입니다. 단지 어셈블러에게 추후 이 프로그램이 실행 되었을때 얼마만큼의 공간이 나중에 쓰일지만을 알려주는 것으로 끝납니다.
.CONST  이 섹션은 프로그램에서 사용될 상수들을 선언할 때 사용합니다. 이 섹션의 상수들의 값은 프로그램에 의해 변경되지 않습니다. 정말 그들은 *상수* 입니다.  

이 세개의 섹션은 프로그램 안에서 꼭 사용될 필요는 없습니다. 단지 필요할 때 선언하여 사용하시면 됩니다.

단 한개의 섹션 코드: .CODE. 여기에 실제 실행되는 코드가 담기게 됩니다.


< label >
end < label >


코드의 특정 범위를 결정할 때 사용되는 어떠한 이름으로 쓸 수 있는 라벨을 의미합니다. 두개의 라벨은 반드시 유일한 내용이여야 합니다.
728x90
블로그 이미지

하인도1

[하인드/하인도/인도짱 의 홈페이지] 저만의 공간입니다. 다양한 소재들을 나열하는 아주 단순 무식한 홈페이지 입니다. 다양한 문서 자료도 있겠지만, 저의 푸념들도 있답니다.

  • «
  • 1
  • »
250x250

블로그 내에 소스 코드 삽입 이사온 기념 스킨도... RSS 전문 기능 비활성화 관련. 스킨 바꾸어 보았습니다. 서버 파일 정리 좀 했습니다.

«   2005/02   »
일 월 화 수 목 금 토
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28

me2sms WSS twi2me 불만 친구 Buscuit 협업 수 e-book 오류 me2photo 것 windows Tutorial 개발환경 블로그 moss me2dayzm 2010 java 매뉴얼 Azure Google Apps Engine MOSS 2007 비스킷 좀 지름신 Visual Studio 인터파크 SharePoint

  • Total :
  • Today :
  • Yesterday :

Copyright © 2015-2025 Socialdev. All Rights Reserved.

Copyright © 2015-2025 Socialdev. All Rights Reserved.

티스토리툴바