News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

nested loops?

Started by marla, February 16, 2006, 04:14:09 PM

Previous topic - Next topic

marla

my professor wanted us to translate code that iterates through ip addresses ... to asm. i am having difficulties using jmp's and loops versus the c style for loop. here is the c code.

<code>
// scan internet
void scanner(void) {
   int a, b, c, d, e;
   for (a = 1; a <= 255; a++) {
      // avoid class A LOCAL networks
      if(a != 10 && a != 127) {
         for (b = 0; b <= 255; b++) {   
            for (c = 0; c <= 255; c++) {
               for (d = 0; d <= 255; d++) {
                  char ip[32];
                  sprintf(ip, "%d.%d.%d.%d", a, b, c,d);
                  for (e = 0; e <= TCP_portsCount; e++) {
                     printf("%s:%i\n", ip, TCP_ports[e]);
                     checkPort(ip, TCP_ports[e]);
                  }
               }
            }
         }
      }
   }
}
</code>

here is my asm code so far. i know that it will not work but i am just trying to get some guidance as to where i should be going.

<code>
; iterate through ip's
   ScanInternet:
      mov  AL, 255
      mov  AH, 255
      mov  BL, 255
      mov  BH, 255

      ip   DB  ""

      mov CX, 255
      count255_1:
         mov CX, 255
         count255_2:
            mov CX, 255
            count255_3:
               mov CX, 255
               count255_4:
                  invoke wsprintf(ip, "%d.%d.%d.%d", AL, AH, BL, BH);
                  call CheckPorts(ip, [TCP_ports + X]);
               loop count255_4
            loop count255_3
         loop count255_2
      loop count255_1
</code>

Ratch

marla,
     The following should get you started.  As you can see, the loop counters are local variables stored on the stack.  One line as noted does not make sense.  How can "a" be two values at the same time?  The operations in the innermost loop you can code yourself.  Ask if you have any questions.  Ratch


@        EQU     OFFSET

.data?
char byte 32 dup (?)
.data
format1 BYTE '%d.%d.%d.%d',0

.CODE
ScanInt STRUCT
a       DWORD ?
b       DWORD ?
cc      DWORD ?
d       DWORD ?
e       DWORD ?
ScanInt ENDS

SCI      EQU ESP.ScanInt

scanner:
PUSHIT 0,0,0,0,0           ;initialize local variables a,b,c,d,e

.REPEAT
  INC [SCI.a]

  ; avoid class A LOCAL networks
  .IF ([SCI.a]!=10) && ([SCI.a]!=127) ;THIS LINE DOES NOT MAKE SENSE
    .REPEAT
      .REPEAT
        .REPEAT
          INVOKE wsprintf,@ char,@ format1,[SCI.a],[SCI.b],[SCI.cc],[SCI.d]

          .REPEAT
;           printf("%s:%i\n", ip, TCP_ports[e]);
;           checkPort(ip, TCP_ports[e]);
           INC [SCI.e]
          .UNTIL [SCI.e] >= TCP_portsCount+1
         INC [SCI.d]
        .UNTIL [SCI.d] >= 255+1
       INC [SCI.cc]
      .UNTIL [SCI.cc] >= 255+1
     INC [SCI.b]
    .UNTIL [SCI.b] >= 255+1
  .ENDIF
.UNTIL [SCI.a] >= 255

ADD ESP,ScanInt             ;recover local variable space
RET
END

Tedd

One thing you need to remember is that calls to functions will mess up the values in most of your registers, so it's a good idea to save any register values you want to keep (or use local variables - as in ratch's example.)
You have the same problem with your code - cx is used as a count for each loop, but each loop inside it will change its value. So you'd need to surround each one with a "push ecx" and "pop ecx" (this is 32-bit code?) to make sure you have the correct counter with each loop.
No snowflake in an avalanche feels responsible.

Tedd

.IF ([SCI.a]!=10) && ([SCI.a]!=127)     ;THIS LINE DOES NOT MAKE SENSE

