Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to manually choose unpacker #17

Open
Masrepus opened this issue May 6, 2019 · 15 comments
Open

Add ability to manually choose unpacker #17

Masrepus opened this issue May 6, 2019 · 15 comments

Comments

@Masrepus
Copy link
Member

Masrepus commented May 6, 2019

Use cases:

  • no packer recognized -> manually select one
  • wrong packer recognized -> override selection
@Calvonator
Copy link

Is anyone still interested in this feature?

@Masrepus
Copy link
Member Author

Yes, we are still interested in this, but unfortunately we are somewhat short on time due to university tasks, so we are currently not as actively developing new features as we would like to. If you want to propose a PR by any chance, you are of course welcome to do so and we'll take the time to review and merge it

@Calvonator
Copy link

Cool sounds good, I'll see what I can do for you 👍

@Masrepus
Copy link
Member Author

Cool, thanks!

@Calvonator
Copy link

@Masrepus Hi just wanted some quick clarification on the following use case.

wrong packer recognized -> override selection

Does this use case mean to give the user the option to manually override the selected unpacker in the case that the indentifypacker() function selects the wrong unpacker?

@Masrepus
Copy link
Member Author

Masrepus commented Feb 5, 2021

Yes I think that makes sense like that 👍🏼

@Calvonator
Copy link

Calvonator commented Feb 17, 2021

@Masrepus Hi just one more clarification for the following use case.
wrong packer recognized -> override selection

Should the user be asked whether they would like to manually override the unpacker each time they begin to unpack a sample or only when the sample is first given to unipacker?

@Masrepus
Copy link
Member Author

Hm I think asking every time would be fine. Otherwise we would need to identify the last used unpacker for a sample from history data that might be corrupted, so that needs additional error handling etc. And I guess that might not be such a predominant use case to justify the extra work. But if you think that it might indeed be nice to include it, of course feel free to do so.

@Masrepus
Copy link
Member Author

And with asking the user, do you mean that every time a sample is opened, the user needs to explicitly accept/override the unpacker? I think it would be preferable to just use the recognized unpacker and if the user wants to change it, they can do so manually

@Calvonator
Copy link

Yeah I agree that it would be preferable that it not be asked every time. Unsure on best way to go about this though, do you think making it an option ID such as M so that when M is entered they can change the unpacker of an already listed sample.
Excuse the crudely drawn example below:
https://gyazo.com/b82d8a571a979f4e093dc082ff69d673

@Masrepus
Copy link
Member Author

Actually we currently use the name of the last unpacker only for information purposes. The unpacker is identified anew every time a sample is loaded. I would create a new shell command for this, e.g. unpacker <name>. You can create such a shell command by adding a method do_unpacker(self, args) to shell.py, or analogously for a different name. The docstring for such a method is then automatically used as a help text when the user calls help <command_name>

@Calvonator
Copy link

Haven't worked with making shell commands before so sorry for the silly question. Is the command name just the same as the function name or is that defined elsewhere?
Also I'm a bit confused about when this command is used. Will this command be used after the initial option ID is entered and a sample is chosen?

@Masrepus
Copy link
Member Author

Ah I see, sorry I should have clarified that! Currently, the unipacker shell is defined in shell.py. One example would be the log command, which is defined here:

def do_log(self, args):
"""Set logging level
Usage: log [OPTIONS]
Options:
i Log every instruction that is executed
r Log memory READ access
w Log memory WRITE access
s Log system API calls
a Log everything"""
if args == "a":
args = "irsw"
print("Log level:")
self.engine.log_mem_read = any(x in args for x in ["r", "read"])
print(f"[{'x' if self.engine.log_mem_read else ' '}] mem read")
self.engine.log_mem_write = any(x in args for x in ["w", "write"])
print(f"[{'x' if self.engine.log_mem_write else ' '}] mem write")
self.engine.log_instr = any(x in args for x in ["i", "instr"])
print(f"[{'x' if self.engine.log_instr else ' '}] instructions")
self.engine.log_apicalls = any(x in args for x in ["s", "sys"])
print(f"[{'x' if self.engine.log_apicalls else ' '}] API calls")

As you can see, the method name is do_log, as the shell framework that we use automatically registers methods that start with do_ as commands, using the portion after the underscore as the command name.

Each such function needs to have the arguments self and args, where - as can be seen in the log example - the shell framework passes you the whole string that the user wrote after the command name. As an example, if the user executes log instr, the framework calls do_log and sets the parameter args to "instr". Inside this method, you can then inspect the argument, and perform any operation that you like.

After you are done with whatever the command should do, you just return from the method as usual, and the shell framework makes sure that the user can enter the next command.

In your case, let's suppose you want to provide a command called unpacker that takes one argument which specifies the name of the unpacker to use. You would then need to create a do_unpacker method inside the Shell class of shell.py. This method checks the value of args whether it is the name of a known unpacker. If it isn't, just print some error message and return. Otherwise, you can then modify the unpacker of self.sample to be the one the user requested.

Regarding the usage of the command, the workflow we imagine should be somewhat like this:

  1. The user starts unipacker and selects a sample to be loaded
  2. Once loading is done, the user is now inside the unipacker shell, which is waiting for him to perform commands (e.g. start emulation, configure logging etc)
  3. At this point, the user will probably check which unpacker has been detected, and maybe decide that a different one should be used
  4. They invoke e.g. unpacker upx, which switches the current unpacker to the UPX implementation
  5. If they decide that they actually didn't want to use UPX, they can switch again
  6. At some point, they start emulation by invoking the r command. After the start of the emulation, the chosen unpacker is no longer changeable, as this might lead to problems when it is changed mid-emulation. The do_unpacker method can simply check the self.started field, and if it is true, the command just prints a warning message that changing the unpacker is not supported once emulation is started, and simply exit

@Calvonator
Copy link

Awesome thank you so much for this reply, I really appreciate the effort you have put in to help me with this. This is my first open source contribution and I'm glad I've had you to guide me through.

I think I should be able to complete this now with the info you've given, hopefully haha.

@Masrepus
Copy link
Member Author

No problem, if you have any further questions don't hesitate to ask them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants