Course / 13 Reversing / Lesson 05 of 05

A beginner license check

Estimated time: 18 min reading · 18 min activity

You can learn to follow input into a decision using a small check whose source you own. The supplied desk_code function accepts a string when its counted length equals 4. It is a teaching example, sometimes called a crackme: a deliberately inspectable check for practice. It is not a commercial license or a grant to examine other software.

This function calls length_of, then chooses one of two return values. Trace the source and label the result a prediction. Confirming it on a specific binary would require a matched build and recorded run, which are absent. The functions in owned-check.c have no main and are not a standalone executable.

Before you begin

Estimated time
18 min reading, 18 min activity. Setup and optional practice may take longer.
Where you work
Read the supplied C/Python source and clearly labeled conceptual models in any current desktop or mobile browser. No compiler, binary execution, debugger, tracer or Ghidra installation is required. Function-only C sources are not standalone programs.
Without a lab
Trace the source or model on paper and record the condition, chosen branch and evidence limit. No matched lesson ELF, disassembly, debugger session or Ghidra export is supplied. Historical observations retain their separate context. Reading works without JavaScript. Checking requires JavaScript and a secure context; saving also requires usable storage.

After this lesson you can:

  • Locate the comparison in desk_code and state the length it requires.
  • Trace the input notes through the source to a return value.
  • Write the stage evidence note with an explicit missing-run limit.

01Example

Source trace of desk_code for the input desk. Predicted from owned-check.c. Not observed. The activity uses a different input.

desk_code returns 1 when length_of(text) equals 4.
Otherwise it returns 0.

Input: desk
Characters: d e s k, then the end marker.
Count: 4
4 equals 4, so the source returns 1.
Label: source trace. No binary was run.

02The comparison

const char *text declares a pointer used here to read characters. A pointer identifies a memory location; const indicates the characters are not modified through this declaration. text[count] selects a character at an offset, starting at 0. The while loop repeats until it reaches '\0', the zero byte ending a C string. The count excludes that terminator. For the supplied ASCII inputs, each character occupies one byte. This function counts bytes, not Unicode characters in general.

The helper returns -1 for a null pointer, the special pointer value 0 meaning no valid location. For the supplied ordinary text strings, it counts characters before the terminator. This small function assumes a valid terminated string within readable memory; it is not a general safe input-validation routine. A null pointer is different from a string terminator: one means no text location, while the other marks the end inside a valid text sequence.

desk_code compares the count with 4 using ==, returns 1 on equality and 0 otherwise. These are chosen function results, not observed process exits. A strings list cannot prove that branch was taken, and a conceptual branch or decompiler sketch cannot replace a matched binary trace.

The hand-drawn listing uses x86-style notation as a teaching model. eax names the result register in this model. call invokes the named function; cmp compares values; jne means jump if not equal. mov places a value in the named register, ret returns, and fail: labels a location the jump can select. These names explain the drawing; they do not claim a compiler emitted it.

Count, compare and predict the selected pathModel. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

Source prediction; no binary was run. Hand-drawn listing, not disassembly.

Supplied ASCII source model

Select notes in this scene for the “Trace a different input” activity.