Yes it does, Ratch :P
(if-not-equal, not if-equal)
No snowflake in an avalanche feels responsible.

Ratch

Tedd,
     Yer right.  I guess I was thinking of equal too much.  Thanx for the correction.  Ratch

marla

i appreciate the help from both of you. i removed the equ so that i can learn/understand the code better. i had a few questions, here is my new complete .asm file.




include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\masm32.inc
include \MASM32\INCLUDE\kernel32.inc

includelib \MASM32\LIB\masm32.lib
includelib \MASM32\LIB\user32.lib
includelib \MASM32\LIB\kernel32.lib

.data?

char BYTE 32 DUP (?)

;-----------------

.data
format1 BYTE '%d.%d.%d.%d',0

;-----------------

.code

ScanInt STRUCT
   a       DWORD ?
   b       DWORD ?
   cc      DWORD ?
   d       DWORD ?
   e       DWORD ?
ScanInt ENDS

scanner:
   PUSHIT 0,0,0,0,0
   .REPEAT
              INC [ESP.ScanInt.a]
              .REPEAT
                 .REPEAT
                    .REPEAT
                       INVOKE wsprintf,OFFSET char,OFFSET format1,[ESP.ScanInt.a],[ESP.ScanInt.b],[ESP.ScanInt.cc],[ESP.ScanInt.d]
       .REPEAT
                           print chr$(char)
                           INC [ESP.ScanInt.e]
                 .UNTIL [ESP.ScanInt.e] >= TCP_portsCount+1
              INC [ESP.ScanInt.d]
            .UNTIL [ESP.ScanInt.d] >= 255+1
        INC [ESP.ScanInt.cc]
      .UNTIL [ESP.ScanInt.cc] >= 255+1
              INC [ESP.ScanInt.b]
              .UNTIL [ESP.ScanInt.b] >= 255+1
           .UNTIL [ESP.ScanInt.a] >= 255

ADD ESP, ScanInt
RET

END


ok:

1: why does "ADD ESP, ScanInt" recover local variables?
2: what does RET do? is that the same as invoke exit_process ?
3: i understand that the masm specific .if .until .repeat etc are just as good as pure asm, but i am trying to learn assembly and not think like C/higher level language: how can i convert this to pure asm?
4: thanks again for the help!

Ratch

marla,
Quote
1: why does "ADD ESP, ScanInt" recover local variables?

     It recovers the stack space used by the local variables.  Five DWORD zeroes were PUSHed onto the stack.  For each PUSH, ESP is decremented by a DWORD length of 4, for a total of 20 after the 5 PUSH sequence.  Just before the program finishes, 20 is added to ESP to balance the stack to what it was when the program was entered.  ScanInt is the name of the STRUCT, which MASM equates to the STRUCT length of 20, when used in context as an arithmetic constant.  The result is the same as doing 5 POP's, but it is quicker and less code to simple move the ESP by adding to it.

Quote
what does RET do? is that the same as invoke exit_process ?

     Yes, I coded the example as if it were to be CALL'ed or INVOKE'ed.  So the program assumes that there is a return address on the stack which RET removes and jumps to. 

Quote
3: i understand that the masm specific .if .until .repeat etc are just as good as pure asm, but i am trying to learn assembly and not think like C/higher level language: how can i convert this to pure asm?

     You can see what code is generated by the assembly and copy that.  The nice thing about those high level constructs is that you don't have to think of creative local jump labels.  Use the /Sn and /Sg switches when you execute MASM to see what the generated code is from the high level constructions.

Quote
4: thanks again for the help!

     You are welcome.  Ratch



Tedd

RET is used to RETurn from a function - back to where it was called from. So it's still a good idea to use ExitProcess where you actually mean for the process to end (ret will usually work, but only because windows catches this and calls exitprocess for you.)

