Hell_Demon (10-26-2009),Void (12-06-2009),why06 (10-22-2009),zeco (11-01-2009)
Hello World in Assembly
As is customary with almost every language the first thing people learn to do is how to print out a string of text (Usually being "Hello World!"). In higher level languages this is usually fairly easy to accomplish in relatively few lines of code. For instance in C++ one could write:
When it comes to assembly though, depending on the environment, a little more work is involved. If you were to disassemble the exe file generated by the above code you would see TONS of assembly code just to do this one simply task.Code:#include <iostream> using namespace std; int main() { cout << "Hello World!" << endl; return 0; }
Back in the 'old days' when MS-DOS reigned supreme and any assembly programming was done is 16 bit you had direct access to the 'MS-DOS API' which allowed you to easily deal with any of the hardware in a fairly direct matter.
On the flip side you had to worry about the structure of your program in memory, which memory model to use etc etc.
For instance a 16 Bit version of Hello World in Assembly would look like this:
It's definitely a little more cryptic. First the memory model gets set, a stack size gets defined. Then the data block sets the variables we will be using. Strings used this way need to be ended with the '$' symbol. The 13 and 10 values are \R and \N respectively which is a newline basically the same as the ENDL command in the C++ sample.Code:.MODEL Small .STACK 100h .DATA msg db 'Hello World!',13,10,'$' .CODE start: mov ax, seg msg mov ds,ax mov ah, 09h lea dx, msg int 21h mov ax,4C00h int 21h end start
The 'start:' label defines the start of the program. The next two lines make sure that the DataSegment actually points to the correct spot in memory.
The core of the above is the Int 21h call. It basically takes a parameter in the AH register and does something with it. You can see it gets called twice. Once with 09h in AH and another with AX having the of 4C00h (thus AH = 4C). Basically when we use INT 21h with AH=09H we are telling it to print a string to the command line whose address pointer sits in the DX Register. As you can see this is basically the 'COUT' statement in the C++ Source
The Second Int21 call basically says 'End of program and return what's in AL' which in this case is 0 so this is similar to the 'Return 0' statement in the C++ program.
Ok this is all great, but in a sense this is all irrelevant for writing assembly now-adays against the Windows API. Everything Windows does is using the 'flat' memory model which means theoretically you have access to a full 4gb of ram, while under DOS it would only give you 16kb, hence the need for segments. Also Windows provides functionality for a lot of the functionality you used to have to talk directly for the hardware for. SO in a sense programming Assembly for windows makes it a little more 'high-level' than old-school MS-DOS Programming.
So what if we now want to write a 32-bit Command-Line Version of 'Hello World' for Windows?
The trick here is to not reinvent the wheel or go to 'low level' as your first instict may be, but to work against the provided Windows API with methods in the Windows DLLs. This code is for Masm32 (different assemblers may need slight tweaks etc)
Looking at this code from the top you'll see again some initialization (target platform is a 386, using the 'flat' memory model , standard calling convention and we're case-sensitive in our names). Then you see a bunch of 'include' and 'includelib' statements. If you're familiar with C++ this is very similar to including a Header File and telling the linker that when when building it should also include a specific LIB file. So in this instance we're including the Windows,Kernel32 and masm32 files. This will allow us to make windows sytem calls.Code:.386 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib .data HelloWorld db "Hello World!", 0 .code start: invoke StdOut, addr HelloWorld invoke ExitProcess, 0 end start
Then we have the Data Segment again where we define our string (notice how this one is '0' terminated) after which we find the Code Segment. Instead of calling Interrupts as we did in the 16-bit sample we are now simply invoking existing Windows API Methods, namely: StdOut and Exit Process.
As you can probably guess StdOut takes as a parameter the pointer to the 'Hello World' String and Exit Process will return '0' since that's what we're passing int.
Notice that this is meant to be ran from within the command line otherwise it will just flash a window without altering the code to wait for input
Ok, Great! But now what if we want to show an actual Dialog inside of windows? Well the only change we'd have to do is basically change where we call StdOut to something that shows a window. Windows provides a function called MessageBox which is just what we're looking for. This function though is provided in the user32.dll so we'll need to make sure to include this in our code.
Looking at MSDN.microsof*****m we can find that the MessageBox functions takes the following parameters
Where hWnd is the window Owner - Not needed so set to NULLCode:int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
lpText is the pointer to the string to display: addr HelloWorld
lpCaption is the Caption for the window, which we just set to the same text for convenience
and uType is a enum which defines what buttons to show on the dialog, In this case just the 'OK' button.
The finished code for this would then look like this:
Code:.386 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib .data HelloWorld db "Hello World!", 0 .code start: invoke MessageBox, NULL, addr HelloWorld, addr HelloWorld, MB_OK invoke ExitProcess, 0 end start
Of course once this gets compiled when looking at it through a disassembler you will not see the code as nice as this, but that's for another time
(ok hint : you'd see something more like this but without the nice readable variable names)
Code:... push MB_OK push offset HelloWorld push offset HelloWorld push NULL call MessageBoxA ...
Last edited by B1ackAnge1; 10-22-2009 at 08:00 PM.
Hell_Demon (10-26-2009),Void (12-06-2009),why06 (10-22-2009),zeco (11-01-2009)
Holy crap! You wrote out the whole thing! Thanks a lot BA. Excellent explanation! May you live out the rest of your days in happiness!
"Every gun that is made, every warship launched, every rocket fired signifies, in the final sense, a theft from those who hunger and are not fed, those who are cold and are not clothed. This world in arms is not spending money alone. It is spending the sweat of its laborers, the genius of its scientists, the hopes of its children. The cost of one modern heavy bomber is this: a modern brick school in more than 30 cities. It is two electric power plants, each serving a town of 60,000 population. It is two fine, fully equipped hospitals. It is some fifty miles of concrete pavement. We pay for a single fighter plane with a half million bushels of wheat. We pay for a single destroyer with new homes that could have housed more than 8,000 people. This is, I repeat, the best way of life to be found on the road the world has been taking. This is not a way of life at all, in any true sense. Under the cloud of threatening war, it is humanity hanging from a cross of iron."- Dwight D. Eisenhower
Ok taking this one step further :
Now what if we wanted to make our own windows app with some controls on it? This means we need to do a few steps. First We need to create both WinMain and WndProc functions. WinMain is similar to your basic 'main' function in any old C++ program, and WndProc is processes all the messages that the window receives.
Luckily in Masm32 you can make what are basically function prototypes and treat them like functions in C++ (though if you're using winasm it sorta looks like VB code down do the horizontal line across the screen.... yuck!). Also with the amount of processing you need to do Masm has some handy macros such as if else while break etc etc.
Then to create the actual controls we use the CreateWindowEx Functions so it's really straightforward.
Most of the code maps pretty true to how a C++ app would be written so I don't think I'll spend much time going through it line by line - besides.. i'm sure no one will even read this...
Except of course for Why - which btw, See how even in Masm32 you can do higher level constructs of loops etc? Ditch the HLA man and just go this route
Maybe for fun i'll rewrite this code to NOT use any macros like if/endif, invoke etc..Code:.386 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib WinMain proto :DWORD, :DWORD, :DWORD, :DWORD .data ClassName db "WinClass", 0 AppName db "Window Hello", 0 TextBox db "edit",0 Button db "button",0 ButtonText db "Let's Do it!",0 DisplayText db "Hello World of Windows!",0 ButtonID WORD 1000h EditID WORD 1001h .data? hInstance HINSTANCE ? hEdit HWND ? hButton HWND ? .code start: invoke GetModuleHandle, NULL mov hInstance, eax invoke WinMain, hInstance, NULL, NULL, 0 invoke ExitProcess, eax WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD local wc:WNDCLASSEX local msg:MSG local hwnd:HWND mov wc.cbSize, SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, offset WndProc mov wc.cbClsExtra, NULL mov wc.cbWndExtra, NULL push hInstance pop wc.hInstance mov wc.hbrBackground, COLOR_BACKGROUND mov wc.lpszMenuName, NULL mov wc.lpszClassName, offset ClassName invoke LoadIcon, NULL, IDI_APPLICATION mov wc.hIcon, eax mov wc.hIconSm, eax invoke LoadCursor, NULL, IDC_ARROW mov wc.hCursor, eax invoke RegisterClassEx, addr wc invoke CreateWindowEx, 0, addr ClassName, addr AppName, WS_OVERLAPPEDWINDOW or WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 120, NULL, NULL, hInst, NULL mov hwnd, eax .while TRUE invoke GetMessage, addr msg, NULL, 0, 0 .break .if (!eax) invoke TranslateMessage, addr msg invoke DispatchMessage, addr msg .endw mov eax, msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .if uMsg == WM_DESTROY invoke PostQuitMessage, 0 .elseif uMsg == WM_CREATE invoke CreateWindowEx,NULL,addr Button,addr ButtonText,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 70,35,80,30,hWnd,ButtonID,hInstance,NULL mov hButton, eax invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr TextBox, NULL, WS_CHILD or WS_VISIBLE,10,10,200,20,hWnd,EditID,hInstance,NULL mov hEdit, eax .elseif uMsg == WM_COMMAND mov eax,wParam .if ax == ButtonID shr eax,10h .if ax == BN_CLICKED invoke SetWindowText,hEdit,addr DisplayText .endif .endif .else invoke DefWindowProc, hWnd, uMsg, wParam, lParam ret .endif xor eax, eax ret WndProc endp end start
Must say though that using the windows API like this sure takes away part of the 'mystery' of assembly programming when you were coding directly against the hardware with INTs etc..
Last edited by B1ackAnge1; 10-23-2009 at 01:11 PM.
Hmmmm... maybe your right. It's just that the guy wrote a 900 page book called the "Art of Assembly Language" that I'm using to learn by, but if it's teaching me the wrong stuff, maybe I should give it up. I think I'll look at some reviews before I make my final decision....
And your right it does sort of make it look less cryptic... too less cryptic. I need to learn pure asm if I'm going to be able to reverse anything at all. So far theres just too much HLL stuff. But I have to say, that last code looked worse then HLA o_O?!
"Every gun that is made, every warship launched, every rocket fired signifies, in the final sense, a theft from those who hunger and are not fed, those who are cold and are not clothed. This world in arms is not spending money alone. It is spending the sweat of its laborers, the genius of its scientists, the hopes of its children. The cost of one modern heavy bomber is this: a modern brick school in more than 30 cities. It is two electric power plants, each serving a town of 60,000 population. It is two fine, fully equipped hospitals. It is some fifty miles of concrete pavement. We pay for a single fighter plane with a half million bushels of wheat. We pay for a single destroyer with new homes that could have housed more than 8,000 people. This is, I repeat, the best way of life to be found on the road the world has been taking. This is not a way of life at all, in any true sense. Under the cloud of threatening war, it is humanity hanging from a cross of iron."- Dwight D. Eisenhower
Well stick with whatever works for you. I've found a good combo for ME in MASM32/WinAsm. Also have VStudio setup to build asm files but doesn't play as nice
The thing with learning is write something like this, then run it through something like ollydbg and see what it turned it into. Since they're relatively small programs they should be easy to follow and it will show you what's going on behind the scenes so you can recognize things when you seem them 'in the field' so to speak. I rewrote your 'negate' program twice (once with using the invoke macro (nice and readable) and once without ) to show you the difference
This was awesome =). You are making me want to learn ASM more and more. But first i have to learn Java just for kicks :/
Wanna hear something really awesome?
What i'm working on right now in ASM, for WHATEVER reason my AVG thinks is a Trojan! XD
The AI program from a little while ago came up as a virus when i tried to download it :/ And a program i made also came up as a virus for no reason. Don't remember which one tho. I wonder what criteria these antiviruses use to detect stuff. That's why i can't bother with 'em. Too many false positives. I don't need it anyway.
"Every gun that is made, every warship launched, every rocket fired signifies, in the final sense, a theft from those who hunger and are not fed, those who are cold and are not clothed. This world in arms is not spending money alone. It is spending the sweat of its laborers, the genius of its scientists, the hopes of its children. The cost of one modern heavy bomber is this: a modern brick school in more than 30 cities. It is two electric power plants, each serving a town of 60,000 population. It is two fine, fully equipped hospitals. It is some fifty miles of concrete pavement. We pay for a single fighter plane with a half million bushels of wheat. We pay for a single destroyer with new homes that could have housed more than 8,000 people. This is, I repeat, the best way of life to be found on the road the world has been taking. This is not a way of life at all, in any true sense. Under the cloud of threatening war, it is humanity hanging from a cross of iron."- Dwight D. Eisenhower
Hmmm very nice but the first code need abeforeCode:system("pause");if u don't have that the console app will flash very very quicly on ur screen and u will can't see nothingCode:return 0;
Yeah thanks, however:
a) the cpp code was a mere illustration and comparison against assembly source to show how few lines are required in higher level languages and not meant to be compiled/run.
b) It's a console app, so running it from the console which is how it's intended to run wouldn't have the issue of it closing after running. (though I'm fairly sure most people on this board don't know how to work in the console)
c) system("Pause") is evil. Getch() or something equivalent would be better suited if one were to insist on running it by double clicking it.
d) see a
but hey glad someone actually read this section for change
Yep i agree with u but i useCode:system("pause");