dbyte 0ebyte 1sbyte 2kbyte 3\0not counted
Counted ASCII bytes0
owned-check.c · exact source excerpt
Original lineSource
9 count = 0;
10 while (text[count] != '\0') {
11 count = count + 1;
13 return count;
17 if (length_of(text) == 4) {
18 return 1;
20 return 0;

Hand-drawn instruction model

desk_code:
  call length_of      ; result in eax
  cmp  eax, 4
  jne  fail           ; jump if not equal
  mov  eax, 1
  ret
fail:
  mov  eax, 0
  ret

Current listing lines: 2. Skipped: none established.

Conceptual eax
unknown
Comparison
not compared
Equal?
unknown
Function return
not returned

jne decision: not established

Equal → line 5 assigns 1 → line 6 returns 1Not equal → fail → line 8 assigns 0 → line 9 returns 0

Select a named token for its definition.

The terminator ends a valid string and is excluded from count. A null pointer means no valid text location; it is a different source case, not a selectable memory simulation.

Begin desk: count is zero; no result or branch is established.

Follow the explanation

  1. Begin desk: count is zero; no result or branch is established.
  2. Count byte 1, d; the terminator is not counted.
  3. Count byte 2, e; the terminator is not counted.
  4. Count byte 3, s; the terminator is not counted.
  5. Count byte 4, k; the terminator is not counted.
  6. length_of counts d, e, s, k and returns 4 in conceptual eax.
  7. cmp compares 4 with 4 and records equality.
  8. jne is not taken because the values are equal; continue to the success assignment.
  9. The success mov sets conceptual eax to 1.
  10. desk_code returns 1; the return bypasses the failure block. This is a source prediction.

Both complete source-prediction paths

owned-check.c · exact source excerpt
Original lineSource
9 count = 0;
10 while (text[count] != '\0') {
11 count = count + 1;
13 return count;
17 if (length_of(text) == 4) {
18 return 1;
20 return 0;
desk_code:
  call length_of      ; result in eax
  cmp  eax, 4
  jne  fail           ; jump if not equal
  mov  eax, 1
  ret
fail:
  mov  eax, 0
  ret
SampleCount and comparisonjne pathAssignment and return
deskd, e, s, k; count 4; 4 equals 4Not taken; lines 5–6 execute; return bypasses failure blockLine 5: eax=1; line 6: function return=1
notesn, o, t, e, s; count 5; 5 differs from 4Taken to fail; lines 5–6 skippedLine 8: eax=0; line 9: function return=0

The while condition tests text[count] against '\0'; each counted ASCII byte increments count once. The terminator is never counted. A null pointer means no valid text location and makes the helper return −1; the supplied two strings are ordinary terminated text.

Source prediction; no binary was run. Function return values 0/1 are not observed process exit codes. A matched executable, disassembly, decompiler listing and syscall trace are missing.

call
call invokes the named function in this conceptual listing.
cmp
cmp compares values; it does not itself choose a jump.
jne
jne means jump if not equal.
mov
mov places a value in the named register.
ret
ret returns from the function.
eax
eax is the conceptual result register in this hand-drawn model. No register was captured.
fail
fail: labels the location the jump can select.
Source prediction; no binary was run. Select notes here for the Trace a different input activity. Only supplied desk and notes are modeled.
/* Desk code check. Original teaching program. Not a product license. Not compiled. */

int length_of(const char *text) {
    int count;

    if (text == 0) {
        return -1;
    }
    count = 0;
    while (text[count] != '\0') {
        count = count + 1;
    }
    return count;
}

int desk_code(const char *text) {
    if (length_of(text) == 4) {
        return 1;
    }
    return 0;
}

Download the owned desk-code source

03Trace a different input

Use the fresh activity text notes and trace its five characters until the terminator. Keep the count, equality result and function return separate. The later understanding question supplies a different string.

Source trace of owned-check.c. Not a program run.

Rule visible in desk_code:
length_of(text) is compared with 4.
Equal: return 1.
Otherwise: return 0.
A text pointer that is 0 makes length_of return -1, which is not 4, so desk_code returns 0.
The end of a text value in this source is the '\0' marker. The count stops before that marker.

Worked input "desk":
Characters d, e, s, k, then the end marker.
Count is 4.
4 equals 4, so the source returns 1.
Predicted from the source. Not observed.

Activity input "notes":
Characters n, o, t, e, s, then the end marker.
Count is 5.
5 equals 4 is false, so the source returns 0.
Predicted from the source. Not observed.

A matched run would be confirmation. No binary was built, so nothing was confirmed by execution.

Download the source trace card

  1. Count the characters in notes before the end marker. Check: the count is 5.
  2. Compare 5 with 4 using the condition in desk_code.
  3. Check: the source trace returns 0.
  4. Stop. Do not compile the check and do not feed it an input.

04Stage evidence note

This is the module note and the stage note. It is ungraded. Use one line for each blank on the card. The limit line has to say that an executable, a disassembly, a decompiler listing, and a syscall trace are still missing. A source prediction for the desk code is not a confirmed run.

Recommended reading order for the note: Compiled and interpreted, then Enough C and assembly, then file, strings, and strace, then the Ghidra lesson, then this check. The catalog list is unchanged.

Stage evidence note. Ungraded. Fill the blanks from the lessons. No binary was run.

1. hello.c role: ____
   Planned hello ELF role: ____
   Was the ELF file produced? ____

2. The compare/jump card is a: conceptual model / captured disassembly. Circle one.
   What is still missing? ____

3. A strings list can show a stored sequence. It cannot by itself show: ____

4. The decompiler sketch compared with compare.c differs in: ____
   Is the sketch the original source file? ____

5. desk_code returns 1 when length_of(text) equals: ____
   Source prediction is not the same thing as a run. Was a binary run? ____

Rubric: one filled line for each item, plus a limit.
A limit names what the page still does not have: an owned executable, a disassembly, a decompiler listing, or a syscall trace.
Stop. Do not compile or run anything to fill a blank.

Download the stage evidence note

  1. Fill blank 1 from the hello card: hello.c is source, and the ELF row is an unproduced compiler-output role.
  2. Fill blank 2 with the words conceptual model. Name the missing disassembly.
  3. Fill blank 3 with what a strings list cannot show: which branch ran.
  4. Fill blank 4 with a name that differs between compare.c and the sketch, and say the sketch is not the source file.
  5. Fill blank 5 with 4, and write that no binary was run. Stop without compiling.

05Optional challenge

From the source, predict the return of desk_code for the two-character input ab. Write the count, the comparison, and the sentence that a run is still missing. About ten minutes.

06Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

Length case: optional changed inputOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

Fresh source rule: desk_code returns 1 exactly when length_of(text) equals 4; otherwise 0.
Input for this case: cord
All four letters count; there is no space or end-marker character in this input.
This is a source prediction only, not a run.

07Explain your evidence

Optional written self-check: explain one decision using the supplied case. No tool run is required.

Check your explanation: count spaces when present and exclude the end marker; connect the length comparison to its branch; distinguish source/model predictions from disassembly and an observed run.

08Common mistakes

If notes is traced as a success, the count was taken from desk. notes has five characters, and the comparison asks for 4.

If the evidence note says a disassembly was captured, the model label was changed. The branch card is conceptual, and the file is still missing.

If the check is treated as a commercial license to defeat, the opening was skipped. The program is an owned length check. Stop if the file in front of you is not owned-check.c.

References

Next is the first lesson of Pwn.