Since your code isn't within a function, you could place the scanint variables in the .data? section (just keeps things a little tidier - they still actually go on the stack, but it's cleaned up when you exitprocess.)

It's good that you want to understand how the higher-level constructs work :wink So here's a quick explanation for 'if' - you can probably work the others out for yourself.

.IF (eax == 7)    ;cmp eax,7  ;jne @else
    mov ecx,1     ;mov ecx,1
                  ;jmp @endif
.ELSE             ;@else:
    mov ecx,0     ;mov ecx,0
.ENDIF            ;@endif:


A while-loop (repeat until) would be similar, but with a jump back to the start to make it loop.



And just for fun, here's your code without these constructs (and ExitProcess, etc)

include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\masm32.inc
include \MASM32\INCLUDE\kernel32.inc

includelib \MASM32\LIB\masm32.lib
includelib \MASM32\LIB\user32.lib
includelib \MASM32\LIB\kernel32.lib

;************************************************************************************************

TCP_portsCount equ 0ffffh

SCANINT STRUCT
   a_      DWORD ?
   b_      DWORD ?
   c_      DWORD ?
   d_      DWORD ?
   e_      DWORD ?
SCANINT ENDS

;************************************************************************************************

.data
format1 BYTE '%d.%d.%d.%d',0

;************************************************************************************************

.data?
scint   SCANINT <?>
char    BYTE 32 DUP (?)

;************************************************************************************************

.code

scanner:
    xor eax,eax         ;eax = 0
    mov [scint.a_],1    ;"0.?.?.?" are invalid, so we start at a=1
    mov [scint.b_],eax
    mov [scint.c_],eax
    mov [scint.d_],eax
    mov [scint.e_],eax

    @loop_a:               ;.REPEAT

;;ip addresses "127.?.?.?" and "10.?.?.?" are reserved - which is why the original had the 'if' to avoid checking them
      @loop_b:                 ;.REPEAT

          @loop_c:                 ;.REPEAT

              @loop_d:                 ;.REPEAT

                    INVOKE wsprintf,OFFSET char,OFFSET format1,[scint.a_],[scint.b_],[scint.c_],[scint.d_]
                  @loop_port:              ;.REPEAT
                    print chr$(char)
                    INC [scint.e_]
                    cmp [scint.e_],TCP_portsCount
                    jle @loop_port         ;.UNTIL ([scint.e_] > TCP_portsCount)

                INC [scint.d_]
                cmp [scint.d_],255
                jle @loop_d            ;.UNTIL ([scint.d_] > 255)

            INC [scint.c_]
            cmp [scint.c_],255
            jle @loop_c            ;.UNTIL ([scint.c_] > 255)

        INC [scint.b_]
        cmp [scint.b_],255
        jle @loop_d            ;.UNTIL ([scint.b_] > 255)

    INC [scint.a_]
    cmp [scint.a_],254     ;;"255.?.?.?" are invalid
    jle @loop_a            ;.UNTIL ([scint.a_] > 254)

    invoke ExitProcess, NULL
END

No snowflake in an avalanche feels responsible.

marla

thanks again, i have learned enough here to keep busy for a long time, time to get nitty gritty. i wish that my school offered a win32 asm class :/

zooba

Quote from: Tedd on February 17, 2006, 12:51:31 PM
Since your code isn't within a function, you could place the scanint variables in the .data? section (just keeps things a little tidier - they still actually go on the stack, but it's cleaned up when you exitprocess.)

They don't go on the stack, they have a page allocated by the loader. Check a disassembly, the addresses are hard-coded (at least the offset from the page is) - there's no registers involved as for the stack.

I also don't understand the point in using a structure instead of MASM's local variables:

scanner PROC
    LOCAL a:DWORD, b:DWORD, c:DWORD, ...
   
    ...
   
scanner ENDP


It's a tool which is just as valid and just as high-level as using a structure. :U

Ratch

zooba,

Quote
They don't go on the stack, they have a page allocated by the loader.

     Right you are.  The .DATA? method suggested by Tedd makes them, in effect, global variables.

