Message board for the users of flat assembler.
> Main > FASMLIB tutorial part 3 - String Library
vid 09 Aug 2006, 08:18
This time we will be talking about FASMLIB's string library.
This string library is adopted (stolen ) from Fresh project, for which it was written mainly by JohnFound and later improved by Decard and roticv.
So to the string library... first i will explain WHY is string library needed. I bet that if you ever needed to work with strings in ASM, you found out yourself . Even C's handling of strings is not the best choice for application nowadays. Because these strings are "static", eg. they reside on given place in memory, their maximal length is limited. But you often need a string, for which you can't tell what it's maximal length should be. And whenever you pass string to another routine, you also must pass it's buffer's length, so routine won't overflow. And this is another problem - static strings are often unchecked for overflow, and placed on stack near exploitable things.
All these problems can be solved by using dynamic strings. That means, that strings will reside in allocated block of memory. These blocks can be "reallocated" any time, eg. size of these blocks can be changed. This way, size of string is not limited.
Such string is created using procedure str.new from str module. It creates empty dynamic string, and returns handle to it.
call str.new jc error mov [string_handle], eax
Now what is string handle.
Simply, string handle is a number, which identifies dynamic string inside str module. This number is always lesser than 10000h, unlike data pointers which are always higher. This way it is easily possible to differentiate between dynamic and static strings. This allows routines of str module to accept both dynamic and static strings.
Note that static strings are accepted only as source, you cannot modify them with str module.
Each created string handle should be deleted. We use procedure str.del for this. It takes string handle to delete as argument.
push [string_handle] call str.del jc error
So let's see some more action with this module:
call str.new jc error mov ebx, eax ;now string, whose handle is in EBX, is empty push _str1 push ebx call str.cat ;concatenate strings, eg. append _str1 to end of EBX string jc error ;now string whose handle is in EBX holds: "Part 1",13,10,0 push _str2 push 3 push ebx call str.ins jc error ;now string whose handle is in EBX holds: "Par***t 1",13,10,0 ;and don't forget to delete the string and free memory push ebx call str.del jc error ... _str1 db "Part 1",13,10,0 _str2 db "***",0
In this example we used str.cat and str.ins.
str.cat concatenates strings, eg. appends second string to end of first string. We use it to move static string "Part 1",13,10 to string handle. Then we use str.ins to insert *** into our string at position 3, eg. behind 3rd character.
Note that when we wanted to initialize string with constant value, we did it this way:
call str.new jc error mov ebx, eax push _string_const push ebx call str.cat jc error
There is also more efficient way, using str.dup. str.dup takes one string as argument, and returns handle to it's duplicate. Argument can be constant string, so this way we can create and initialize dynamic string from constant string:
;same effect as above example push _string_const call str.dup jc error mov ebx, eax
str module offers plenty of basic routines to manipalute with strings, you can find their description in doc/modules/str.txt. But sometimes you need to do something with strings, that str module can't do. In that case, you need pointer to string buffer, not handle. You can obtain pointer to string from handle using str.ptr. It takes string handle as argument, and returns pointer to string buffer. You can also pass pointer to string constant to str.ptr and it will then return the same pointer you passed. So you can use str.ptr to get pointer to string from either handle or string constant pointer.
Best explained on example: Let's say you want to replace every ':' character in string with space ' '
;EBX holds string handle ;ESI = pointer to EBX string push ebx call str.ptr jc error mov esi, eax ;replace ':' characters .scan_char: lodsb cmp al, 0 ;0 character is end of string je .end_of_string cmp al, ':' jne .scan_char mov byte [esi-1], ' ' ;replace char jmp .scan_char .end_of_string:
But problem comes out when you want to change length of string. If you want to make string longer, you need to enlarge buffer, and if you are shortening the string, you should also shorten buffer to preserve memory. So you should use str.setlen to set length of string before every such operation. Don't care if the size really needs to be changed, it is solved (a little) in str.setlen. Just calculate the resulting string length before every operation, and call this.
Here is example, that will append 13,10 characters (end of line) to the end of string:
;EBX holds string handle ;ECX = length of string push ebx call str.len ;str.len returns lenght of string jc error mov ecx, eax ;set new length of string to ECX+2 mov eax, ecx add eax, 2 push eax ;new length of string push ebx ;string handle to resize call str.setlen jc error ;ESI = pointer to EBX string buffer push ebx call str.ptr jc error mov esi, eax ;append 13,10 to end of EBX string mov word [esi+ecx], 0A0Dh
But there is one more issue with modyfing string yourself - after you get the pointer with str.ptr, you shouldn't use any function from str module that alter string length, because string buffer can be moved to another place in memory, and so the pointer will become invalid. For example, if previous code will be written in this way, it will be a possible bug:
;EBX holds string handle ;ECX = length of string push ebx call str.len ;str.len returns lenght of string jc error mov ecx, eax ;ESI = pointer to EBX string buffer push ebx call str.ptr jc error mov esi, eax ;set new length of string to ECX+2 mov eax, ecx add eax, 2 push eax ;new length of string push ebx ;string handle to resize call str.setlen jc error ;BUG!!! pointer in ESI may have becomed invalid, if string was moved ;append 13,10 to end of EBX string mov word [esi+ecx], 0A0Dh
In conclussion, i suggest not to modify string buffer yourself, if you can use procedures from str module, you will prevent some bugs. Hopefully, more functions will be added to str module soon.
I also suggest you to browse documentation of str module, so you know what it can do and what it can't.
And don't forget that you must initialize str module before you use it with str.init, and uninitialize it with str.uninit after you're done with it
Last edited by vid on 10 Sep 2006, 20:41; edited 2 times in total
|09 Aug 2006, 08:18||
vid 10 Sep 2006, 20:14
Here is one more thing you should be interested in. You may remember how we used to output strings in tutorial part 2 - we had to pass pointer to buffer, and number of bytes. This isn't very handy way for regular use. Good-old null-terminated strings are easier. With knowledge we now have, we can write procedure that does this for us:
;=============================================================================================== ; stream.write ; desc: writes string to stream ; args: stream - handle of stream to write to ; string - string handle or pointer ; ret: CF set on error ;=============================================================================================== proc stream.write, stream, string push ebx ecx ; EBX = handle or pointer to string mov ebx, [string] ; ECX = length of EBX string push ebx call str.len jc .r mov ecx, ebx ; EAX = actual pointer to string (if it is string handle) push ebx call str.ptr jc .r ; write string to stream push ecx ; length of buffer push eax ; pointer to buffer push [stream] ; stream handle call stream.writebuf jc .r ;everything went OK clc .r: pop ecx ebx ret endp
in fact, FASMLIB already has such procedure, with same name and arguments
|10 Sep 2006, 20:14||
< Last Thread | Next Thread >
Copyright © 1999-2023, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.
Website powered by rwasa.