Quote
     I also don't understand the point in using a structure instead of MASM's local variables:

     I am responsible for suggesting to marla that he use that method.  That's because I am a PROCless programmer.  I consider PROCs a "red tape" directive that is more trouble than it is worth.  I would be happy to debate the pros and cons of my method.  For background, you might want to review the link below.  Ratch

http://www.masmforum.com/simple/index.php?topic=3783.0

zooba

Quote from: Ratch on February 18, 2006, 02:19:13 AM
I am responsible for suggesting to marla that he use that method.  That's because I am a PROCless programmer.  I consider PROCs a "red tape" directive that is more trouble than it is worth.  I would be happy to debate the pros and cons of my method.  For background, you might want to review the link below.  Ratch

http://www.masmforum.com/simple/index.php?topic=3783.0


I've read the page you link to. I am happy to debate with you, but in the end it comes down to personal preference anyway :wink . Using a structure as a stack-frame substitute is an advanced process which (IMHO) is not ideal for teaching beginners. :U

Ratch

zooba,

Quote
Using a structure as a stack-frame substitute is an advanced process which (IMHO) is not ideal for teaching beginners.

     You could be right.  It's hard at times for a experienced user to put oneself into the beginners shoes.   I think it should be up to the  beginner to decide which method he wants to learn and use.  My opinion is that PROC's are another layer of software and complexity that gets between the problem and the programmer.  And sometimes I observe a lot of angst and gnashing of teeth with regard to making PROC's do what the programmer wants.   Does anyone else have a opinion?  Ratch

Ian_B

#13
I think more than any other language ASM allows programmers to indulge their individual style, whether for good or bad. That's why in a couple of threads recently it seems like there are "too many cooks" trying to show the best way to do something really very simple, making their own assumptions about what the poor newbie really wants or needs...  :bdg   It's no wonder ASM seems confusing!

{EDIT}

As long as code is well-commented, verbosely even, style shouldn't really be that much of an issue to newbies. The important thing is surely explaining the assumptions and the methods clearly.

IanB

Ratch

Ian_B,
     Thank you for your musings.

Quote
... but the writer was completely convinced that the best way to code procedures (or at least ones as small as these) was to pass all the parameters in registers rather than all the syscall push/pop convention and messing with stack frames, etc.

     Putting all the function parameters into registers is a good way to go if one has the registers to do it, and you don't need to use the parameter registers within the function for other purposes other than reading the values they contain.  If one has to save and restore the registers, then the parameters might as well be sent to the function via stack/memory in the first place.  Unfortunately INTEL type CPU's suffer register 'starvation' , unlike the MOTOROLA 68XXX series.  I use registers to pass parameters whenever I can, but sooner or later one runs out of registers to use.

Quote
I never use stack parameters or frames unless it's absolutely necessary, and because I often have fall-through procs with different start conditions, I use the dangerous "option no-scoped" parameter

     Since I don't use PROC's, my entire program is running as "no-scoped".  No problem, however, because I use global variables where needed, and local variables which are manipulated on the stack.  I too like to fall through on  code that can also be INVOKE'd.

Quote
Even at my stage in the game, I don't think I'd want to play with your proc coding suggestion, Ratch. I'm kinda happy with what I do, and it would add another layer of complexity I don't really need or want when I'm finding it hard enough going to just code in ASM

     I hear you and understand.  My method requires one to be aware of the stack at all times.  In other words, one has to be a stackmaster.  But is it is very flexible.  Sometimes one can PUSH parameters a long way before an INVOKE is encountered, when the parameter first becomes available, so the parameter does not have to be saved somewhere until it is needed.

Quote
Same with all the macro stuff that you guys use all the time

     MACRO's can be useful and misused.  Some MACRO's I've seen are complicated 'works of art' rather than something a programmer would like to use constantly.  Still others do trivial things that might as well be coded directly.  Ratch